ia64/xen-unstable

view tools/python/xen/xm/create.py @ 11436:4fc1688d9a16

Fix xm create -F. This can't be very widely used, because it looks like it's
been broken for ages!

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