ia64/xen-unstable

view tools/python/xen/xm/create.py @ 10998:ae14b5b77938

[XEND] Daemonise vncviewer when it is spawned from xend.

There are various reasons it should be daemonized, but the particular
problem we hit was that YaST called "xm create" and waited on output on
stdout/stderr; xm then spawned vncviewer (which never closed its
inherited stdout and stderr); xm then would exit, but YaST still had
open file descriptors, and therefore waited forever. It would be
possible to work around in YaST, but it seemed cleaner to daemonize
vncviewer.

Signed-off-by: Charles Coffing <ccoffing@novell.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Aug 08 10:21:51 2006 +0100 (2006-08-08)
parents 92ef1215a6f1
children 58a04bfedf6b
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
28 import traceback
30 from xen.xend import sxp
31 from xen.xend import PrettyPrint
32 import xen.xend.XendClient
33 from xen.xend.XendClient import server
34 from xen.xend.XendBootloader import bootloader
35 from xen.util import blkif
36 from xen.util import security
38 from xen.xm.opts import *
40 import console
43 gopts = Opts(use="""[options] [vars]
45 Create a domain.
47 Domain creation parameters can be set by command-line switches, from
48 a python configuration script or an SXP config file. See documentation
49 for --defconfig, --config. Configuration variables can be set using
50 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
52 """)
54 gopts.opt('help', short='h',
55 fn=set_true, default=0,
56 use="Print this help.")
58 gopts.opt('help_config',
59 fn=set_true, default=0,
60 use="Print help for the 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 processed.
75 Each command-line option sets a configuration variable named after
76 its long option name, and these variables are placed in the
77 environment of the script before it is loaded.
78 Variables for options that may be repeated have list values.
79 Other variables can be set using VAR=VAL on the command line.
81 After the script is loaded, option values that were not set on the
82 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).
89 SXP is the underlying configuration format used by Xen.
90 SXP configurations can be hand-written or generated from Python configuration
91 scripts, using the -n (dryrun) option to print the configuration.""")
93 gopts.opt('dryrun', short='n',
94 fn=set_true, default=0,
95 use="""Dry run - print the configuration but don't create the domain.
96 Loads the configuration script, creates the SXP configuration and prints it.""")
98 gopts.opt('paused', short='p',
99 fn=set_true, default=0,
100 use='Leave the domain paused after it is created.')
102 gopts.opt('console_autoconnect', short='c',
103 fn=set_true, default=0,
104 use="Connect to the console after the domain is created.")
106 gopts.var('vncviewer', val='no|yes',
107 fn=set_bool, default=None,
108 use="""Spawn a vncviewer listening for a vnc server in the domain.
109 The address of the vncviewer is passed to the domain on the kernel command
110 line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
111 A display value with a free port is chosen if possible.
112 Only valid when vnc=1.
113 """)
115 gopts.var('vncconsole', val='no|yes',
116 fn=set_bool, default=None,
117 use="""Spawn a vncviewer process for the domain's graphical console.
118 Only valid when vnc=1.
119 """)
121 gopts.var('name', val='NAME',
122 fn=set_value, default=None,
123 use="Domain name. Must be unique.")
125 gopts.var('bootloader', val='FILE',
126 fn=set_value, default=None,
127 use="Path to bootloader.")
129 gopts.var('bootargs', val='NAME',
130 fn=set_value, default=None,
131 use="Arguments to pass to boot loader")
133 gopts.var('bootentry', val='NAME',
134 fn=set_value, default=None,
135 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
137 gopts.var('kernel', val='FILE',
138 fn=set_value, default=None,
139 use="Path to kernel image.")
141 gopts.var('ramdisk', val='FILE',
142 fn=set_value, default='',
143 use="Path to ramdisk.")
145 gopts.var('features', val='FEATURES',
146 fn=set_value, default='',
147 use="Features to enable in guest kernel")
149 gopts.var('builder', val='FUNCTION',
150 fn=set_value, default='linux',
151 use="Function to use to build the domain.")
153 gopts.var('memory', val='MEMORY',
154 fn=set_int, default=128,
155 use="Domain memory in MB.")
157 gopts.var('maxmem', val='MEMORY',
158 fn=set_int, default=None,
159 use="Maximum domain memory in MB.")
161 gopts.var('cpu', val='CPU',
162 fn=set_int, default=None,
163 use="CPU to run the VCPU0 on.")
165 gopts.var('cpus', val='CPUS',
166 fn=set_value, default=None,
167 use="CPUS to run the domain on.")
169 gopts.var('pae', val='PAE',
170 fn=set_int, default=0,
171 use="Disable or enable PAE of HVM domain.")
173 gopts.var('acpi', val='ACPI',
174 fn=set_int, default=0,
175 use="Disable or enable ACPI of HVM domain.")
177 gopts.var('apic', val='APIC',
178 fn=set_int, default=0,
179 use="Disable or enable APIC of HVM domain.")
181 gopts.var('vcpus', val='VCPUS',
182 fn=set_int, default=1,
183 use="# of Virtual CPUS in domain.")
185 gopts.var('cpu_weight', val='WEIGHT',
186 fn=set_float, default=None,
187 use="""Set the new domain's cpu weight.
188 WEIGHT is a float that controls the domain's share of the cpu.""")
190 gopts.var('restart', val='onreboot|always|never',
191 fn=set_value, default=None,
192 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
193 instead.
195 Whether the domain should be restarted on exit.
196 - onreboot: restart on exit with shutdown code reboot
197 - always: always restart on exit, ignore exit code
198 - never: never restart on exit, ignore exit code""")
200 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
201 fn=set_value, default=None,
202 use="""Behaviour when a domain exits with reason 'poweroff'.
203 - destroy: the domain is cleaned up as normal;
204 - restart: a new domain is started in place of the old one;
205 - preserve: no clean-up is done until the domain is manually
206 destroyed (using xm destroy, for example);
207 - rename-restart: the old domain is not cleaned up, but is
208 renamed and a new domain started in its place.
209 """)
211 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
212 fn=set_value, default=None,
213 use="""Behaviour when a domain exits with reason 'reboot'.
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_crash', val='destroy|restart|preserve|rename-restart',
223 fn=set_value, default=None,
224 use="""Behaviour when a domain exits with reason 'crash'.
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('blkif', val='no|yes',
234 fn=set_bool, default=0,
235 use="Make the domain a block device backend.")
237 gopts.var('netif', val='no|yes',
238 fn=set_bool, default=0,
239 use="Make the domain a network interface backend.")
241 gopts.var('tpmif', val='no|yes',
242 fn=append_value, default=0,
243 use="Make the domain a TPM interface backend.")
245 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
246 fn=append_value, default=[],
247 use="""Add a disk device to a domain. The physical device is DEV,
248 which is exported to the domain as VDEV. The disk is read-only if MODE
249 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
250 backend driver domain to use for the disk.
251 The option may be repeated to add more than one disk.""")
253 gopts.var('pci', val='BUS:DEV.FUNC',
254 fn=append_value, default=[],
255 use="""Add a PCI device to a domain, using given params (in hex).
256 For example 'pci=c0:02.1a'.
257 The option may be repeated to add more than one pci device.""")
259 gopts.var('ioports', val='FROM[-TO]',
260 fn=append_value, default=[],
261 use="""Add a legacy I/O range to a domain, using given params (in hex).
262 For example 'ioports=02f8-02ff'.
263 The option may be repeated to add more than one i/o range.""")
265 gopts.var('irq', val='IRQ',
266 fn=append_value, default=[],
267 use="""Add an IRQ (interrupt line) to a domain.
268 For example 'irq=7'.
269 This option may be repeated to add more than one IRQ.""")
271 gopts.var('usbport', val='PATH',
272 fn=append_value, default=[],
273 use="""Add a physical USB port to a domain, as specified by the path
274 to that port. This option may be repeated to add more than one port.""")
276 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
277 fn=append_value, default=[],
278 use="""Add a network interface with the given MAC address and bridge.
279 The vif is configured by calling the given configuration script.
280 If type is not specified, default is netfront not ioemu device.
281 If mac is not specified a random MAC address is used.
282 If not specified then the network backend chooses it's own MAC address.
283 If bridge is not specified the first bridge found is used.
284 If script is not specified the default script is used.
285 If backend is not specified the default backend driver domain is used.
286 If vifname is not specified the backend virtual interface will have name vifD.N
287 where D is the domain id and N is the interface id.
288 This option may be repeated to add more than one vif.
289 Specifying vifs will increase the number of interfaces as needed.""")
291 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
292 fn=append_value, default=[],
293 use="""Add a TPM interface. On the backend side use the given
294 instance as virtual TPM instance. The given number is merely the
295 preferred instance number. The hotplug script will determine
296 which instance number will actually be assigned to the domain.
297 The associtation between virtual machine and the TPM instance
298 number can be found in /etc/xen/vtpm.db. Use the backend in the
299 given domain.""")
301 gopts.var('access_control', val="policy=POLICY,label=LABEL",
302 fn=append_value, default=[],
303 use="""Add a security label and the security policy reference that defines it.
304 The local ssid reference is calculated when starting/resuming the domain. At
305 this time, the policy is checked against the active policy as well. This way,
306 migrating through save/restore is covered and local labels are automatically
307 created correctly on the system where a domain is started / resumed.""")
309 gopts.var('nics', val="NUM",
310 fn=set_int, default=-1,
311 use="""DEPRECATED. Use empty vif entries instead.
313 Set the number of network interfaces.
314 Use the vif option to define interface parameters, otherwise
315 defaults are used. Specifying vifs will increase the
316 number of interfaces as needed.""")
318 gopts.var('root', val='DEVICE',
319 fn=set_value, default='',
320 use="""Set the root= parameter on the kernel command line.
321 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
323 gopts.var('extra', val="ARGS",
324 fn=set_value, default='',
325 use="Set extra arguments to append to the kernel command line.")
327 gopts.var('ip', val='IPADDR',
328 fn=set_value, default='',
329 use="Set the kernel IP interface address.")
331 gopts.var('gateway', val="IPADDR",
332 fn=set_value, default='',
333 use="Set the kernel IP gateway.")
335 gopts.var('netmask', val="MASK",
336 fn=set_value, default = '',
337 use="Set the kernel IP netmask.")
339 gopts.var('hostname', val="NAME",
340 fn=set_value, default='',
341 use="Set the kernel IP hostname.")
343 gopts.var('interface', val="INTF",
344 fn=set_value, default="eth0",
345 use="Set the kernel IP interface name.")
347 gopts.var('dhcp', val="off|dhcp",
348 fn=set_value, default='off',
349 use="Set the kernel dhcp option.")
351 gopts.var('nfs_server', val="IPADDR",
352 fn=set_value, default=None,
353 use="Set the address of the NFS server for NFS root.")
355 gopts.var('nfs_root', val="PATH",
356 fn=set_value, default=None,
357 use="Set the path of the root NFS directory.")
359 gopts.var('device_model', val='FILE',
360 fn=set_value, default='',
361 use="Path to device model program.")
363 gopts.var('fda', val='FILE',
364 fn=set_value, default='',
365 use="Path to fda")
367 gopts.var('fdb', val='FILE',
368 fn=set_value, default='',
369 use="Path to fdb")
371 gopts.var('serial', val='FILE',
372 fn=set_value, default='',
373 use="Path to serial or pty or vc")
375 gopts.var('localtime', val='no|yes',
376 fn=set_bool, default=0,
377 use="Is RTC set to localtime?")
379 gopts.var('usb', val='no|yes',
380 fn=set_bool, default=0,
381 use="Emulate USB devices?")
383 gopts.var('usbdevice', val='NAME',
384 fn=set_value, default='',
385 use="Name of USB device to add?")
387 gopts.var('stdvga', val='no|yes',
388 fn=set_bool, default=0,
389 use="Use std vga or cirrhus logic graphics")
391 gopts.var('isa', val='no|yes',
392 fn=set_bool, default=0,
393 use="Simulate an ISA only system?")
395 gopts.var('boot', val="a|b|c|d",
396 fn=set_value, default='c',
397 use="Default boot device")
399 gopts.var('nographic', val='no|yes',
400 fn=set_bool, default=0,
401 use="Should device models use graphics?")
403 gopts.var('soundhw', val='audiodev',
404 fn=set_value, default='',
405 use="Should device models enable audio device?")
407 gopts.var('vnc', val='',
408 fn=set_value, default=None,
409 use="""Should the device model use VNC?""")
411 gopts.var('vncdisplay', val='',
412 fn=set_value, default=None,
413 use="""VNC display to use""")
415 gopts.var('sdl', val='',
416 fn=set_value, default=None,
417 use="""Should the device model use SDL?""")
419 gopts.var('display', val='DISPLAY',
420 fn=set_value, default=None,
421 use="X11 display to use")
423 gopts.var('xauthority', val='XAUTHORITY',
424 fn=set_value, default=None,
425 use="X11 Authority to use")
427 gopts.var('uuid', val='',
428 fn=set_value, default=None,
429 use="""xenstore UUID (universally unique identifier) to use. One
430 will be randomly generated if this option is not set, just like MAC
431 addresses for virtual network interfaces. This must be a unique
432 value across the entire cluster.""")
435 def err(msg):
436 """Print an error to stderr and exit.
437 """
438 print >>sys.stderr, "Error:", msg
439 sys.exit(1)
442 def warn(msg):
443 """Print a warning to stdout.
444 """
445 print >>sys.stderr, "Warning:", msg
448 def strip(pre, s):
449 """Strip prefix 'pre' if present.
450 """
451 if s.startswith(pre):
452 return s[len(pre):]
453 else:
454 return s
456 def configure_image(vals):
457 """Create the image config.
458 """
459 if not vals.builder:
460 return None
461 config_image = [ vals.builder ]
462 if vals.kernel:
463 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
464 if vals.ramdisk:
465 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
466 if vals.cmdline_ip:
467 cmdline_ip = strip('ip=', vals.cmdline_ip)
468 config_image.append(['ip', cmdline_ip])
469 if vals.root:
470 cmdline_root = strip('root=', vals.root)
471 config_image.append(['root', cmdline_root])
472 if vals.extra:
473 config_image.append(['args', vals.extra])
475 if vals.builder == 'hvm':
476 configure_hvm(config_image, vals)
478 return config_image
480 def configure_disks(config_devs, vals):
481 """Create the config for disks (virtual block devices).
482 """
483 for (uname, dev, mode, backend) in vals.disk:
485 if uname.startswith('tap:'):
486 cls = 'tap'
487 else:
488 cls = 'vbd'
490 config_vbd = [cls,
491 ['uname', uname],
492 ['dev', dev ],
493 ['mode', mode ] ]
494 if backend:
495 config_vbd.append(['backend', backend])
496 config_devs.append(['device', config_vbd])
498 def configure_pci(config_devs, vals):
499 """Create the config for pci devices.
500 """
501 config_pci = []
502 for (domain, bus, slot, func) in vals.pci:
503 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
504 ['slot', slot], ['func', func]])
506 if len(config_pci)>0:
507 config_pci.insert(0, 'pci')
508 config_devs.append(['device', config_pci])
510 def configure_ioports(config_devs, vals):
511 """Create the config for legacy i/o ranges.
512 """
513 for (io_from, io_to) in vals.ioports:
514 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
515 config_devs.append(['device', config_ioports])
517 def configure_irq(config_devs, vals):
518 """Create the config for irqs.
519 """
520 for irq in vals.irq:
521 config_irq = ['irq', ['irq', irq]]
522 config_devs.append(['device', config_irq])
524 def configure_usb(config_devs, vals):
525 for path in vals.usbport:
526 config_usb = ['usbport', ['path', path]]
527 config_devs.append(['device', config_usb])
530 def configure_security(config, vals):
531 """Create the config for ACM security labels.
532 """
533 access_control = vals.access_control
534 num = len(access_control)
535 if num == 1:
536 d = access_control[0]
537 policy = d.get('policy')
538 label = d.get('label')
539 if policy != security.active_policy:
540 err("Security policy (" + policy + ") incompatible with enforced policy ("
541 + security.active_policy + ")." )
542 config_access_control = ['access_control',
543 ['policy', policy],
544 ['label', label] ]
546 #ssidref cannot be specified together with access_control
547 if sxp.child_value(config, 'ssidref'):
548 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
549 #else calculate ssidre from label
550 ssidref = security.label2ssidref(label, policy, 'dom')
551 if not ssidref :
552 err("ERROR calculating ssidref from access_control.")
553 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
554 config.append(security_label)
555 elif num == 0:
556 if hasattr(vals, 'ssidref'):
557 if not security.on():
558 err("ERROR: Security ssidref specified but no policy active.")
559 ssidref = getattr(vals, 'ssidref')
560 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
561 config.append(security_label)
562 elif num > 1:
563 err("VM config error: Multiple access_control definitions!")
566 def configure_vtpm(config_devs, vals):
567 """Create the config for virtual TPM interfaces.
568 """
569 vtpm = vals.vtpm
570 vtpm_n = 1
571 for idx in range(0, vtpm_n):
572 if idx < len(vtpm):
573 d = vtpm[idx]
574 instance = d.get('instance')
575 if instance == "VTPMD":
576 instance = "0"
577 else:
578 if instance != None:
579 try:
580 if int(instance) == 0:
581 err('VM config error: vTPM instance must not be 0.')
582 except ValueError:
583 err('Vm config error: could not parse instance number.')
584 backend = d.get('backend')
585 config_vtpm = ['vtpm']
586 if instance:
587 config_vtpm.append(['pref_instance', instance])
588 if backend:
589 config_vtpm.append(['backend', backend])
590 config_devs.append(['device', config_vtpm])
593 def configure_vifs(config_devs, vals):
594 """Create the config for virtual network interfaces.
595 """
597 vifs = vals.vif
598 vifs_n = len(vifs)
600 if hasattr(vals, 'nics'):
601 if vals.nics > 0:
602 warn("The nics option is deprecated. Please use an empty vif "
603 "entry instead:\n\n vif = [ '' ]\n")
604 for _ in range(vifs_n, vals.nics):
605 vifs.append('')
606 vifs_n = len(vifs)
607 elif vals.nics == 0:
608 warn("The nics option is deprecated. Please remove it.")
610 for c in vifs:
611 d = comma_sep_kv_to_dict(c)
612 config_vif = ['vif']
614 def f(k):
615 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
616 'vifname', 'rate', 'model']:
617 err('Invalid vif option: ' + k)
619 config_vif.append([k, d[k]])
621 map(f, d.keys())
622 config_devs.append(['device', config_vif])
625 def configure_hvm(config_image, vals):
626 """Create the config for HVM devices.
627 """
628 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
629 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
630 'vnc', 'vncdisplay', 'vncconsole', 'sdl', 'display',
631 'acpi', 'apic', 'xauthority', 'usb', 'usbdevice' ]
632 for a in args:
633 if (vals.__dict__[a]):
634 config_image.append([a, vals.__dict__[a]])
636 def run_bootloader(vals, config_image):
637 if not os.access(vals.bootloader, os.X_OK):
638 err("Bootloader isn't executable")
639 if len(vals.disk) < 1:
640 err("No disks configured and boot loader requested")
641 (uname, dev, mode, backend) = vals.disk[0]
642 file = blkif.blkdev_uname_to_file(uname)
644 if vals.bootentry:
645 warn("The bootentry option is deprecated. Use bootargs and pass "
646 "--entry= directly.")
647 vals.bootargs = "--entry=%s" %(vals.bootentry,)
649 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
650 vals.bootargs, config_image)
652 def make_config(vals):
653 """Create the domain configuration.
654 """
656 config = ['vm']
658 def add_conf(n):
659 if hasattr(vals, n):
660 v = getattr(vals, n)
661 if v:
662 config.append([n, v])
664 map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
665 'on_reboot', 'on_crash', 'vcpus', 'features'])
667 if vals.uuid is not None:
668 config.append(['uuid', vals.uuid])
669 if vals.cpu is not None:
670 config.append(['cpu', vals.cpu])
671 if vals.cpus is not None:
672 config.append(['cpus', vals.cpus])
673 if vals.cpu_weight is not None:
674 config.append(['cpu_weight', vals.cpu_weight])
675 if vals.blkif:
676 config.append(['backend', ['blkif']])
677 if vals.netif:
678 config.append(['backend', ['netif']])
679 if vals.tpmif:
680 config.append(['backend', ['tpmif']])
681 if vals.localtime:
682 config.append(['localtime', vals.localtime])
684 config_image = configure_image(vals)
685 if vals.bootloader:
686 config_image = run_bootloader(vals, config_image)
687 config.append(['bootloader', vals.bootloader])
688 if vals.bootargs:
689 config.append(['bootloader_args'], vals.bootargs)
690 config.append(['image', config_image])
692 config_devs = []
693 configure_disks(config_devs, vals)
694 configure_pci(config_devs, vals)
695 configure_ioports(config_devs, vals)
696 configure_irq(config_devs, vals)
697 configure_vifs(config_devs, vals)
698 configure_usb(config_devs, vals)
699 configure_vtpm(config_devs, vals)
700 configure_security(config, vals)
701 config += config_devs
703 return config
705 def preprocess_disk(vals):
706 if not vals.disk: return
707 disk = []
708 for v in vals.disk:
709 d = v.split(',')
710 n = len(d)
711 if n == 3:
712 d.append(None)
713 elif n == 4:
714 pass
715 else:
716 err('Invalid disk specifier: ' + v)
717 disk.append(d)
718 vals.disk = disk
720 def preprocess_pci(vals):
721 if not vals.pci: return
722 pci = []
723 for pci_dev_str in vals.pci:
724 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
725 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
726 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
727 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
728 if pci_match!=None:
729 pci_dev_info = pci_match.groupdict('0')
730 try:
731 pci.append( ('0x'+pci_dev_info['domain'], \
732 '0x'+pci_dev_info['bus'], \
733 '0x'+pci_dev_info['slot'], \
734 '0x'+pci_dev_info['func']))
735 except IndexError:
736 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
737 vals.pci = pci
739 def preprocess_ioports(vals):
740 if not vals.ioports: return
741 ioports = []
742 for v in vals.ioports:
743 d = v.split('-')
744 if len(d) < 1 or len(d) > 2:
745 err('Invalid i/o port range specifier: ' + v)
746 if len(d) == 1:
747 d.append(d[0])
748 # Components are in hex: add hex specifier.
749 hexd = map(lambda v: '0x'+v, d)
750 ioports.append(hexd)
751 vals.ioports = ioports
753 def preprocess_vtpm(vals):
754 if not vals.vtpm: return
755 vtpms = []
756 for vtpm in vals.vtpm:
757 d = {}
758 a = vtpm.split(',')
759 for b in a:
760 (k, v) = b.strip().split('=', 1)
761 k = k.strip()
762 v = v.strip()
763 if k not in ['backend', 'instance']:
764 err('Invalid vtpm specifier: ' + vtpm)
765 d[k] = v
766 vtpms.append(d)
767 vals.vtpm = vtpms
769 def preprocess_access_control(vals):
770 if not vals.access_control:
771 return
772 access_controls = []
773 num = len(vals.access_control)
774 if num == 1:
775 access_control = (vals.access_control)[0]
776 d = {}
777 a = access_control.split(',')
778 if len(a) > 2:
779 err('Too many elements in access_control specifier: ' + access_control)
780 for b in a:
781 (k, v) = b.strip().split('=', 1)
782 k = k.strip()
783 v = v.strip()
784 if k not in ['policy','label']:
785 err('Invalid access_control specifier: ' + access_control)
786 d[k] = v
787 access_controls.append(d)
788 vals.access_control = access_controls
789 elif num > 1:
790 err('Multiple access_control definitions.')
792 def preprocess_ip(vals):
793 if vals.ip or vals.dhcp != 'off':
794 dummy_nfs_server = '1.2.3.4'
795 ip = (vals.ip
796 + ':' + (vals.nfs_server or dummy_nfs_server)
797 + ':' + vals.gateway
798 + ':' + vals.netmask
799 + ':' + vals.hostname
800 + ':' + vals.interface
801 + ':' + vals.dhcp)
802 else:
803 ip = ''
804 vals.cmdline_ip = ip
806 def preprocess_nfs(vals):
807 if not vals.nfs_root: return
808 if not vals.nfs_server:
809 err('Must set nfs root and nfs server')
810 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
811 vals.extra = nfs + ' ' + vals.extra
814 def get_host_addr():
815 host = socket.gethostname()
816 addr = socket.gethostbyname(host)
817 return addr
819 VNC_BASE_PORT = 5500
821 def choose_vnc_display():
822 """Try to choose a free vnc display.
823 """
824 def netstat_local_ports():
825 """Run netstat to get a list of the local ports in use.
826 """
827 l = os.popen("netstat -nat").readlines()
828 r = []
829 # Skip 2 lines of header.
830 for x in l[2:]:
831 # Local port is field 3.
832 y = x.split()[3]
833 # Field is addr:port, split off the port.
834 y = y.split(':')[-1]
835 r.append(int(y))
836 return r
838 ports = netstat_local_ports()
839 for d in range(1, 100):
840 port = VNC_BASE_PORT + d
841 if port in ports: continue
842 return d
843 return None
845 vncpid = None
847 def daemonize(prog, args):
848 """Runs a program as a daemon with the list of arguments. Returns the PID
849 of the daemonized program, or returns 0 on error.
850 """
851 r, w = os.pipe()
852 pid = os.fork()
854 if pid == 0:
855 os.close(r)
856 w = os.fdopen(w, 'w')
857 os.setsid()
858 try:
859 pid2 = os.fork()
860 except:
861 pid2 = None
862 if pid2 == 0:
863 os.chdir("/")
864 for fd in range(0, 256):
865 try:
866 os.close(fd)
867 except:
868 pass
869 os.open("/dev/null", os.O_RDWR)
870 os.dup2(0, 1)
871 os.dup2(0, 2)
872 os.execvp(prog, args)
873 os._exit(1)
874 else:
875 w.write(str(pid2 or 0))
876 w.close()
877 os._exit(0)
879 os.close(w)
880 r = os.fdopen(r)
881 daemon_pid = int(r.read())
882 r.close()
883 os.waitpid(pid, 0)
884 return daemon_pid
886 def spawn_vnc(display):
887 """Spawns a vncviewer that listens on the specified display. On success,
888 returns the port that the vncviewer is listening on and sets the global
889 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
890 """
891 vncargs = (["vncviewer", "-log", "*:stdout:0",
892 "-listen", "%d" % (VNC_BASE_PORT + display) ])
893 global vncpid
894 vncpid = daemonize("vncviewer", vncargs)
895 if vncpid == 0:
896 return 0
897 return VNC_BASE_PORT + display
899 def preprocess_vnc(vals):
900 """If vnc was specified, spawn a vncviewer in listen mode
901 and pass its address to the domain on the kernel command line.
902 """
903 if vals.dryrun: return
904 if vals.vncviewer:
905 vnc_display = choose_vnc_display()
906 if not vnc_display:
907 warn("No free vnc display")
908 return
909 print 'VNC=', vnc_display
910 vnc_port = spawn_vnc(vnc_display)
911 if vnc_port > 0:
912 vnc_host = get_host_addr()
913 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
914 vals.extra = vnc + ' ' + vals.extra
916 def preprocess(vals):
917 if not vals.kernel and not vals.bootloader:
918 err("No kernel specified")
919 preprocess_disk(vals)
920 preprocess_pci(vals)
921 preprocess_ioports(vals)
922 preprocess_ip(vals)
923 preprocess_nfs(vals)
924 preprocess_vnc(vals)
925 preprocess_vtpm(vals)
926 preprocess_access_control(vals)
929 def comma_sep_kv_to_dict(c):
930 """Convert comma-separated, equals-separated key-value pairs into a
931 dictionary.
932 """
933 d = {}
934 c = c.strip()
935 if len(c) > 0:
936 a = c.split(',')
937 for b in a:
938 if b.find('=') == -1:
939 err("%s should be a pair, separated by an equals sign." % b)
940 (k, v) = b.split('=', 1)
941 k = k.strip()
942 v = v.strip()
943 d[k] = v
944 return d
947 def make_domain(opts, config):
948 """Create, build and start a domain.
950 @param opts: options
951 @param config: configuration
952 @return: domain id
953 @rtype: int
954 """
956 try:
957 dominfo = server.xend.domain.create(config)
958 except xmlrpclib.Fault, ex:
959 import signal
960 if vncpid:
961 os.kill(vncpid, signal.SIGKILL)
962 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
963 err("the domain '%s' does not exist." % ex.faultString)
964 else:
965 err("%s" % ex.faultString)
966 except Exception, ex:
967 # main.py has good error messages that let the user know what failed.
968 # unless the error is a create.py specific thing, it should be handled
969 # at main. The purpose of this general-case 'Exception' handler is to
970 # clean up create.py specific processes/data but since create.py does
971 # not know what to do with the error, it should pass it up.
972 import signal
973 if vncpid:
974 os.kill(vncpid, signal.SIGKILL)
975 raise ex
977 dom = sxp.child_value(dominfo, 'name')
979 try:
980 server.xend.domain.waitForDevices(dom)
981 except xmlrpclib.Fault, ex:
982 server.xend.domain.destroy(dom)
983 err("%s" % ex.faultString)
984 except:
985 server.xend.domain.destroy(dom)
986 err("Device creation failed for domain %s" % dom)
988 if not opts.vals.paused:
989 try:
990 server.xend.domain.unpause(dom)
991 except:
992 server.xend.domain.destroy(dom)
993 err("Failed to unpause domain %s" % dom)
994 opts.info("Started domain %s" % (dom))
995 return int(sxp.child_value(dominfo, 'domid'))
998 def get_xauthority():
999 xauth = os.getenv("XAUTHORITY")
1000 if not xauth:
1001 home = os.getenv("HOME")
1002 if not home:
1003 import posix, pwd
1004 home = pwd.getpwuid(posix.getuid())[5]
1005 xauth = home + "/.Xauthority"
1006 return xauth
1009 def parseCommandLine(argv):
1010 gopts.reset()
1011 args = gopts.parse(argv)
1012 if gopts.vals.help:
1013 gopts.usage()
1014 if gopts.vals.help or gopts.vals.help_config:
1015 gopts.load_defconfig(help=1)
1016 if gopts.vals.help or gopts.vals.help_config:
1017 return (None, None)
1019 if not gopts.vals.display:
1020 gopts.vals.display = os.getenv("DISPLAY")
1022 if not gopts.vals.xauthority:
1023 gopts.vals.xauthority = get_xauthority()
1025 # Process remaining args as config variables.
1026 for arg in args:
1027 if '=' in arg:
1028 (var, val) = arg.strip().split('=', 1)
1029 gopts.setvar(var.strip(), val.strip())
1030 if gopts.vals.config:
1031 config = gopts.vals.config
1032 else:
1033 gopts.load_defconfig()
1034 preprocess(gopts.vals)
1035 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1036 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1037 config = make_config(gopts.vals)
1039 return (gopts, config)
1042 def check_domain_label(config, verbose):
1043 """All that we need to check here is that the domain label exists and
1044 is not null when security is on. Other error conditions are
1045 handled when the config file is parsed.
1046 """
1047 answer = 0
1048 default_label = None
1049 secon = 0
1050 if security.on():
1051 default_label = security.ssidref2label(security.NULL_SSIDREF)
1052 secon = 1
1054 # get the domain acm_label
1055 dom_label = None
1056 dom_name = None
1057 for x in sxp.children(config):
1058 if sxp.name(x) == 'security':
1059 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1060 if sxp.name(x) == 'name':
1061 dom_name = sxp.child0(x)
1063 # sanity check on domain label
1064 if verbose:
1065 print "Checking domain:"
1066 if (not secon) and (not dom_label):
1067 answer = 1
1068 if verbose:
1069 print " %s: PERMITTED" % (dom_name)
1070 elif (secon) and (dom_label) and (dom_label != default_label):
1071 answer = 1
1072 if verbose:
1073 print " %s: PERMITTED" % (dom_name)
1074 else:
1075 print " %s: DENIED" % (dom_name)
1076 if not secon:
1077 print " --> Security off, but domain labeled"
1078 else:
1079 print " --> Domain not labeled"
1080 answer = 0
1082 return answer
1085 def config_security_check(config, verbose):
1086 """Checks each resource listed in the config to see if the active
1087 policy will permit creation of a new domain using the config.
1088 Returns 1 if the config passes all tests, otherwise 0.
1089 """
1090 answer = 1
1092 # get the domain acm_label
1093 domain_label = None
1094 domain_policy = None
1095 for x in sxp.children(config):
1096 if sxp.name(x) == 'security':
1097 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1098 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1100 # if no domain label, use default
1101 if not domain_label and security.on():
1102 try:
1103 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1104 except:
1105 traceback.print_exc(limit=1)
1106 return 0
1107 domain_policy = 'NULL'
1108 elif not domain_label:
1109 domain_label = ""
1110 domain_policy = 'NULL'
1112 if verbose:
1113 print "Checking resources:"
1115 # build a list of all resources in the config file
1116 resources = []
1117 for x in sxp.children(config):
1118 if sxp.name(x) == 'device':
1119 if sxp.name(sxp.child0(x)) == 'vbd':
1120 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1122 # perform a security check on each resource
1123 for resource in resources:
1124 try:
1125 security.res_security_check(resource, domain_label)
1126 if verbose:
1127 print " %s: PERMITTED" % (resource)
1129 except security.ACMError:
1130 print " %s: DENIED" % (resource)
1131 (res_label, res_policy) = security.get_res_label(resource)
1132 print " --> res:"+res_label+" ("+res_policy+")"
1133 print " --> dom:"+domain_label+" ("+domain_policy+")"
1134 answer = 0
1136 return answer
1139 def create_security_check(config):
1140 passed = 0
1141 try:
1142 if check_domain_label(config, verbose=0):
1143 if config_security_check(config, verbose=0):
1144 passed = 1
1145 else:
1146 print "Checking resources: (skipped)"
1147 except security.ACMError:
1148 traceback.print_exc(limit=1)
1150 return passed
1153 def main(argv):
1154 try:
1155 (opts, config) = parseCommandLine(argv)
1156 except StandardError, ex:
1157 err(str(ex))
1159 if not opts:
1160 return
1162 if opts.vals.dryrun:
1163 PrettyPrint.prettyprint(config)
1164 else:
1165 if not create_security_check(config):
1166 print "Security configuration prevents domain from starting"
1167 else:
1168 dom = make_domain(opts, config)
1169 if opts.vals.console_autoconnect:
1170 console.execConsole(dom)
1172 if __name__ == '__main__':
1173 main(sys.argv)