ia64/xen-unstable

view tools/python/xen/xm/create.py @ 12261:29bfe8852dce

Catch IOError when using local configuration files, to give better diagnostics.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
author Ewan Mellor <ewan@xensource.com>
date Mon Nov 06 11:36:38 2006 +0000 (2006-11-06)
parents 2368e779f89f
children 9c9dbc5df557
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.XendClient import server
33 from xen.xend.XendBootloader import bootloader
34 from xen.util import blkif
35 from xen.util import security
37 from xen.xm.opts import *
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=0,
178 use="Disable or enable PAE of HVM domain.")
180 gopts.var('acpi', val='ACPI',
181 fn=set_int, default=0,
182 use="Disable or enable ACPI of HVM domain.")
184 gopts.var('vcpus', val='VCPUS',
185 fn=set_int, default=1,
186 use="# of Virtual CPUS in domain.")
188 gopts.var('cpu_cap', val='CAP',
189 fn=set_int, default=None,
190 use="""Set the maximum amount of cpu.
191 CAP is a percentage that fixes the maximum amount of cpu.""")
193 gopts.var('cpu_weight', val='WEIGHT',
194 fn=set_int, default=None,
195 use="""Set the cpu time ratio to be allocated to the domain.""")
197 gopts.var('restart', val='onreboot|always|never',
198 fn=set_value, default=None,
199 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
200 instead.
202 Whether the domain should be restarted on exit.
203 - onreboot: restart on exit with shutdown code reboot
204 - always: always restart on exit, ignore exit code
205 - never: never restart on exit, ignore exit code""")
207 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
208 fn=set_value, default=None,
209 use="""Behaviour when a domain exits with reason 'poweroff'.
210 - destroy: the domain is cleaned up as normal;
211 - restart: a new domain is started in place of the old one;
212 - preserve: no clean-up is done until the domain is manually
213 destroyed (using xm destroy, for example);
214 - rename-restart: the old domain is not cleaned up, but is
215 renamed and a new domain started in its place.
216 """)
218 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
219 fn=set_value, default=None,
220 use="""Behaviour when a domain exits with reason 'reboot'.
221 - destroy: the domain is cleaned up as normal;
222 - restart: a new domain is started in place of the old one;
223 - preserve: no clean-up is done until the domain is manually
224 destroyed (using xm destroy, for example);
225 - rename-restart: the old domain is not cleaned up, but is
226 renamed and a new domain started in its place.
227 """)
229 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
230 fn=set_value, default=None,
231 use="""Behaviour when a domain exits with reason 'crash'.
232 - destroy: the domain is cleaned up as normal;
233 - restart: a new domain is started in place of the old one;
234 - preserve: no clean-up is done until the domain is manually
235 destroyed (using xm destroy, for example);
236 - rename-restart: the old domain is not cleaned up, but is
237 renamed and a new domain started in its place.
238 """)
240 gopts.var('blkif', val='no|yes',
241 fn=set_bool, default=0,
242 use="Make the domain a block device backend.")
244 gopts.var('netif', val='no|yes',
245 fn=set_bool, default=0,
246 use="Make the domain a network interface backend.")
248 gopts.var('tpmif', val='no|yes',
249 fn=append_value, default=0,
250 use="Make the domain a TPM interface backend.")
252 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
253 fn=append_value, default=[],
254 use="""Add a disk device to a domain. The physical device is DEV,
255 which is exported to the domain as VDEV. The disk is read-only if MODE
256 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
257 backend driver domain to use for the disk.
258 The option may be repeated to add more than one disk.""")
260 gopts.var('pci', val='BUS:DEV.FUNC',
261 fn=append_value, default=[],
262 use="""Add a PCI device to a domain, using given params (in hex).
263 For example 'pci=c0:02.1a'.
264 The option may be repeated to add more than one pci device.""")
266 gopts.var('ioports', val='FROM[-TO]',
267 fn=append_value, default=[],
268 use="""Add a legacy I/O range to a domain, using given params (in hex).
269 For example 'ioports=02f8-02ff'.
270 The option may be repeated to add more than one i/o range.""")
272 gopts.var('irq', val='IRQ',
273 fn=append_value, default=[],
274 use="""Add an IRQ (interrupt line) to a domain.
275 For example 'irq=7'.
276 This option may be repeated to add more than one IRQ.""")
278 gopts.var('usbport', val='PATH',
279 fn=append_value, default=[],
280 use="""Add a physical USB port to a domain, as specified by the path
281 to that port. This option may be repeated to add more than one port.""")
283 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
284 fn=append_value, default=[],
285 use="""Add a network interface with the given MAC address and bridge.
286 The vif is configured by calling the given configuration script.
287 If type is not specified, default is netfront not ioemu device.
288 If mac is not specified a random MAC address is used.
289 If not specified then the network backend chooses it's own MAC address.
290 If bridge is not specified the first bridge found is used.
291 If script is not specified the default script is used.
292 If backend is not specified the default backend driver domain is used.
293 If vifname is not specified the backend virtual interface will have name vifD.N
294 where D is the domain id and N is the interface id.
295 This option may be repeated to add more than one vif.
296 Specifying vifs will increase the number of interfaces as needed.""")
298 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
299 fn=append_value, default=[],
300 use="""Add a TPM interface. On the backend side use the given
301 instance as virtual TPM instance. The given number is merely the
302 preferred instance number. The hotplug script will determine
303 which instance number will actually be assigned to the domain.
304 The associtation between virtual machine and the TPM instance
305 number can be found in /etc/xen/vtpm.db. Use the backend in the
306 given domain.
307 The type parameter can be used to select a specific driver type
308 that the VM can use. To prevent a fully virtualized domain (HVM)
309 from being able to access an emulated device model, you may specify
310 'paravirtualized' here.""")
312 gopts.var('access_control', val="policy=POLICY,label=LABEL",
313 fn=append_value, default=[],
314 use="""Add a security label and the security policy reference that defines it.
315 The local ssid reference is calculated when starting/resuming the domain. At
316 this time, the policy is checked against the active policy as well. This way,
317 migrating through save/restore is covered and local labels are automatically
318 created correctly on the system where a domain is started / resumed.""")
320 gopts.var('nics', val="NUM",
321 fn=set_int, default=-1,
322 use="""DEPRECATED. Use empty vif entries instead.
324 Set the number of network interfaces.
325 Use the vif option to define interface parameters, otherwise
326 defaults are used. Specifying vifs will increase the
327 number of interfaces as needed.""")
329 gopts.var('root', val='DEVICE',
330 fn=set_value, default='',
331 use="""Set the root= parameter on the kernel command line.
332 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
334 gopts.var('extra', val="ARGS",
335 fn=set_value, default='',
336 use="Set extra arguments to append to the kernel command line.")
338 gopts.var('ip', val='IPADDR',
339 fn=set_value, default='',
340 use="Set the kernel IP interface address.")
342 gopts.var('gateway', val="IPADDR",
343 fn=set_value, default='',
344 use="Set the kernel IP gateway.")
346 gopts.var('netmask', val="MASK",
347 fn=set_value, default = '',
348 use="Set the kernel IP netmask.")
350 gopts.var('hostname', val="NAME",
351 fn=set_value, default='',
352 use="Set the kernel IP hostname.")
354 gopts.var('interface', val="INTF",
355 fn=set_value, default="eth0",
356 use="Set the kernel IP interface name.")
358 gopts.var('dhcp', val="off|dhcp",
359 fn=set_value, default='off',
360 use="Set the kernel dhcp option.")
362 gopts.var('nfs_server', val="IPADDR",
363 fn=set_value, default=None,
364 use="Set the address of the NFS server for NFS root.")
366 gopts.var('nfs_root', val="PATH",
367 fn=set_value, default=None,
368 use="Set the path of the root NFS directory.")
370 gopts.var('device_model', val='FILE',
371 fn=set_value, default='',
372 use="Path to device model program.")
374 gopts.var('fda', val='FILE',
375 fn=set_value, default='',
376 use="Path to fda")
378 gopts.var('fdb', val='FILE',
379 fn=set_value, default='',
380 use="Path to fdb")
382 gopts.var('serial', val='FILE',
383 fn=set_value, default='',
384 use="Path to serial or pty or vc")
386 gopts.var('localtime', val='no|yes',
387 fn=set_bool, default=0,
388 use="Is RTC set to localtime?")
390 gopts.var('usb', val='no|yes',
391 fn=set_bool, default=0,
392 use="Emulate USB devices?")
394 gopts.var('usbdevice', val='NAME',
395 fn=set_value, default='',
396 use="Name of USB device to add?")
398 gopts.var('stdvga', val='no|yes',
399 fn=set_bool, default=0,
400 use="Use std vga or cirrhus logic graphics")
402 gopts.var('isa', val='no|yes',
403 fn=set_bool, default=0,
404 use="Simulate an ISA only system?")
406 gopts.var('boot', val="a|b|c|d",
407 fn=set_value, default='c',
408 use="Default boot device")
410 gopts.var('nographic', val='no|yes',
411 fn=set_bool, default=0,
412 use="Should device models use graphics?")
414 gopts.var('soundhw', val='audiodev',
415 fn=set_value, default='',
416 use="Should device models enable audio device?")
418 gopts.var('vnc', val='',
419 fn=set_value, default=None,
420 use="""Should the device model use VNC?""")
422 gopts.var('vncdisplay', val='',
423 fn=set_value, default=None,
424 use="""VNC display to use""")
426 gopts.var('vnclisten', val='',
427 fn=set_value, default=None,
428 use="""Address for VNC server to listen on.""")
430 gopts.var('vncunused', val='',
431 fn=set_bool, default=1,
432 use="""Try to find an unused port for the VNC server.
433 Only valid when vnc=1.""")
435 gopts.var('sdl', val='',
436 fn=set_value, default=None,
437 use="""Should the device model use SDL?""")
439 gopts.var('display', val='DISPLAY',
440 fn=set_value, default=None,
441 use="X11 display to use")
443 gopts.var('xauthority', val='XAUTHORITY',
444 fn=set_value, default=None,
445 use="X11 Authority to use")
447 gopts.var('uuid', val='',
448 fn=set_value, default=None,
449 use="""xenstore UUID (universally unique identifier) to use. One
450 will be randomly generated if this option is not set, just like MAC
451 addresses for virtual network interfaces. This must be a unique
452 value across the entire cluster.""")
454 gopts.var('on_xend_start', val='ignore|start',
455 fn=set_value, default='ignore',
456 use='Action to perform when xend starts')
458 gopts.var('on_xend_stop', val='continue|shutdown|suspend',
459 fn=set_value, default="ignore",
460 use="""Behaviour when Xend stops:
461 - ignore: Domain continues to run;
462 - shutdown: Domain is shutdown;
463 - suspend: Domain is suspended;
464 """)
466 def err(msg):
467 """Print an error to stderr and exit.
468 """
469 print >>sys.stderr, "Error:", msg
470 sys.exit(1)
473 def warn(msg):
474 """Print a warning to stdout.
475 """
476 print >>sys.stderr, "Warning:", msg
479 def strip(pre, s):
480 """Strip prefix 'pre' if present.
481 """
482 if s.startswith(pre):
483 return s[len(pre):]
484 else:
485 return s
487 def configure_image(vals):
488 """Create the image config.
489 """
490 if not vals.builder:
491 return None
492 config_image = [ vals.builder ]
493 if vals.kernel:
494 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
495 if vals.ramdisk:
496 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
497 if vals.cmdline_ip:
498 cmdline_ip = strip('ip=', vals.cmdline_ip)
499 config_image.append(['ip', cmdline_ip])
500 if vals.root:
501 cmdline_root = strip('root=', vals.root)
502 config_image.append(['root', cmdline_root])
503 if vals.extra:
504 config_image.append(['args', vals.extra])
506 if vals.builder == 'hvm':
507 configure_hvm(config_image, vals)
509 return config_image
511 def configure_disks(config_devs, vals):
512 """Create the config for disks (virtual block devices).
513 """
514 for (uname, dev, mode, backend) in vals.disk:
515 if uname.startswith('tap:'):
516 cls = 'tap'
517 else:
518 cls = 'vbd'
520 config_vbd = [cls,
521 ['uname', uname],
522 ['dev', dev ],
523 ['mode', mode ] ]
524 if backend:
525 config_vbd.append(['backend', backend])
526 config_devs.append(['device', config_vbd])
528 def configure_pci(config_devs, vals):
529 """Create the config for pci devices.
530 """
531 config_pci = []
532 for (domain, bus, slot, func) in vals.pci:
533 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
534 ['slot', slot], ['func', func]])
536 if len(config_pci)>0:
537 config_pci.insert(0, 'pci')
538 config_devs.append(['device', config_pci])
540 def configure_ioports(config_devs, vals):
541 """Create the config for legacy i/o ranges.
542 """
543 for (io_from, io_to) in vals.ioports:
544 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
545 config_devs.append(['device', config_ioports])
547 def configure_irq(config_devs, vals):
548 """Create the config for irqs.
549 """
550 for irq in vals.irq:
551 config_irq = ['irq', ['irq', irq]]
552 config_devs.append(['device', config_irq])
554 def configure_usb(config_devs, vals):
555 for path in vals.usbport:
556 config_usb = ['usbport', ['path', path]]
557 config_devs.append(['device', config_usb])
560 def configure_security(config, vals):
561 """Create the config for ACM security labels.
562 """
563 access_control = vals.access_control
564 num = len(access_control)
565 if num == 1:
566 d = access_control[0]
567 policy = d.get('policy')
568 label = d.get('label')
569 if policy != security.active_policy:
570 err("Security policy (" + policy + ") incompatible with enforced policy ("
571 + security.active_policy + ")." )
572 config_access_control = ['access_control',
573 ['policy', policy],
574 ['label', label] ]
576 #ssidref cannot be specified together with access_control
577 if sxp.child_value(config, 'ssidref'):
578 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
579 #else calculate ssidre from label
580 ssidref = security.label2ssidref(label, policy, 'dom')
581 if not ssidref :
582 err("ERROR calculating ssidref from access_control.")
583 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
584 config.append(security_label)
585 elif num == 0:
586 if hasattr(vals, 'ssidref'):
587 if not security.on():
588 err("ERROR: Security ssidref specified but no policy active.")
589 ssidref = getattr(vals, 'ssidref')
590 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
591 config.append(security_label)
592 elif num > 1:
593 err("VM config error: Multiple access_control definitions!")
596 def configure_vtpm(config_devs, vals):
597 """Create the config for virtual TPM interfaces.
598 """
599 vtpm = vals.vtpm
600 if len(vtpm) > 0:
601 d = vtpm[0]
602 instance = d.get('instance')
603 if instance == "VTPMD":
604 instance = "0"
605 else:
606 if instance != None:
607 try:
608 if int(instance) == 0:
609 err('VM config error: vTPM instance must not be 0.')
610 except ValueError:
611 err('Vm config error: could not parse instance number.')
612 backend = d.get('backend')
613 typ = d.get('type')
614 config_vtpm = ['vtpm']
615 if instance:
616 config_vtpm.append(['pref_instance', instance])
617 if backend:
618 config_vtpm.append(['backend', backend])
619 if typ:
620 config_vtpm.append(['type', type])
621 config_devs.append(['device', config_vtpm])
624 def configure_vifs(config_devs, vals):
625 """Create the config for virtual network interfaces.
626 """
628 vifs = vals.vif
629 vifs_n = len(vifs)
631 if hasattr(vals, 'nics'):
632 if vals.nics > 0:
633 warn("The nics option is deprecated. Please use an empty vif "
634 "entry instead:\n\n vif = [ '' ]\n")
635 for _ in range(vifs_n, vals.nics):
636 vifs.append('')
637 vifs_n = len(vifs)
638 elif vals.nics == 0:
639 warn("The nics option is deprecated. Please remove it.")
641 for c in vifs:
642 d = comma_sep_kv_to_dict(c)
643 config_vif = ['vif']
645 def f(k):
646 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
647 'vifname', 'rate', 'model']:
648 err('Invalid vif option: ' + k)
650 config_vif.append([k, d[k]])
652 map(f, d.keys())
653 config_devs.append(['device', config_vif])
656 def configure_hvm(config_image, vals):
657 """Create the config for HVM devices.
658 """
659 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
660 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
661 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
662 'sdl', 'display', 'xauthority',
663 'acpi', 'usb', 'usbdevice' ]
664 for a in args:
665 if (vals.__dict__[a]):
666 config_image.append([a, vals.__dict__[a]])
667 config_image.append(['vncpasswd', vals.vncpasswd])
669 def run_bootloader(vals, config_image):
670 if not os.access(vals.bootloader, os.X_OK):
671 err("Bootloader isn't executable")
672 if len(vals.disk) < 1:
673 err("No disks configured and boot loader requested")
674 (uname, dev, mode, backend) = vals.disk[0]
675 file = blkif.blkdev_uname_to_file(uname)
677 if vals.bootentry:
678 warn("The bootentry option is deprecated. Use bootargs and pass "
679 "--entry= directly.")
680 vals.bootargs = "--entry=%s" %(vals.bootentry,)
682 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
683 vals.bootargs, config_image)
685 def make_config(vals):
686 """Create the domain configuration.
687 """
689 config = ['vm']
691 def add_conf(n):
692 if hasattr(vals, n):
693 v = getattr(vals, n)
694 if v:
695 config.append([n, v])
697 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
698 'restart', 'on_poweroff',
699 'on_reboot', 'on_crash', 'vcpus', 'features',
700 'on_xend_start', 'on_xend_stop'])
702 if vals.uuid is not None:
703 config.append(['uuid', vals.uuid])
704 if vals.cpu is not None:
705 config.append(['cpu', vals.cpu])
706 if vals.cpus is not None:
707 config.append(['cpus', vals.cpus])
708 if vals.cpu_cap is not None:
709 config.append(['cpu_cap', vals.cpu_cap])
710 if vals.cpu_weight is not None:
711 config.append(['cpu_weight', vals.cpu_weight])
712 if vals.blkif:
713 config.append(['backend', ['blkif']])
714 if vals.netif:
715 config.append(['backend', ['netif']])
716 if vals.tpmif:
717 config.append(['backend', ['tpmif']])
718 if vals.localtime:
719 config.append(['localtime', vals.localtime])
721 config_image = configure_image(vals)
722 if vals.bootloader:
723 config_image = run_bootloader(vals, config_image)
724 config.append(['bootloader', vals.bootloader])
725 if vals.bootargs:
726 config.append(['bootloader_args', vals.bootargs])
727 config.append(['image', config_image])
729 config_devs = []
730 configure_disks(config_devs, vals)
731 configure_pci(config_devs, vals)
732 configure_ioports(config_devs, vals)
733 configure_irq(config_devs, vals)
734 configure_vifs(config_devs, vals)
735 configure_usb(config_devs, vals)
736 configure_vtpm(config_devs, vals)
737 configure_security(config, vals)
738 config += config_devs
740 return config
742 def preprocess_disk(vals):
743 if not vals.disk: return
744 disk = []
745 for v in vals.disk:
746 d = v.split(',')
747 n = len(d)
748 if n == 3:
749 d.append(None)
750 elif n == 4:
751 pass
752 else:
753 err('Invalid disk specifier: ' + v)
754 disk.append(d)
755 vals.disk = disk
757 def preprocess_pci(vals):
758 if not vals.pci: return
759 pci = []
760 for pci_dev_str in vals.pci:
761 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
762 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
763 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
764 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
765 if pci_match!=None:
766 pci_dev_info = pci_match.groupdict('0')
767 try:
768 pci.append( ('0x'+pci_dev_info['domain'], \
769 '0x'+pci_dev_info['bus'], \
770 '0x'+pci_dev_info['slot'], \
771 '0x'+pci_dev_info['func']))
772 except IndexError:
773 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
774 vals.pci = pci
776 def preprocess_ioports(vals):
777 if not vals.ioports: return
778 ioports = []
779 for v in vals.ioports:
780 d = v.split('-')
781 if len(d) < 1 or len(d) > 2:
782 err('Invalid i/o port range specifier: ' + v)
783 if len(d) == 1:
784 d.append(d[0])
785 # Components are in hex: add hex specifier.
786 hexd = map(lambda v: '0x'+v, d)
787 ioports.append(hexd)
788 vals.ioports = ioports
790 def preprocess_vtpm(vals):
791 if not vals.vtpm: return
792 vtpms = []
793 for vtpm in vals.vtpm:
794 d = {}
795 a = vtpm.split(',')
796 for b in a:
797 (k, v) = b.strip().split('=', 1)
798 k = k.strip()
799 v = v.strip()
800 if k not in ['backend', 'instance']:
801 err('Invalid vtpm specifier: ' + vtpm)
802 d[k] = v
803 vtpms.append(d)
804 vals.vtpm = vtpms
806 def preprocess_access_control(vals):
807 if not vals.access_control:
808 return
809 access_controls = []
810 num = len(vals.access_control)
811 if num == 1:
812 access_control = (vals.access_control)[0]
813 d = {}
814 a = access_control.split(',')
815 if len(a) > 2:
816 err('Too many elements in access_control specifier: ' + access_control)
817 for b in a:
818 (k, v) = b.strip().split('=', 1)
819 k = k.strip()
820 v = v.strip()
821 if k not in ['policy','label']:
822 err('Invalid access_control specifier: ' + access_control)
823 d[k] = v
824 access_controls.append(d)
825 vals.access_control = access_controls
826 elif num > 1:
827 err('Multiple access_control definitions.')
829 def preprocess_ip(vals):
830 if vals.ip or vals.dhcp != 'off':
831 dummy_nfs_server = '1.2.3.4'
832 ip = (vals.ip
833 + ':' + (vals.nfs_server or dummy_nfs_server)
834 + ':' + vals.gateway
835 + ':' + vals.netmask
836 + ':' + vals.hostname
837 + ':' + vals.interface
838 + ':' + vals.dhcp)
839 else:
840 ip = ''
841 vals.cmdline_ip = ip
843 def preprocess_nfs(vals):
844 if not vals.nfs_root: return
845 if not vals.nfs_server:
846 err('Must set nfs root and nfs server')
847 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
848 vals.extra = nfs + ' ' + vals.extra
851 def get_host_addr():
852 host = socket.gethostname()
853 addr = socket.gethostbyname(host)
854 return addr
856 VNC_BASE_PORT = 5500
858 def choose_vnc_display():
859 """Try to choose a free vnc display.
860 """
861 def netstat_local_ports():
862 """Run netstat to get a list of the local ports in use.
863 """
864 l = os.popen("netstat -nat").readlines()
865 r = []
866 # Skip 2 lines of header.
867 for x in l[2:]:
868 # Local port is field 3.
869 y = x.split()[3]
870 # Field is addr:port, split off the port.
871 y = y.split(':')[-1]
872 r.append(int(y))
873 return r
875 ports = netstat_local_ports()
876 for d in range(1, 100):
877 port = VNC_BASE_PORT + d
878 if port in ports: continue
879 return d
880 return None
881 vncpid = None
883 def daemonize(prog, args):
884 """Runs a program as a daemon with the list of arguments. Returns the PID
885 of the daemonized program, or returns 0 on error.
886 """
887 r, w = os.pipe()
888 pid = os.fork()
890 if pid == 0:
891 os.close(r)
892 w = os.fdopen(w, 'w')
893 os.setsid()
894 try:
895 pid2 = os.fork()
896 except:
897 pid2 = None
898 if pid2 == 0:
899 os.chdir("/")
900 for fd in range(0, 256):
901 try:
902 os.close(fd)
903 except:
904 pass
905 os.open("/dev/null", os.O_RDWR)
906 os.dup2(0, 1)
907 os.dup2(0, 2)
908 os.execvp(prog, args)
909 os._exit(1)
910 else:
911 w.write(str(pid2 or 0))
912 w.close()
913 os._exit(0)
914 os.close(w)
915 r = os.fdopen(r)
916 daemon_pid = int(r.read())
917 r.close()
918 os.waitpid(pid, 0)
919 return daemon_pid
921 def spawn_vnc(display):
922 """Spawns a vncviewer that listens on the specified display. On success,
923 returns the port that the vncviewer is listening on and sets the global
924 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
925 """
926 vncargs = (["vncviewer", "-log", "*:stdout:0",
927 "-listen", "%d" % (VNC_BASE_PORT + display) ])
928 global vncpid
929 vncpid = daemonize("vncviewer", vncargs)
930 if vncpid == 0:
931 return 0
933 return VNC_BASE_PORT + display
935 def preprocess_vnc(vals):
936 """If vnc was specified, spawn a vncviewer in listen mode
937 and pass its address to the domain on the kernel command line.
938 """
939 if vals.dryrun: return
940 if vals.vncviewer:
941 vnc_display = choose_vnc_display()
942 if not vnc_display:
943 warn("No free vnc display")
944 return
945 print 'VNC=', vnc_display
946 vnc_port = spawn_vnc(vnc_display)
947 if vnc_port > 0:
948 vnc_host = get_host_addr()
949 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
950 vals.extra = vnc + ' ' + vals.extra
952 def preprocess(vals):
953 if not vals.kernel and not vals.bootloader:
954 err("No kernel specified")
955 preprocess_disk(vals)
956 preprocess_pci(vals)
957 preprocess_ioports(vals)
958 preprocess_ip(vals)
959 preprocess_nfs(vals)
960 preprocess_vnc(vals)
961 preprocess_vtpm(vals)
962 preprocess_access_control(vals)
965 def comma_sep_kv_to_dict(c):
966 """Convert comma-separated, equals-separated key-value pairs into a
967 dictionary.
968 """
969 d = {}
970 c = c.strip()
971 if len(c) > 0:
972 a = c.split(',')
973 for b in a:
974 if b.find('=') == -1:
975 err("%s should be a pair, separated by an equals sign." % b)
976 (k, v) = b.split('=', 1)
977 k = k.strip()
978 v = v.strip()
979 d[k] = v
980 return d
983 def make_domain(opts, config):
984 """Create, build and start a domain.
986 @param opts: options
987 @param config: configuration
988 @return: domain id
989 @rtype: int
990 """
992 try:
993 dominfo = server.xend.domain.create(config)
994 except xmlrpclib.Fault, ex:
995 import signal
996 if vncpid:
997 os.kill(vncpid, signal.SIGKILL)
998 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
999 err("the domain '%s' does not exist." % ex.faultString)
1000 else:
1001 err("%s" % ex.faultString)
1002 except Exception, ex:
1003 # main.py has good error messages that let the user know what failed.
1004 # unless the error is a create.py specific thing, it should be handled
1005 # at main. The purpose of this general-case 'Exception' handler is to
1006 # clean up create.py specific processes/data but since create.py does
1007 # not know what to do with the error, it should pass it up.
1008 import signal
1009 if vncpid:
1010 os.kill(vncpid, signal.SIGKILL)
1011 raise
1013 dom = sxp.child_value(dominfo, 'name')
1015 try:
1016 server.xend.domain.waitForDevices(dom)
1017 except xmlrpclib.Fault, ex:
1018 server.xend.domain.destroy(dom)
1019 err("%s" % ex.faultString)
1020 except:
1021 server.xend.domain.destroy(dom)
1022 err("Device creation failed for domain %s" % dom)
1024 if not opts.vals.paused:
1025 try:
1026 server.xend.domain.unpause(dom)
1027 except:
1028 server.xend.domain.destroy(dom)
1029 err("Failed to unpause domain %s" % dom)
1030 opts.info("Started domain %s" % (dom))
1031 return int(sxp.child_value(dominfo, 'domid'))
1034 def get_xauthority():
1035 xauth = os.getenv("XAUTHORITY")
1036 if not xauth:
1037 home = os.getenv("HOME")
1038 if not home:
1039 import posix, pwd
1040 home = pwd.getpwuid(posix.getuid())[5]
1041 xauth = home + "/.Xauthority"
1042 return xauth
1045 def parseCommandLine(argv):
1046 gopts.reset()
1047 args = gopts.parse(argv)
1049 if gopts.vals.help or gopts.vals.help_config:
1050 if gopts.vals.help_config:
1051 print gopts.val_usage()
1052 return (None, None)
1054 if not gopts.vals.display:
1055 gopts.vals.display = os.getenv("DISPLAY")
1057 if not gopts.vals.xauthority:
1058 gopts.vals.xauthority = get_xauthority()
1060 # Process remaining args as config variables.
1061 for arg in args:
1062 if '=' in arg:
1063 (var, val) = arg.strip().split('=', 1)
1064 gopts.setvar(var.strip(), val.strip())
1065 if gopts.vals.config:
1066 config = gopts.vals.config
1067 else:
1068 gopts.load_defconfig()
1069 preprocess(gopts.vals)
1070 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1071 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1072 config = make_config(gopts.vals)
1074 return (gopts, config)
1077 def check_domain_label(config, verbose):
1078 """All that we need to check here is that the domain label exists and
1079 is not null when security is on. Other error conditions are
1080 handled when the config file is parsed.
1081 """
1082 answer = 0
1083 default_label = None
1084 secon = 0
1085 if security.on():
1086 default_label = security.ssidref2label(security.NULL_SSIDREF)
1087 secon = 1
1089 # get the domain acm_label
1090 dom_label = None
1091 dom_name = None
1092 for x in sxp.children(config):
1093 if sxp.name(x) == 'security':
1094 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1095 if sxp.name(x) == 'name':
1096 dom_name = sxp.child0(x)
1098 # sanity check on domain label
1099 if verbose:
1100 print "Checking domain:"
1101 if (not secon) and (not dom_label):
1102 answer = 1
1103 if verbose:
1104 print " %s: PERMITTED" % (dom_name)
1105 elif (secon) and (dom_label) and (dom_label != default_label):
1106 answer = 1
1107 if verbose:
1108 print " %s: PERMITTED" % (dom_name)
1109 else:
1110 print " %s: DENIED" % (dom_name)
1111 if not secon:
1112 print " --> Security off, but domain labeled"
1113 else:
1114 print " --> Domain not labeled"
1115 answer = 0
1117 return answer
1119 def config_security_check(config, verbose):
1120 """Checks each resource listed in the config to see if the active
1121 policy will permit creation of a new domain using the config.
1122 Returns 1 if the config passes all tests, otherwise 0.
1123 """
1124 answer = 1
1126 # get the domain acm_label
1127 domain_label = None
1128 domain_policy = None
1129 for x in sxp.children(config):
1130 if sxp.name(x) == 'security':
1131 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1132 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1134 # if no domain label, use default
1135 if not domain_label and security.on():
1136 try:
1137 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1138 except:
1139 traceback.print_exc(limit=1)
1140 return 0
1141 domain_policy = 'NULL'
1142 elif not domain_label:
1143 domain_label = ""
1144 domain_policy = 'NULL'
1146 if verbose:
1147 print "Checking resources:"
1149 # build a list of all resources in the config file
1150 resources = []
1151 for x in sxp.children(config):
1152 if sxp.name(x) == 'device':
1153 if sxp.name(sxp.child0(x)) == 'vbd':
1154 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1156 # perform a security check on each resource
1157 for resource in resources:
1158 try:
1159 security.res_security_check(resource, domain_label)
1160 if verbose:
1161 print " %s: PERMITTED" % (resource)
1163 except security.ACMError:
1164 print " %s: DENIED" % (resource)
1165 (res_label, res_policy) = security.get_res_label(resource)
1166 if not res_label:
1167 res_label = ""
1168 print " --> res: %s (%s)" % (str(res_label),
1169 str(res_policy))
1170 print " --> dom: %s (%s)" % (str(domain_label),
1171 str(domain_policy))
1173 answer = 0
1175 return answer
1177 def create_security_check(config):
1178 passed = 0
1179 try:
1180 if check_domain_label(config, verbose=0):
1181 if config_security_check(config, verbose=0):
1182 passed = 1
1183 else:
1184 print "Checking resources: (skipped)"
1185 except security.ACMError:
1186 sys.exit(-1)
1188 return passed
1190 def help():
1191 return str(gopts)
1193 def main(argv):
1194 try:
1195 (opts, config) = parseCommandLine(argv)
1196 except StandardError, ex:
1197 err(str(ex))
1199 if not opts:
1200 return
1202 if type(config) == str:
1203 try:
1204 config = sxp.parse(file(config))[0]
1205 except IOError, exn:
1206 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1208 if opts.vals.dryrun:
1209 PrettyPrint.prettyprint(config)
1210 else:
1211 if not create_security_check(config):
1212 raise security.ACMError('Security Configuration prevents domain from starting')
1213 else:
1214 dom = make_domain(opts, config)
1215 if opts.vals.console_autoconnect:
1216 console.execConsole(dom)
1218 if __name__ == '__main__':
1219 main(sys.argv)