ia64/xen-unstable

view tools/python/xen/xm/create.py @ 12810:cf1ca0615414

Added support for configuration file for xm, and use that to specify
contact and authentication details for the Xen-API server.

The default behaviour is still to use the existing legacy XML-RPC server.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Wed Dec 06 11:02:32 2006 +0000 (2006-12-06)
parents 000609d8c93f
children 531c67ed64f4
line source
1 #============================================================================
2 # This library is free software; you can redistribute it and/or
3 # modify it under the terms of version 2.1 of the GNU Lesser General Public
4 # License as published by the Free Software Foundation.
5 #
6 # This library is distributed in the hope that it will be useful,
7 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9 # Lesser General Public License for more details.
10 #
11 # You should have received a copy of the GNU Lesser General Public
12 # License along with this library; if not, write to the Free Software
13 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 #============================================================================
15 # Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
16 # Copyright (C) 2005 Nguyen Anh Quynh <aquynh@gmail.com>
17 # Copyright (C) 2005-2006 XenSource Ltd
18 #============================================================================
20 """Domain creation.
21 """
22 import os
23 import os.path
24 import sys
25 import socket
26 import re
27 import xmlrpclib
29 from xen.xend import sxp
30 from xen.xend import PrettyPrint
31 import xen.xend.XendClient
32 from xen.xend.XendBootloader import bootloader
33 from xen.util import blkif
34 from xen.util import security
36 from xen.xm.opts import *
38 from main import server
39 import console
42 gopts = Opts(use="""[options] [vars]
44 Create a domain.
46 Domain creation parameters can be set by command-line switches, from
47 a python configuration script or an SXP config file. See documentation
48 for --defconfig, --config. Configuration variables can be set using
49 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
51 """)
53 gopts.opt('help', short='h',
54 fn=set_true, default=0,
55 use="Print this help.")
57 gopts.opt('help_config',
58 fn=set_true, default=0,
59 use="Print the available configuration variables (vars) for the "
60 "configuration script.")
62 gopts.opt('quiet', short='q',
63 fn=set_true, default=0,
64 use="Quiet.")
66 gopts.opt('path', val='PATH',
67 fn=set_value, default='.:/etc/xen',
68 use="Search path for configuration scripts. "
69 "The value of PATH is a colon-separated directory list.")
71 gopts.opt('defconfig', short='f', val='FILE',
72 fn=set_value, default='xmdefconfig',
73 use="Use the given Python configuration script."
74 "The configuration script is loaded after arguments have been "
75 "processed. Each command-line option sets a configuration "
76 "variable named after its long option name, and these "
77 "variables are placed in the environment of the script before "
78 "it is loaded. Variables for options that may be repeated have "
79 "list values. Other variables can be set using VAR=VAL on the "
80 "command line. "
81 "After the script is loaded, option values that were not set "
82 "on the command line are replaced by the values set in the script.")
84 gopts.default('defconfig')
86 gopts.opt('config', short='F', val='FILE',
87 fn=set_value, default=None,
88 use="Domain configuration to use (SXP).\n"
89 "SXP is the underlying configuration format used by Xen.\n"
90 "SXP configurations can be hand-written or generated from Python "
91 "configuration scripts, using the -n (dryrun) option to print "
92 "the configuration.")
94 gopts.opt('dryrun', short='n',
95 fn=set_true, default=0,
96 use="Dry run - prints the resulting configuration in SXP but "
97 "does not create the domain.")
99 gopts.opt('paused', short='p',
100 fn=set_true, default=0,
101 use='Leave the domain paused after it is created.')
103 gopts.opt('console_autoconnect', short='c',
104 fn=set_true, default=0,
105 use="Connect to the console after the domain is created.")
107 gopts.var('vncpasswd', val='NAME',
108 fn=set_value, default=None,
109 use="Password for VNC console on HVM domain.")
111 gopts.var('vncviewer', val='no|yes',
112 fn=set_bool, default=None,
113 use="Spawn a vncviewer listening for a vnc server in the domain.\n"
114 "The address of the vncviewer is passed to the domain on the "
115 "kernel command line using 'VNC_SERVER=<host>:<port>'. The port "
116 "used by vnc is 5500 + DISPLAY. A display value with a free port "
117 "is chosen if possible.\nOnly valid when vnc=1.")
119 gopts.var('vncconsole', val='no|yes',
120 fn=set_bool, default=None,
121 use="Spawn a vncviewer process for the domain's graphical console.\n"
122 "Only valid when vnc=1.")
124 gopts.var('name', val='NAME',
125 fn=set_value, default=None,
126 use="Domain name. Must be unique.")
128 gopts.var('bootloader', val='FILE',
129 fn=set_value, default=None,
130 use="Path to bootloader.")
132 gopts.var('bootargs', val='NAME',
133 fn=set_value, default=None,
134 use="Arguments to pass to boot loader")
136 gopts.var('bootentry', val='NAME',
137 fn=set_value, default=None,
138 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
140 gopts.var('kernel', val='FILE',
141 fn=set_value, default=None,
142 use="Path to kernel image.")
144 gopts.var('ramdisk', val='FILE',
145 fn=set_value, default='',
146 use="Path to ramdisk.")
148 gopts.var('features', val='FEATURES',
149 fn=set_value, default='',
150 use="Features to enable in guest kernel")
152 gopts.var('builder', val='FUNCTION',
153 fn=set_value, default='linux',
154 use="Function to use to build the domain.")
156 gopts.var('memory', val='MEMORY',
157 fn=set_int, default=128,
158 use="Domain memory in MB.")
160 gopts.var('maxmem', val='MEMORY',
161 fn=set_int, default=None,
162 use="Maximum domain memory in MB.")
164 gopts.var('shadow_memory', val='MEMORY',
165 fn=set_int, default=0,
166 use="Domain shadow memory in MB.")
168 gopts.var('cpu', val='CPU',
169 fn=set_int, default=None,
170 use="CPU to run the VCPU0 on.")
172 gopts.var('cpus', val='CPUS',
173 fn=set_value, default=None,
174 use="CPUS to run the domain on.")
176 gopts.var('pae', val='PAE',
177 fn=set_int, default=1,
178 use="Disable or enable PAE of HVM domain.")
180 gopts.var('acpi', val='ACPI',
181 fn=set_int, default=1,
182 use="Disable or enable ACPI of HVM domain.")
184 gopts.var('apic', val='APIC',
185 fn=set_int, default=1,
186 use="Disable or enable APIC mode.")
188 gopts.var('vcpus', val='VCPUS',
189 fn=set_int, default=1,
190 use="# of Virtual CPUS in domain.")
192 gopts.var('cpu_cap', val='CAP',
193 fn=set_int, default=None,
194 use="""Set the maximum amount of cpu.
195 CAP is a percentage that fixes the maximum amount of cpu.""")
197 gopts.var('cpu_weight', val='WEIGHT',
198 fn=set_int, default=None,
199 use="""Set the cpu time ratio to be allocated to the domain.""")
201 gopts.var('restart', val='onreboot|always|never',
202 fn=set_value, default=None,
203 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
204 instead.
206 Whether the domain should be restarted on exit.
207 - onreboot: restart on exit with shutdown code reboot
208 - always: always restart on exit, ignore exit code
209 - never: never restart on exit, ignore exit code""")
211 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
212 fn=set_value, default=None,
213 use="""Behaviour when a domain exits with reason 'poweroff'.
214 - destroy: the domain is cleaned up as normal;
215 - restart: a new domain is started in place of the old one;
216 - preserve: no clean-up is done until the domain is manually
217 destroyed (using xm destroy, for example);
218 - rename-restart: the old domain is not cleaned up, but is
219 renamed and a new domain started in its place.
220 """)
222 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
223 fn=set_value, default=None,
224 use="""Behaviour when a domain exits with reason 'reboot'.
225 - destroy: the domain is cleaned up as normal;
226 - restart: a new domain is started in place of the old one;
227 - preserve: no clean-up is done until the domain is manually
228 destroyed (using xm destroy, for example);
229 - rename-restart: the old domain is not cleaned up, but is
230 renamed and a new domain started in its place.
231 """)
233 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
234 fn=set_value, default=None,
235 use="""Behaviour when a domain exits with reason 'crash'.
236 - destroy: the domain is cleaned up as normal;
237 - restart: a new domain is started in place of the old one;
238 - preserve: no clean-up is done until the domain is manually
239 destroyed (using xm destroy, for example);
240 - rename-restart: the old domain is not cleaned up, but is
241 renamed and a new domain started in its place.
242 """)
244 gopts.var('blkif', val='no|yes',
245 fn=set_bool, default=0,
246 use="Make the domain a block device backend.")
248 gopts.var('netif', val='no|yes',
249 fn=set_bool, default=0,
250 use="Make the domain a network interface backend.")
252 gopts.var('tpmif', val='no|yes',
253 fn=append_value, default=0,
254 use="Make the domain a TPM interface backend.")
256 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
257 fn=append_value, default=[],
258 use="""Add a disk device to a domain. The physical device is DEV,
259 which is exported to the domain as VDEV. The disk is read-only if MODE
260 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
261 backend driver domain to use for the disk.
262 The option may be repeated to add more than one disk.""")
264 gopts.var('pci', val='BUS:DEV.FUNC',
265 fn=append_value, default=[],
266 use="""Add a PCI device to a domain, using given params (in hex).
267 For example 'pci=c0:02.1a'.
268 The option may be repeated to add more than one pci device.""")
270 gopts.var('ioports', val='FROM[-TO]',
271 fn=append_value, default=[],
272 use="""Add a legacy I/O range to a domain, using given params (in hex).
273 For example 'ioports=02f8-02ff'.
274 The option may be repeated to add more than one i/o range.""")
276 gopts.var('irq', val='IRQ',
277 fn=append_value, default=[],
278 use="""Add an IRQ (interrupt line) to a domain.
279 For example 'irq=7'.
280 This option may be repeated to add more than one IRQ.""")
282 gopts.var('usbport', val='PATH',
283 fn=append_value, default=[],
284 use="""Add a physical USB port to a domain, as specified by the path
285 to that port. This option may be repeated to add more than one port.""")
287 gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY",
288 fn=append_value, default=[],
289 use="""Make the domain a framebuffer backend.
290 The backend type should be either sdl or vnc.
291 For type=vnc, connect an external vncviewer. The server will listen
292 on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
293 domain id. If vncunused=1, the server will try to find an arbitrary
294 unused port above 5900.
295 For type=sdl, a viewer will be started automatically using the
296 given DISPLAY and XAUTHORITY, which default to the current user's
297 ones.""")
299 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
300 fn=append_value, default=[],
301 use="""Add a network interface with the given MAC address and bridge.
302 The vif is configured by calling the given configuration script.
303 If type is not specified, default is netfront not ioemu device.
304 If mac is not specified a random MAC address is used.
305 If not specified then the network backend chooses it's own MAC address.
306 If bridge is not specified the first bridge found is used.
307 If script is not specified the default script is used.
308 If backend is not specified the default backend driver domain is used.
309 If vifname is not specified the backend virtual interface will have name vifD.N
310 where D is the domain id and N is the interface id.
311 This option may be repeated to add more than one vif.
312 Specifying vifs will increase the number of interfaces as needed.""")
314 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
315 fn=append_value, default=[],
316 use="""Add a TPM interface. On the backend side use the given
317 instance as virtual TPM instance. The given number is merely the
318 preferred instance number. The hotplug script will determine
319 which instance number will actually be assigned to the domain.
320 The associtation between virtual machine and the TPM instance
321 number can be found in /etc/xen/vtpm.db. Use the backend in the
322 given domain.
323 The type parameter can be used to select a specific driver type
324 that the VM can use. To prevent a fully virtualized domain (HVM)
325 from being able to access an emulated device model, you may specify
326 'paravirtualized' here.""")
328 gopts.var('access_control', val="policy=POLICY,label=LABEL",
329 fn=append_value, default=[],
330 use="""Add a security label and the security policy reference that defines it.
331 The local ssid reference is calculated when starting/resuming the domain. At
332 this time, the policy is checked against the active policy as well. This way,
333 migrating through save/restore is covered and local labels are automatically
334 created correctly on the system where a domain is started / resumed.""")
336 gopts.var('nics', val="NUM",
337 fn=set_int, default=-1,
338 use="""DEPRECATED. Use empty vif entries instead.
340 Set the number of network interfaces.
341 Use the vif option to define interface parameters, otherwise
342 defaults are used. Specifying vifs will increase the
343 number of interfaces as needed.""")
345 gopts.var('root', val='DEVICE',
346 fn=set_value, default='',
347 use="""Set the root= parameter on the kernel command line.
348 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
350 gopts.var('extra', val="ARGS",
351 fn=set_value, default='',
352 use="Set extra arguments to append to the kernel command line.")
354 gopts.var('ip', val='IPADDR',
355 fn=set_value, default='',
356 use="Set the kernel IP interface address.")
358 gopts.var('gateway', val="IPADDR",
359 fn=set_value, default='',
360 use="Set the kernel IP gateway.")
362 gopts.var('netmask', val="MASK",
363 fn=set_value, default = '',
364 use="Set the kernel IP netmask.")
366 gopts.var('hostname', val="NAME",
367 fn=set_value, default='',
368 use="Set the kernel IP hostname.")
370 gopts.var('interface', val="INTF",
371 fn=set_value, default="eth0",
372 use="Set the kernel IP interface name.")
374 gopts.var('dhcp', val="off|dhcp",
375 fn=set_value, default='off',
376 use="Set the kernel dhcp option.")
378 gopts.var('nfs_server', val="IPADDR",
379 fn=set_value, default=None,
380 use="Set the address of the NFS server for NFS root.")
382 gopts.var('nfs_root', val="PATH",
383 fn=set_value, default=None,
384 use="Set the path of the root NFS directory.")
386 gopts.var('device_model', val='FILE',
387 fn=set_value, default='',
388 use="Path to device model program.")
390 gopts.var('fda', val='FILE',
391 fn=set_value, default='',
392 use="Path to fda")
394 gopts.var('fdb', val='FILE',
395 fn=set_value, default='',
396 use="Path to fdb")
398 gopts.var('serial', val='FILE',
399 fn=set_value, default='',
400 use="Path to serial or pty or vc")
402 gopts.var('localtime', val='no|yes',
403 fn=set_bool, default=0,
404 use="Is RTC set to localtime?")
406 gopts.var('keymap', val='FILE',
407 fn=set_value, default='',
408 use="Set keyboard layout used")
410 gopts.var('usb', val='no|yes',
411 fn=set_bool, default=0,
412 use="Emulate USB devices?")
414 gopts.var('usbdevice', val='NAME',
415 fn=set_value, default='',
416 use="Name of USB device to add?")
418 gopts.var('stdvga', val='no|yes',
419 fn=set_bool, default=0,
420 use="Use std vga or cirrhus logic graphics")
422 gopts.var('isa', val='no|yes',
423 fn=set_bool, default=0,
424 use="Simulate an ISA only system?")
426 gopts.var('boot', val="a|b|c|d",
427 fn=set_value, default='c',
428 use="Default boot device")
430 gopts.var('nographic', val='no|yes',
431 fn=set_bool, default=0,
432 use="Should device models use graphics?")
434 gopts.var('soundhw', val='audiodev',
435 fn=set_value, default='',
436 use="Should device models enable audio device?")
438 gopts.var('vnc', val='',
439 fn=set_value, default=None,
440 use="""Should the device model use VNC?""")
442 gopts.var('vncdisplay', val='',
443 fn=set_value, default=None,
444 use="""VNC display to use""")
446 gopts.var('vnclisten', val='',
447 fn=set_value, default=None,
448 use="""Address for VNC server to listen on.""")
450 gopts.var('vncunused', val='',
451 fn=set_bool, default=1,
452 use="""Try to find an unused port for the VNC server.
453 Only valid when vnc=1.""")
455 gopts.var('sdl', val='',
456 fn=set_value, default=None,
457 use="""Should the device model use SDL?""")
459 gopts.var('display', val='DISPLAY',
460 fn=set_value, default=None,
461 use="X11 display to use")
463 gopts.var('xauthority', val='XAUTHORITY',
464 fn=set_value, default=None,
465 use="X11 Authority to use")
467 gopts.var('uuid', val='',
468 fn=set_value, default=None,
469 use="""xenstore UUID (universally unique identifier) to use. One
470 will be randomly generated if this option is not set, just like MAC
471 addresses for virtual network interfaces. This must be a unique
472 value across the entire cluster.""")
474 gopts.var('on_xend_start', val='ignore|start',
475 fn=set_value, default='ignore',
476 use='Action to perform when xend starts')
478 gopts.var('on_xend_stop', val='continue|shutdown|suspend',
479 fn=set_value, default="ignore",
480 use="""Behaviour when Xend stops:
481 - ignore: Domain continues to run;
482 - shutdown: Domain is shutdown;
483 - suspend: Domain is suspended;
484 """)
486 def err(msg):
487 """Print an error to stderr and exit.
488 """
489 print >>sys.stderr, "Error:", msg
490 sys.exit(1)
493 def warn(msg):
494 """Print a warning to stdout.
495 """
496 print >>sys.stderr, "Warning:", msg
499 def strip(pre, s):
500 """Strip prefix 'pre' if present.
501 """
502 if s.startswith(pre):
503 return s[len(pre):]
504 else:
505 return s
507 def configure_image(vals):
508 """Create the image config.
509 """
510 if not vals.builder:
511 return None
512 config_image = [ vals.builder ]
513 if vals.kernel:
514 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
515 if vals.ramdisk:
516 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
517 if vals.cmdline_ip:
518 cmdline_ip = strip('ip=', vals.cmdline_ip)
519 config_image.append(['ip', cmdline_ip])
520 if vals.root:
521 cmdline_root = strip('root=', vals.root)
522 config_image.append(['root', cmdline_root])
523 if vals.extra:
524 config_image.append(['args', vals.extra])
526 if vals.builder == 'hvm':
527 configure_hvm(config_image, vals)
529 return config_image
531 def configure_disks(config_devs, vals):
532 """Create the config for disks (virtual block devices).
533 """
534 for (uname, dev, mode, backend) in vals.disk:
535 if uname.startswith('tap:'):
536 cls = 'tap'
537 else:
538 cls = 'vbd'
540 config_vbd = [cls,
541 ['uname', uname],
542 ['dev', dev ],
543 ['mode', mode ] ]
544 if backend:
545 config_vbd.append(['backend', backend])
546 config_devs.append(['device', config_vbd])
548 def configure_pci(config_devs, vals):
549 """Create the config for pci devices.
550 """
551 config_pci = []
552 for (domain, bus, slot, func) in vals.pci:
553 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
554 ['slot', slot], ['func', func]])
556 if len(config_pci)>0:
557 config_pci.insert(0, 'pci')
558 config_devs.append(['device', config_pci])
560 def configure_ioports(config_devs, vals):
561 """Create the config for legacy i/o ranges.
562 """
563 for (io_from, io_to) in vals.ioports:
564 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
565 config_devs.append(['device', config_ioports])
567 def configure_irq(config_devs, vals):
568 """Create the config for irqs.
569 """
570 for irq in vals.irq:
571 config_irq = ['irq', ['irq', irq]]
572 config_devs.append(['device', config_irq])
574 def configure_usb(config_devs, vals):
575 for path in vals.usbport:
576 config_usb = ['usbport', ['path', path]]
577 config_devs.append(['device', config_usb])
579 def configure_vfbs(config_devs, vals):
580 for f in vals.vfb:
581 d = comma_sep_kv_to_dict(f)
582 config = ['vfb']
583 if not d.has_key("type"):
584 d['type'] = 'sdl'
585 for (k,v) in d.iteritems():
586 if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
587 'xauthority', 'type' ]:
588 err("configuration option %s unknown to vfbs" % k)
589 config.append([k,v])
590 if not d.has_key("display") and os.environ.has_key("DISPLAY"):
591 config.append(["display", os.environ['DISPLAY']])
592 if not d.has_key("xauthority"):
593 config.append(["xauthority", get_xauthority()])
594 config_devs.append(['device', ['vkbd']])
595 config_devs.append(['device', config])
597 def configure_security(config, vals):
598 """Create the config for ACM security labels.
599 """
600 access_control = vals.access_control
601 num = len(access_control)
602 if num == 1:
603 d = access_control[0]
604 policy = d.get('policy')
605 label = d.get('label')
606 if policy != security.active_policy:
607 err("Security policy (" + policy + ") incompatible with enforced policy ("
608 + security.active_policy + ")." )
609 config_access_control = ['access_control',
610 ['policy', policy],
611 ['label', label] ]
613 #ssidref cannot be specified together with access_control
614 if sxp.child_value(config, 'ssidref'):
615 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
616 #else calculate ssidre from label
617 ssidref = security.label2ssidref(label, policy, 'dom')
618 if not ssidref :
619 err("ERROR calculating ssidref from access_control.")
620 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
621 config.append(security_label)
622 elif num == 0:
623 if hasattr(vals, 'ssidref'):
624 if not security.on():
625 err("ERROR: Security ssidref specified but no policy active.")
626 ssidref = getattr(vals, 'ssidref')
627 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
628 config.append(security_label)
629 elif num > 1:
630 err("VM config error: Multiple access_control definitions!")
633 def configure_vtpm(config_devs, vals):
634 """Create the config for virtual TPM interfaces.
635 """
636 vtpm = vals.vtpm
637 if len(vtpm) > 0:
638 d = vtpm[0]
639 instance = d.get('instance')
640 if instance == "VTPMD":
641 instance = "0"
642 else:
643 if instance != None:
644 try:
645 if int(instance) == 0:
646 err('VM config error: vTPM instance must not be 0.')
647 except ValueError:
648 err('Vm config error: could not parse instance number.')
649 backend = d.get('backend')
650 typ = d.get('type')
651 config_vtpm = ['vtpm']
652 if instance:
653 config_vtpm.append(['pref_instance', instance])
654 if backend:
655 config_vtpm.append(['backend', backend])
656 if typ:
657 config_vtpm.append(['type', type])
658 config_devs.append(['device', config_vtpm])
661 def configure_vifs(config_devs, vals):
662 """Create the config for virtual network interfaces.
663 """
665 vifs = vals.vif
666 vifs_n = len(vifs)
668 if hasattr(vals, 'nics'):
669 if vals.nics > 0:
670 warn("The nics option is deprecated. Please use an empty vif "
671 "entry instead:\n\n vif = [ '' ]\n")
672 for _ in range(vifs_n, vals.nics):
673 vifs.append('')
674 vifs_n = len(vifs)
675 elif vals.nics == 0:
676 warn("The nics option is deprecated. Please remove it.")
678 for c in vifs:
679 d = comma_sep_kv_to_dict(c)
680 config_vif = ['vif']
682 def f(k):
683 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
684 'vifname', 'rate', 'model']:
685 err('Invalid vif option: ' + k)
687 config_vif.append([k, d[k]])
689 map(f, d.keys())
690 config_devs.append(['device', config_vif])
693 def configure_hvm(config_image, vals):
694 """Create the config for HVM devices.
695 """
696 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
697 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
698 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
699 'sdl', 'display', 'xauthority',
700 'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
701 for a in args:
702 if a in vals.__dict__ and vals.__dict__[a] is not None:
703 config_image.append([a, vals.__dict__[a]])
704 config_image.append(['vncpasswd', vals.vncpasswd])
706 def run_bootloader(vals, config_image):
707 if not os.access(vals.bootloader, os.X_OK):
708 err("Bootloader isn't executable")
709 if len(vals.disk) < 1:
710 err("No disks configured and boot loader requested")
711 (uname, dev, mode, backend) = vals.disk[0]
712 file = blkif.blkdev_uname_to_file(uname)
714 if vals.bootentry:
715 warn("The bootentry option is deprecated. Use bootargs and pass "
716 "--entry= directly.")
717 vals.bootargs = "--entry=%s" %(vals.bootentry,)
719 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
720 vals.bootargs, config_image)
722 def make_config(vals):
723 """Create the domain configuration.
724 """
726 config = ['vm']
728 def add_conf(n):
729 if hasattr(vals, n):
730 v = getattr(vals, n)
731 if v:
732 config.append([n, v])
734 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
735 'restart', 'on_poweroff',
736 'on_reboot', 'on_crash', 'vcpus', 'features',
737 'on_xend_start', 'on_xend_stop'])
739 if vals.uuid is not None:
740 config.append(['uuid', vals.uuid])
741 if vals.cpu is not None:
742 config.append(['cpu', vals.cpu])
743 if vals.cpus is not None:
744 config.append(['cpus', vals.cpus])
745 if vals.cpu_cap is not None:
746 config.append(['cpu_cap', vals.cpu_cap])
747 if vals.cpu_weight is not None:
748 config.append(['cpu_weight', vals.cpu_weight])
749 if vals.blkif:
750 config.append(['backend', ['blkif']])
751 if vals.netif:
752 config.append(['backend', ['netif']])
753 if vals.tpmif:
754 config.append(['backend', ['tpmif']])
755 if vals.localtime:
756 config.append(['localtime', vals.localtime])
758 config_image = configure_image(vals)
759 if vals.bootloader:
760 config_image = run_bootloader(vals, config_image)
761 config.append(['bootloader', vals.bootloader])
762 if vals.bootargs:
763 config.append(['bootloader_args', vals.bootargs])
764 config.append(['image', config_image])
766 config_devs = []
767 configure_disks(config_devs, vals)
768 configure_pci(config_devs, vals)
769 configure_ioports(config_devs, vals)
770 configure_irq(config_devs, vals)
771 configure_vifs(config_devs, vals)
772 configure_usb(config_devs, vals)
773 configure_vtpm(config_devs, vals)
774 configure_vfbs(config_devs, vals)
775 configure_security(config, vals)
776 config += config_devs
778 return config
780 def preprocess_disk(vals):
781 if not vals.disk: return
782 disk = []
783 for v in vals.disk:
784 d = v.split(',')
785 n = len(d)
786 if n == 3:
787 d.append(None)
788 elif n == 4:
789 pass
790 else:
791 err('Invalid disk specifier: ' + v)
792 disk.append(d)
793 vals.disk = disk
795 def preprocess_pci(vals):
796 if not vals.pci: return
797 pci = []
798 for pci_dev_str in vals.pci:
799 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
800 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
801 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
802 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
803 if pci_match!=None:
804 pci_dev_info = pci_match.groupdict('0')
805 try:
806 pci.append( ('0x'+pci_dev_info['domain'], \
807 '0x'+pci_dev_info['bus'], \
808 '0x'+pci_dev_info['slot'], \
809 '0x'+pci_dev_info['func']))
810 except IndexError:
811 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
812 vals.pci = pci
814 def preprocess_ioports(vals):
815 if not vals.ioports: return
816 ioports = []
817 for v in vals.ioports:
818 d = v.split('-')
819 if len(d) < 1 or len(d) > 2:
820 err('Invalid i/o port range specifier: ' + v)
821 if len(d) == 1:
822 d.append(d[0])
823 # Components are in hex: add hex specifier.
824 hexd = map(lambda v: '0x'+v, d)
825 ioports.append(hexd)
826 vals.ioports = ioports
828 def preprocess_vtpm(vals):
829 if not vals.vtpm: return
830 vtpms = []
831 for vtpm in vals.vtpm:
832 d = {}
833 a = vtpm.split(',')
834 for b in a:
835 (k, v) = b.strip().split('=', 1)
836 k = k.strip()
837 v = v.strip()
838 if k not in ['backend', 'instance']:
839 err('Invalid vtpm specifier: ' + vtpm)
840 d[k] = v
841 vtpms.append(d)
842 vals.vtpm = vtpms
844 def preprocess_access_control(vals):
845 if not vals.access_control:
846 return
847 access_controls = []
848 num = len(vals.access_control)
849 if num == 1:
850 access_control = (vals.access_control)[0]
851 d = {}
852 a = access_control.split(',')
853 if len(a) > 2:
854 err('Too many elements in access_control specifier: ' + access_control)
855 for b in a:
856 (k, v) = b.strip().split('=', 1)
857 k = k.strip()
858 v = v.strip()
859 if k not in ['policy','label']:
860 err('Invalid access_control specifier: ' + access_control)
861 d[k] = v
862 access_controls.append(d)
863 vals.access_control = access_controls
864 elif num > 1:
865 err('Multiple access_control definitions.')
867 def preprocess_ip(vals):
868 if vals.ip or vals.dhcp != 'off':
869 dummy_nfs_server = '1.2.3.4'
870 ip = (vals.ip
871 + ':' + (vals.nfs_server or dummy_nfs_server)
872 + ':' + vals.gateway
873 + ':' + vals.netmask
874 + ':' + vals.hostname
875 + ':' + vals.interface
876 + ':' + vals.dhcp)
877 else:
878 ip = ''
879 vals.cmdline_ip = ip
881 def preprocess_nfs(vals):
882 if not vals.nfs_root: return
883 if not vals.nfs_server:
884 err('Must set nfs root and nfs server')
885 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
886 vals.extra = nfs + ' ' + vals.extra
889 def get_host_addr():
890 host = socket.gethostname()
891 addr = socket.gethostbyname(host)
892 return addr
894 VNC_BASE_PORT = 5500
896 def choose_vnc_display():
897 """Try to choose a free vnc display.
898 """
899 def netstat_local_ports():
900 """Run netstat to get a list of the local ports in use.
901 """
902 l = os.popen("netstat -nat").readlines()
903 r = []
904 # Skip 2 lines of header.
905 for x in l[2:]:
906 # Local port is field 3.
907 y = x.split()[3]
908 # Field is addr:port, split off the port.
909 y = y.split(':')[-1]
910 r.append(int(y))
911 return r
913 ports = netstat_local_ports()
914 for d in range(1, 100):
915 port = VNC_BASE_PORT + d
916 if port in ports: continue
917 return d
918 return None
919 vncpid = None
921 def daemonize(prog, args):
922 """Runs a program as a daemon with the list of arguments. Returns the PID
923 of the daemonized program, or returns 0 on error.
924 """
925 r, w = os.pipe()
926 pid = os.fork()
928 if pid == 0:
929 os.close(r)
930 w = os.fdopen(w, 'w')
931 os.setsid()
932 try:
933 pid2 = os.fork()
934 except:
935 pid2 = None
936 if pid2 == 0:
937 os.chdir("/")
938 for fd in range(0, 256):
939 try:
940 os.close(fd)
941 except:
942 pass
943 os.open("/dev/null", os.O_RDWR)
944 os.dup2(0, 1)
945 os.dup2(0, 2)
946 os.execvp(prog, args)
947 os._exit(1)
948 else:
949 w.write(str(pid2 or 0))
950 w.close()
951 os._exit(0)
952 os.close(w)
953 r = os.fdopen(r)
954 daemon_pid = int(r.read())
955 r.close()
956 os.waitpid(pid, 0)
957 return daemon_pid
959 def spawn_vnc(display):
960 """Spawns a vncviewer that listens on the specified display. On success,
961 returns the port that the vncviewer is listening on and sets the global
962 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
963 """
964 vncargs = (["vncviewer", "-log", "*:stdout:0",
965 "-listen", "%d" % (VNC_BASE_PORT + display) ])
966 global vncpid
967 vncpid = daemonize("vncviewer", vncargs)
968 if vncpid == 0:
969 return 0
971 return VNC_BASE_PORT + display
973 def preprocess_vnc(vals):
974 """If vnc was specified, spawn a vncviewer in listen mode
975 and pass its address to the domain on the kernel command line.
976 """
977 if vals.dryrun: return
978 if vals.vncviewer:
979 vnc_display = choose_vnc_display()
980 if not vnc_display:
981 warn("No free vnc display")
982 return
983 print 'VNC=', vnc_display
984 vnc_port = spawn_vnc(vnc_display)
985 if vnc_port > 0:
986 vnc_host = get_host_addr()
987 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
988 vals.extra = vnc + ' ' + vals.extra
990 def preprocess(vals):
991 if not vals.kernel and not vals.bootloader:
992 err("No kernel specified")
993 preprocess_disk(vals)
994 preprocess_pci(vals)
995 preprocess_ioports(vals)
996 preprocess_ip(vals)
997 preprocess_nfs(vals)
998 preprocess_vnc(vals)
999 preprocess_vtpm(vals)
1000 preprocess_access_control(vals)
1003 def comma_sep_kv_to_dict(c):
1004 """Convert comma-separated, equals-separated key-value pairs into a
1005 dictionary.
1006 """
1007 d = {}
1008 c = c.strip()
1009 if len(c) > 0:
1010 a = c.split(',')
1011 for b in a:
1012 if b.find('=') == -1:
1013 err("%s should be a pair, separated by an equals sign." % b)
1014 (k, v) = b.split('=', 1)
1015 k = k.strip()
1016 v = v.strip()
1017 d[k] = v
1018 return d
1021 def make_domain(opts, config):
1022 """Create, build and start a domain.
1024 @param opts: options
1025 @param config: configuration
1026 @return: domain id
1027 @rtype: int
1028 """
1030 try:
1031 dominfo = server.xend.domain.create(config)
1032 except xmlrpclib.Fault, ex:
1033 import signal
1034 if vncpid:
1035 os.kill(vncpid, signal.SIGKILL)
1036 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
1037 err("the domain '%s' does not exist." % ex.faultString)
1038 else:
1039 err("%s" % ex.faultString)
1040 except Exception, ex:
1041 # main.py has good error messages that let the user know what failed.
1042 # unless the error is a create.py specific thing, it should be handled
1043 # at main. The purpose of this general-case 'Exception' handler is to
1044 # clean up create.py specific processes/data but since create.py does
1045 # not know what to do with the error, it should pass it up.
1046 import signal
1047 if vncpid:
1048 os.kill(vncpid, signal.SIGKILL)
1049 raise
1051 dom = sxp.child_value(dominfo, 'name')
1053 try:
1054 server.xend.domain.waitForDevices(dom)
1055 except xmlrpclib.Fault, ex:
1056 server.xend.domain.destroy(dom)
1057 err("%s" % ex.faultString)
1058 except:
1059 server.xend.domain.destroy(dom)
1060 err("Device creation failed for domain %s" % dom)
1062 if not opts.vals.paused:
1063 try:
1064 server.xend.domain.unpause(dom)
1065 except:
1066 server.xend.domain.destroy(dom)
1067 err("Failed to unpause domain %s" % dom)
1068 opts.info("Started domain %s" % (dom))
1069 return int(sxp.child_value(dominfo, 'domid'))
1072 def get_xauthority():
1073 xauth = os.getenv("XAUTHORITY")
1074 if not xauth:
1075 home = os.getenv("HOME")
1076 if not home:
1077 import posix, pwd
1078 home = pwd.getpwuid(posix.getuid())[5]
1079 xauth = home + "/.Xauthority"
1080 return xauth
1083 def parseCommandLine(argv):
1084 gopts.reset()
1085 args = gopts.parse(argv)
1087 if gopts.vals.help or gopts.vals.help_config:
1088 if gopts.vals.help_config:
1089 print gopts.val_usage()
1090 return (None, None)
1092 if not gopts.vals.display:
1093 gopts.vals.display = os.getenv("DISPLAY")
1095 if not gopts.vals.xauthority:
1096 gopts.vals.xauthority = get_xauthority()
1098 # Process remaining args as config variables.
1099 for arg in args:
1100 if '=' in arg:
1101 (var, val) = arg.strip().split('=', 1)
1102 gopts.setvar(var.strip(), val.strip())
1103 if gopts.vals.config:
1104 config = gopts.vals.config
1105 else:
1106 gopts.load_defconfig()
1107 preprocess(gopts.vals)
1108 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1109 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1110 config = make_config(gopts.vals)
1112 return (gopts, config)
1115 def check_domain_label(config, verbose):
1116 """All that we need to check here is that the domain label exists and
1117 is not null when security is on. Other error conditions are
1118 handled when the config file is parsed.
1119 """
1120 answer = 0
1121 default_label = None
1122 secon = 0
1123 if security.on():
1124 default_label = security.ssidref2label(security.NULL_SSIDREF)
1125 secon = 1
1127 # get the domain acm_label
1128 dom_label = None
1129 dom_name = None
1130 for x in sxp.children(config):
1131 if sxp.name(x) == 'security':
1132 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1133 if sxp.name(x) == 'name':
1134 dom_name = sxp.child0(x)
1136 # sanity check on domain label
1137 if verbose:
1138 print "Checking domain:"
1139 if (not secon) and (not dom_label):
1140 answer = 1
1141 if verbose:
1142 print " %s: PERMITTED" % (dom_name)
1143 elif (secon) and (dom_label) and (dom_label != default_label):
1144 answer = 1
1145 if verbose:
1146 print " %s: PERMITTED" % (dom_name)
1147 else:
1148 print " %s: DENIED" % (dom_name)
1149 if not secon:
1150 print " --> Security off, but domain labeled"
1151 else:
1152 print " --> Domain not labeled"
1153 answer = 0
1155 return answer
1157 def config_security_check(config, verbose):
1158 """Checks each resource listed in the config to see if the active
1159 policy will permit creation of a new domain using the config.
1160 Returns 1 if the config passes all tests, otherwise 0.
1161 """
1162 answer = 1
1164 # get the domain acm_label
1165 domain_label = None
1166 domain_policy = None
1167 for x in sxp.children(config):
1168 if sxp.name(x) == 'security':
1169 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1170 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1172 # if no domain label, use default
1173 if not domain_label and security.on():
1174 try:
1175 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1176 except:
1177 traceback.print_exc(limit=1)
1178 return 0
1179 domain_policy = 'NULL'
1180 elif not domain_label:
1181 domain_label = ""
1182 domain_policy = 'NULL'
1184 if verbose:
1185 print "Checking resources:"
1187 # build a list of all resources in the config file
1188 resources = []
1189 for x in sxp.children(config):
1190 if sxp.name(x) == 'device':
1191 if sxp.name(sxp.child0(x)) == 'vbd':
1192 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1194 # perform a security check on each resource
1195 for resource in resources:
1196 try:
1197 security.res_security_check(resource, domain_label)
1198 if verbose:
1199 print " %s: PERMITTED" % (resource)
1201 except security.ACMError:
1202 print " %s: DENIED" % (resource)
1203 (res_label, res_policy) = security.get_res_label(resource)
1204 if not res_label:
1205 res_label = ""
1206 print " --> res: %s (%s)" % (str(res_label),
1207 str(res_policy))
1208 print " --> dom: %s (%s)" % (str(domain_label),
1209 str(domain_policy))
1211 answer = 0
1213 return answer
1215 def create_security_check(config):
1216 passed = 0
1217 try:
1218 if check_domain_label(config, verbose=0):
1219 if config_security_check(config, verbose=0):
1220 passed = 1
1221 else:
1222 print "Checking resources: (skipped)"
1223 except security.ACMError:
1224 sys.exit(-1)
1226 return passed
1228 def help():
1229 return str(gopts)
1231 def main(argv):
1232 try:
1233 (opts, config) = parseCommandLine(argv)
1234 except StandardError, ex:
1235 err(str(ex))
1237 if not opts:
1238 return
1240 if type(config) == str:
1241 try:
1242 config = sxp.parse(file(config))[0]
1243 except IOError, exn:
1244 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1246 if opts.vals.dryrun:
1247 PrettyPrint.prettyprint(config)
1248 else:
1249 if not create_security_check(config):
1250 raise security.ACMError('Security Configuration prevents domain from starting')
1251 else:
1252 dom = make_domain(opts, config)
1253 if opts.vals.console_autoconnect:
1254 console.execConsole(dom)
1256 if __name__ == '__main__':
1257 main(sys.argv)