ia64/xen-unstable

view tools/python/xen/xm/create.py @ 11029:bd04004865ba

[qemu] Add vncunused option.
If the port used for the requested display number is in use, try
additional ports until a free port is found.

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Wed Aug 09 15:03:38 2006 +0100 (2006-08-09)
parents 58a04bfedf6b
children 0f917d63e960
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('vncunused', val='',
416 fn=set_bool, default=1,
417 use="""Try to find an unused port for the VNC server.
418 Only valid when vnc=1.""")
420 gopts.var('sdl', val='',
421 fn=set_value, default=None,
422 use="""Should the device model use SDL?""")
424 gopts.var('display', val='DISPLAY',
425 fn=set_value, default=None,
426 use="X11 display to use")
428 gopts.var('xauthority', val='XAUTHORITY',
429 fn=set_value, default=None,
430 use="X11 Authority to use")
432 gopts.var('uuid', val='',
433 fn=set_value, default=None,
434 use="""xenstore UUID (universally unique identifier) to use. One
435 will be randomly generated if this option is not set, just like MAC
436 addresses for virtual network interfaces. This must be a unique
437 value across the entire cluster.""")
440 def err(msg):
441 """Print an error to stderr and exit.
442 """
443 print >>sys.stderr, "Error:", msg
444 sys.exit(1)
447 def warn(msg):
448 """Print a warning to stdout.
449 """
450 print >>sys.stderr, "Warning:", msg
453 def strip(pre, s):
454 """Strip prefix 'pre' if present.
455 """
456 if s.startswith(pre):
457 return s[len(pre):]
458 else:
459 return s
461 def configure_image(vals):
462 """Create the image config.
463 """
464 if not vals.builder:
465 return None
466 config_image = [ vals.builder ]
467 if vals.kernel:
468 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
469 if vals.ramdisk:
470 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
471 if vals.cmdline_ip:
472 cmdline_ip = strip('ip=', vals.cmdline_ip)
473 config_image.append(['ip', cmdline_ip])
474 if vals.root:
475 cmdline_root = strip('root=', vals.root)
476 config_image.append(['root', cmdline_root])
477 if vals.extra:
478 config_image.append(['args', vals.extra])
480 if vals.builder == 'hvm':
481 configure_hvm(config_image, vals)
483 return config_image
485 def configure_disks(config_devs, vals):
486 """Create the config for disks (virtual block devices).
487 """
488 for (uname, dev, mode, backend) in vals.disk:
490 if uname.startswith('tap:'):
491 cls = 'tap'
492 else:
493 cls = 'vbd'
495 config_vbd = [cls,
496 ['uname', uname],
497 ['dev', dev ],
498 ['mode', mode ] ]
499 if backend:
500 config_vbd.append(['backend', backend])
501 config_devs.append(['device', config_vbd])
503 def configure_pci(config_devs, vals):
504 """Create the config for pci devices.
505 """
506 config_pci = []
507 for (domain, bus, slot, func) in vals.pci:
508 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
509 ['slot', slot], ['func', func]])
511 if len(config_pci)>0:
512 config_pci.insert(0, 'pci')
513 config_devs.append(['device', config_pci])
515 def configure_ioports(config_devs, vals):
516 """Create the config for legacy i/o ranges.
517 """
518 for (io_from, io_to) in vals.ioports:
519 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
520 config_devs.append(['device', config_ioports])
522 def configure_irq(config_devs, vals):
523 """Create the config for irqs.
524 """
525 for irq in vals.irq:
526 config_irq = ['irq', ['irq', irq]]
527 config_devs.append(['device', config_irq])
529 def configure_usb(config_devs, vals):
530 for path in vals.usbport:
531 config_usb = ['usbport', ['path', path]]
532 config_devs.append(['device', config_usb])
535 def configure_security(config, vals):
536 """Create the config for ACM security labels.
537 """
538 access_control = vals.access_control
539 num = len(access_control)
540 if num == 1:
541 d = access_control[0]
542 policy = d.get('policy')
543 label = d.get('label')
544 if policy != security.active_policy:
545 err("Security policy (" + policy + ") incompatible with enforced policy ("
546 + security.active_policy + ")." )
547 config_access_control = ['access_control',
548 ['policy', policy],
549 ['label', label] ]
551 #ssidref cannot be specified together with access_control
552 if sxp.child_value(config, 'ssidref'):
553 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
554 #else calculate ssidre from label
555 ssidref = security.label2ssidref(label, policy, 'dom')
556 if not ssidref :
557 err("ERROR calculating ssidref from access_control.")
558 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
559 config.append(security_label)
560 elif num == 0:
561 if hasattr(vals, 'ssidref'):
562 if not security.on():
563 err("ERROR: Security ssidref specified but no policy active.")
564 ssidref = getattr(vals, 'ssidref')
565 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
566 config.append(security_label)
567 elif num > 1:
568 err("VM config error: Multiple access_control definitions!")
571 def configure_vtpm(config_devs, vals):
572 """Create the config for virtual TPM interfaces.
573 """
574 vtpm = vals.vtpm
575 vtpm_n = 1
576 for idx in range(0, vtpm_n):
577 if idx < len(vtpm):
578 d = vtpm[idx]
579 instance = d.get('instance')
580 if instance == "VTPMD":
581 instance = "0"
582 else:
583 if instance != None:
584 try:
585 if int(instance) == 0:
586 err('VM config error: vTPM instance must not be 0.')
587 except ValueError:
588 err('Vm config error: could not parse instance number.')
589 backend = d.get('backend')
590 config_vtpm = ['vtpm']
591 if instance:
592 config_vtpm.append(['pref_instance', instance])
593 if backend:
594 config_vtpm.append(['backend', backend])
595 config_devs.append(['device', config_vtpm])
598 def configure_vifs(config_devs, vals):
599 """Create the config for virtual network interfaces.
600 """
602 vifs = vals.vif
603 vifs_n = len(vifs)
605 if hasattr(vals, 'nics'):
606 if vals.nics > 0:
607 warn("The nics option is deprecated. Please use an empty vif "
608 "entry instead:\n\n vif = [ '' ]\n")
609 for _ in range(vifs_n, vals.nics):
610 vifs.append('')
611 vifs_n = len(vifs)
612 elif vals.nics == 0:
613 warn("The nics option is deprecated. Please remove it.")
615 for c in vifs:
616 d = comma_sep_kv_to_dict(c)
617 config_vif = ['vif']
619 def f(k):
620 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
621 'vifname', 'rate', 'model']:
622 err('Invalid vif option: ' + k)
624 config_vif.append([k, d[k]])
626 map(f, d.keys())
627 config_devs.append(['device', config_vif])
630 def configure_hvm(config_image, vals):
631 """Create the config for HVM devices.
632 """
633 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
634 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
635 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'sdl', 'display',
636 'acpi', 'apic', 'xauthority', 'usb', 'usbdevice' ]
637 for a in args:
638 if (vals.__dict__[a]):
639 config_image.append([a, vals.__dict__[a]])
641 def run_bootloader(vals, config_image):
642 if not os.access(vals.bootloader, os.X_OK):
643 err("Bootloader isn't executable")
644 if len(vals.disk) < 1:
645 err("No disks configured and boot loader requested")
646 (uname, dev, mode, backend) = vals.disk[0]
647 file = blkif.blkdev_uname_to_file(uname)
649 if vals.bootentry:
650 warn("The bootentry option is deprecated. Use bootargs and pass "
651 "--entry= directly.")
652 vals.bootargs = "--entry=%s" %(vals.bootentry,)
654 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
655 vals.bootargs, config_image)
657 def make_config(vals):
658 """Create the domain configuration.
659 """
661 config = ['vm']
663 def add_conf(n):
664 if hasattr(vals, n):
665 v = getattr(vals, n)
666 if v:
667 config.append([n, v])
669 map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
670 'on_reboot', 'on_crash', 'vcpus', 'features'])
672 if vals.uuid is not None:
673 config.append(['uuid', vals.uuid])
674 if vals.cpu is not None:
675 config.append(['cpu', vals.cpu])
676 if vals.cpus is not None:
677 config.append(['cpus', vals.cpus])
678 if vals.cpu_weight is not None:
679 config.append(['cpu_weight', vals.cpu_weight])
680 if vals.blkif:
681 config.append(['backend', ['blkif']])
682 if vals.netif:
683 config.append(['backend', ['netif']])
684 if vals.tpmif:
685 config.append(['backend', ['tpmif']])
686 if vals.localtime:
687 config.append(['localtime', vals.localtime])
689 config_image = configure_image(vals)
690 if vals.bootloader:
691 config_image = run_bootloader(vals, config_image)
692 config.append(['bootloader', vals.bootloader])
693 if vals.bootargs:
694 config.append(['bootloader_args'], vals.bootargs)
695 config.append(['image', config_image])
697 config_devs = []
698 configure_disks(config_devs, vals)
699 configure_pci(config_devs, vals)
700 configure_ioports(config_devs, vals)
701 configure_irq(config_devs, vals)
702 configure_vifs(config_devs, vals)
703 configure_usb(config_devs, vals)
704 configure_vtpm(config_devs, vals)
705 configure_security(config, vals)
706 config += config_devs
708 return config
710 def preprocess_disk(vals):
711 if not vals.disk: return
712 disk = []
713 for v in vals.disk:
714 d = v.split(',')
715 n = len(d)
716 if n == 3:
717 d.append(None)
718 elif n == 4:
719 pass
720 else:
721 err('Invalid disk specifier: ' + v)
722 disk.append(d)
723 vals.disk = disk
725 def preprocess_pci(vals):
726 if not vals.pci: return
727 pci = []
728 for pci_dev_str in vals.pci:
729 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
730 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
731 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
732 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
733 if pci_match!=None:
734 pci_dev_info = pci_match.groupdict('0')
735 try:
736 pci.append( ('0x'+pci_dev_info['domain'], \
737 '0x'+pci_dev_info['bus'], \
738 '0x'+pci_dev_info['slot'], \
739 '0x'+pci_dev_info['func']))
740 except IndexError:
741 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
742 vals.pci = pci
744 def preprocess_ioports(vals):
745 if not vals.ioports: return
746 ioports = []
747 for v in vals.ioports:
748 d = v.split('-')
749 if len(d) < 1 or len(d) > 2:
750 err('Invalid i/o port range specifier: ' + v)
751 if len(d) == 1:
752 d.append(d[0])
753 # Components are in hex: add hex specifier.
754 hexd = map(lambda v: '0x'+v, d)
755 ioports.append(hexd)
756 vals.ioports = ioports
758 def preprocess_vtpm(vals):
759 if not vals.vtpm: return
760 vtpms = []
761 for vtpm in vals.vtpm:
762 d = {}
763 a = vtpm.split(',')
764 for b in a:
765 (k, v) = b.strip().split('=', 1)
766 k = k.strip()
767 v = v.strip()
768 if k not in ['backend', 'instance']:
769 err('Invalid vtpm specifier: ' + vtpm)
770 d[k] = v
771 vtpms.append(d)
772 vals.vtpm = vtpms
774 def preprocess_access_control(vals):
775 if not vals.access_control:
776 return
777 access_controls = []
778 num = len(vals.access_control)
779 if num == 1:
780 access_control = (vals.access_control)[0]
781 d = {}
782 a = access_control.split(',')
783 if len(a) > 2:
784 err('Too many elements in access_control specifier: ' + access_control)
785 for b in a:
786 (k, v) = b.strip().split('=', 1)
787 k = k.strip()
788 v = v.strip()
789 if k not in ['policy','label']:
790 err('Invalid access_control specifier: ' + access_control)
791 d[k] = v
792 access_controls.append(d)
793 vals.access_control = access_controls
794 elif num > 1:
795 err('Multiple access_control definitions.')
797 def preprocess_ip(vals):
798 if vals.ip or vals.dhcp != 'off':
799 dummy_nfs_server = '1.2.3.4'
800 ip = (vals.ip
801 + ':' + (vals.nfs_server or dummy_nfs_server)
802 + ':' + vals.gateway
803 + ':' + vals.netmask
804 + ':' + vals.hostname
805 + ':' + vals.interface
806 + ':' + vals.dhcp)
807 else:
808 ip = ''
809 vals.cmdline_ip = ip
811 def preprocess_nfs(vals):
812 if not vals.nfs_root: return
813 if not vals.nfs_server:
814 err('Must set nfs root and nfs server')
815 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
816 vals.extra = nfs + ' ' + vals.extra
819 def get_host_addr():
820 host = socket.gethostname()
821 addr = socket.gethostbyname(host)
822 return addr
824 VNC_BASE_PORT = 5500
826 def choose_vnc_display():
827 """Try to choose a free vnc display.
828 """
829 def netstat_local_ports():
830 """Run netstat to get a list of the local ports in use.
831 """
832 l = os.popen("netstat -nat").readlines()
833 r = []
834 # Skip 2 lines of header.
835 for x in l[2:]:
836 # Local port is field 3.
837 y = x.split()[3]
838 # Field is addr:port, split off the port.
839 y = y.split(':')[-1]
840 r.append(int(y))
841 return r
843 ports = netstat_local_ports()
844 for d in range(1, 100):
845 port = VNC_BASE_PORT + d
846 if port in ports: continue
847 return d
848 return None
850 vncpid = None
852 def daemonize(prog, args):
853 """Runs a program as a daemon with the list of arguments. Returns the PID
854 of the daemonized program, or returns 0 on error.
855 """
856 r, w = os.pipe()
857 pid = os.fork()
859 if pid == 0:
860 os.close(r)
861 w = os.fdopen(w, 'w')
862 os.setsid()
863 try:
864 pid2 = os.fork()
865 except:
866 pid2 = None
867 if pid2 == 0:
868 os.chdir("/")
869 for fd in range(0, 256):
870 try:
871 os.close(fd)
872 except:
873 pass
874 os.open("/dev/null", os.O_RDWR)
875 os.dup2(0, 1)
876 os.dup2(0, 2)
877 os.execvp(prog, args)
878 os._exit(1)
879 else:
880 w.write(str(pid2 or 0))
881 w.close()
882 os._exit(0)
884 os.close(w)
885 r = os.fdopen(r)
886 daemon_pid = int(r.read())
887 r.close()
888 os.waitpid(pid, 0)
889 return daemon_pid
891 def spawn_vnc(display):
892 """Spawns a vncviewer that listens on the specified display. On success,
893 returns the port that the vncviewer is listening on and sets the global
894 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
895 """
896 vncargs = (["vncviewer", "-log", "*:stdout:0",
897 "-listen", "%d" % (VNC_BASE_PORT + display) ])
898 global vncpid
899 vncpid = daemonize("vncviewer", vncargs)
900 if vncpid == 0:
901 return 0
902 return VNC_BASE_PORT + display
904 def preprocess_vnc(vals):
905 """If vnc was specified, spawn a vncviewer in listen mode
906 and pass its address to the domain on the kernel command line.
907 """
908 if vals.dryrun: return
909 if vals.vncviewer:
910 vnc_display = choose_vnc_display()
911 if not vnc_display:
912 warn("No free vnc display")
913 return
914 print 'VNC=', vnc_display
915 vnc_port = spawn_vnc(vnc_display)
916 if vnc_port > 0:
917 vnc_host = get_host_addr()
918 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
919 vals.extra = vnc + ' ' + vals.extra
921 def preprocess(vals):
922 if not vals.kernel and not vals.bootloader:
923 err("No kernel specified")
924 preprocess_disk(vals)
925 preprocess_pci(vals)
926 preprocess_ioports(vals)
927 preprocess_ip(vals)
928 preprocess_nfs(vals)
929 preprocess_vnc(vals)
930 preprocess_vtpm(vals)
931 preprocess_access_control(vals)
934 def comma_sep_kv_to_dict(c):
935 """Convert comma-separated, equals-separated key-value pairs into a
936 dictionary.
937 """
938 d = {}
939 c = c.strip()
940 if len(c) > 0:
941 a = c.split(',')
942 for b in a:
943 if b.find('=') == -1:
944 err("%s should be a pair, separated by an equals sign." % b)
945 (k, v) = b.split('=', 1)
946 k = k.strip()
947 v = v.strip()
948 d[k] = v
949 return d
952 def make_domain(opts, config):
953 """Create, build and start a domain.
955 @param opts: options
956 @param config: configuration
957 @return: domain id
958 @rtype: int
959 """
961 try:
962 dominfo = server.xend.domain.create(config)
963 except xmlrpclib.Fault, ex:
964 import signal
965 if vncpid:
966 os.kill(vncpid, signal.SIGKILL)
967 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
968 err("the domain '%s' does not exist." % ex.faultString)
969 else:
970 err("%s" % ex.faultString)
971 except Exception, ex:
972 # main.py has good error messages that let the user know what failed.
973 # unless the error is a create.py specific thing, it should be handled
974 # at main. The purpose of this general-case 'Exception' handler is to
975 # clean up create.py specific processes/data but since create.py does
976 # not know what to do with the error, it should pass it up.
977 import signal
978 if vncpid:
979 os.kill(vncpid, signal.SIGKILL)
980 raise
982 dom = sxp.child_value(dominfo, 'name')
984 try:
985 server.xend.domain.waitForDevices(dom)
986 except xmlrpclib.Fault, ex:
987 server.xend.domain.destroy(dom)
988 err("%s" % ex.faultString)
989 except:
990 server.xend.domain.destroy(dom)
991 err("Device creation failed for domain %s" % dom)
993 if not opts.vals.paused:
994 try:
995 server.xend.domain.unpause(dom)
996 except:
997 server.xend.domain.destroy(dom)
998 err("Failed to unpause domain %s" % dom)
999 opts.info("Started domain %s" % (dom))
1000 return int(sxp.child_value(dominfo, 'domid'))
1003 def get_xauthority():
1004 xauth = os.getenv("XAUTHORITY")
1005 if not xauth:
1006 home = os.getenv("HOME")
1007 if not home:
1008 import posix, pwd
1009 home = pwd.getpwuid(posix.getuid())[5]
1010 xauth = home + "/.Xauthority"
1011 return xauth
1014 def parseCommandLine(argv):
1015 gopts.reset()
1016 args = gopts.parse(argv)
1017 if gopts.vals.help:
1018 gopts.usage()
1019 if gopts.vals.help or gopts.vals.help_config:
1020 gopts.load_defconfig(help=1)
1021 if gopts.vals.help or gopts.vals.help_config:
1022 return (None, None)
1024 if not gopts.vals.display:
1025 gopts.vals.display = os.getenv("DISPLAY")
1027 if not gopts.vals.xauthority:
1028 gopts.vals.xauthority = get_xauthority()
1030 # Process remaining args as config variables.
1031 for arg in args:
1032 if '=' in arg:
1033 (var, val) = arg.strip().split('=', 1)
1034 gopts.setvar(var.strip(), val.strip())
1035 if gopts.vals.config:
1036 config = gopts.vals.config
1037 else:
1038 gopts.load_defconfig()
1039 preprocess(gopts.vals)
1040 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1041 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1042 config = make_config(gopts.vals)
1044 return (gopts, config)
1047 def check_domain_label(config, verbose):
1048 """All that we need to check here is that the domain label exists and
1049 is not null when security is on. Other error conditions are
1050 handled when the config file is parsed.
1051 """
1052 answer = 0
1053 default_label = None
1054 secon = 0
1055 if security.on():
1056 default_label = security.ssidref2label(security.NULL_SSIDREF)
1057 secon = 1
1059 # get the domain acm_label
1060 dom_label = None
1061 dom_name = None
1062 for x in sxp.children(config):
1063 if sxp.name(x) == 'security':
1064 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1065 if sxp.name(x) == 'name':
1066 dom_name = sxp.child0(x)
1068 # sanity check on domain label
1069 if verbose:
1070 print "Checking domain:"
1071 if (not secon) and (not dom_label):
1072 answer = 1
1073 if verbose:
1074 print " %s: PERMITTED" % (dom_name)
1075 elif (secon) and (dom_label) and (dom_label != default_label):
1076 answer = 1
1077 if verbose:
1078 print " %s: PERMITTED" % (dom_name)
1079 else:
1080 print " %s: DENIED" % (dom_name)
1081 if not secon:
1082 print " --> Security off, but domain labeled"
1083 else:
1084 print " --> Domain not labeled"
1085 answer = 0
1087 return answer
1090 def config_security_check(config, verbose):
1091 """Checks each resource listed in the config to see if the active
1092 policy will permit creation of a new domain using the config.
1093 Returns 1 if the config passes all tests, otherwise 0.
1094 """
1095 answer = 1
1097 # get the domain acm_label
1098 domain_label = None
1099 domain_policy = None
1100 for x in sxp.children(config):
1101 if sxp.name(x) == 'security':
1102 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1103 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1105 # if no domain label, use default
1106 if not domain_label and security.on():
1107 try:
1108 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1109 except:
1110 traceback.print_exc(limit=1)
1111 return 0
1112 domain_policy = 'NULL'
1113 elif not domain_label:
1114 domain_label = ""
1115 domain_policy = 'NULL'
1117 if verbose:
1118 print "Checking resources:"
1120 # build a list of all resources in the config file
1121 resources = []
1122 for x in sxp.children(config):
1123 if sxp.name(x) == 'device':
1124 if sxp.name(sxp.child0(x)) == 'vbd':
1125 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1127 # perform a security check on each resource
1128 for resource in resources:
1129 try:
1130 security.res_security_check(resource, domain_label)
1131 if verbose:
1132 print " %s: PERMITTED" % (resource)
1134 except security.ACMError:
1135 print " %s: DENIED" % (resource)
1136 (res_label, res_policy) = security.get_res_label(resource)
1137 print " --> res:"+res_label+" ("+res_policy+")"
1138 print " --> dom:"+domain_label+" ("+domain_policy+")"
1139 answer = 0
1141 return answer
1144 def create_security_check(config):
1145 passed = 0
1146 try:
1147 if check_domain_label(config, verbose=0):
1148 if config_security_check(config, verbose=0):
1149 passed = 1
1150 else:
1151 print "Checking resources: (skipped)"
1152 except security.ACMError:
1153 traceback.print_exc(limit=1)
1155 return passed
1158 def main(argv):
1159 try:
1160 (opts, config) = parseCommandLine(argv)
1161 except StandardError, ex:
1162 err(str(ex))
1164 if not opts:
1165 return
1167 if opts.vals.dryrun:
1168 PrettyPrint.prettyprint(config)
1169 else:
1170 if not create_security_check(config):
1171 print "Security configuration prevents domain from starting"
1172 else:
1173 dom = make_domain(opts, config)
1174 if opts.vals.console_autoconnect:
1175 console.execConsole(dom)
1177 if __name__ == '__main__':
1178 main(sys.argv)