debuggers.hg

view tools/python/xen/xm/create.py @ 10959:4c2fab8f8c34

[qemu] Use xenstore to configure ioemu block devices.
- read ioemu block device config from xenstore
- don't require the ioemu: prefix on block devices any longer
- allow change of media associated with cdrom drives
- replace cdrom= option by :cdrom suffix on regular block device config:
'file:/root/mytest.iso,hdc:cdrom,r'
- don't create default cdrom drive anymore - to create default empty
cdrom drive use: ',hdc:cdrom,r'

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Thu Aug 03 18:28:29 2006 +0100 (2006-08-03)
parents ecb8ff1fcf1f
children 92ef1215a6f1
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('audio', val='no|yes',
404 fn=set_bool, default=0,
405 use="Should device models enable audio?")
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', 'audio',
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 spawn_vnc(display):
848 vncargs = (["vncviewer", "-log", "*:stdout:0",
849 "-listen", "%d" % (VNC_BASE_PORT + display) ])
850 global vncpid
851 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
853 return VNC_BASE_PORT + display
855 def preprocess_vnc(vals):
856 """If vnc was specified, spawn a vncviewer in listen mode
857 and pass its address to the domain on the kernel command line.
858 """
859 if vals.dryrun: return
860 if vals.vncviewer:
861 vnc_display = choose_vnc_display()
862 if not vnc_display:
863 warn("No free vnc display")
864 return
865 print 'VNC=', vnc_display
866 vnc_port = spawn_vnc(vnc_display)
867 if vnc_port > 0:
868 vnc_host = get_host_addr()
869 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
870 vals.extra = vnc + ' ' + vals.extra
872 def preprocess(vals):
873 if not vals.kernel and not vals.bootloader:
874 err("No kernel specified")
875 preprocess_disk(vals)
876 preprocess_pci(vals)
877 preprocess_ioports(vals)
878 preprocess_ip(vals)
879 preprocess_nfs(vals)
880 preprocess_vnc(vals)
881 preprocess_vtpm(vals)
882 preprocess_access_control(vals)
885 def comma_sep_kv_to_dict(c):
886 """Convert comma-separated, equals-separated key-value pairs into a
887 dictionary.
888 """
889 d = {}
890 c = c.strip()
891 if len(c) > 0:
892 a = c.split(',')
893 for b in a:
894 if b.find('=') == -1:
895 err("%s should be a pair, separated by an equals sign." % b)
896 (k, v) = b.split('=', 1)
897 k = k.strip()
898 v = v.strip()
899 d[k] = v
900 return d
903 def make_domain(opts, config):
904 """Create, build and start a domain.
906 @param opts: options
907 @param config: configuration
908 @return: domain id
909 @rtype: int
910 """
912 try:
913 dominfo = server.xend.domain.create(config)
914 except xmlrpclib.Fault, ex:
915 import signal
916 if vncpid:
917 os.kill(vncpid, signal.SIGKILL)
918 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
919 err("the domain '%s' does not exist." % ex.faultString)
920 else:
921 err("%s" % ex.faultString)
922 except Exception, ex:
923 # main.py has good error messages that let the user know what failed.
924 # unless the error is a create.py specific thing, it should be handled
925 # at main. The purpose of this general-case 'Exception' handler is to
926 # clean up create.py specific processes/data but since create.py does
927 # not know what to do with the error, it should pass it up.
928 import signal
929 if vncpid:
930 os.kill(vncpid, signal.SIGKILL)
931 raise ex
933 dom = sxp.child_value(dominfo, 'name')
935 try:
936 server.xend.domain.waitForDevices(dom)
937 except xmlrpclib.Fault, ex:
938 server.xend.domain.destroy(dom)
939 err("%s" % ex.faultString)
940 except:
941 server.xend.domain.destroy(dom)
942 err("Device creation failed for domain %s" % dom)
944 if not opts.vals.paused:
945 try:
946 server.xend.domain.unpause(dom)
947 except:
948 server.xend.domain.destroy(dom)
949 err("Failed to unpause domain %s" % dom)
950 opts.info("Started domain %s" % (dom))
951 return int(sxp.child_value(dominfo, 'domid'))
954 def get_xauthority():
955 xauth = os.getenv("XAUTHORITY")
956 if not xauth:
957 home = os.getenv("HOME")
958 if not home:
959 import posix, pwd
960 home = pwd.getpwuid(posix.getuid())[5]
961 xauth = home + "/.Xauthority"
962 return xauth
965 def parseCommandLine(argv):
966 gopts.reset()
967 args = gopts.parse(argv)
968 if gopts.vals.help:
969 gopts.usage()
970 if gopts.vals.help or gopts.vals.help_config:
971 gopts.load_defconfig(help=1)
972 if gopts.vals.help or gopts.vals.help_config:
973 return (None, None)
975 if not gopts.vals.display:
976 gopts.vals.display = os.getenv("DISPLAY")
978 if not gopts.vals.xauthority:
979 gopts.vals.xauthority = get_xauthority()
981 # Process remaining args as config variables.
982 for arg in args:
983 if '=' in arg:
984 (var, val) = arg.strip().split('=', 1)
985 gopts.setvar(var.strip(), val.strip())
986 if gopts.vals.config:
987 config = gopts.vals.config
988 else:
989 gopts.load_defconfig()
990 preprocess(gopts.vals)
991 if not gopts.getopt('name') and gopts.getopt('defconfig'):
992 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
993 config = make_config(gopts.vals)
995 return (gopts, config)
998 def check_domain_label(config, verbose):
999 """All that we need to check here is that the domain label exists and
1000 is not null when security is on. Other error conditions are
1001 handled when the config file is parsed.
1002 """
1003 answer = 0
1004 default_label = None
1005 secon = 0
1006 if security.on():
1007 default_label = security.ssidref2label(security.NULL_SSIDREF)
1008 secon = 1
1010 # get the domain acm_label
1011 dom_label = None
1012 dom_name = None
1013 for x in sxp.children(config):
1014 if sxp.name(x) == 'security':
1015 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1016 if sxp.name(x) == 'name':
1017 dom_name = sxp.child0(x)
1019 # sanity check on domain label
1020 if verbose:
1021 print "Checking domain:"
1022 if (not secon) and (not dom_label):
1023 answer = 1
1024 if verbose:
1025 print " %s: PERMITTED" % (dom_name)
1026 elif (secon) and (dom_label) and (dom_label != default_label):
1027 answer = 1
1028 if verbose:
1029 print " %s: PERMITTED" % (dom_name)
1030 else:
1031 print " %s: DENIED" % (dom_name)
1032 if not secon:
1033 print " --> Security off, but domain labeled"
1034 else:
1035 print " --> Domain not labeled"
1036 answer = 0
1038 return answer
1041 def config_security_check(config, verbose):
1042 """Checks each resource listed in the config to see if the active
1043 policy will permit creation of a new domain using the config.
1044 Returns 1 if the config passes all tests, otherwise 0.
1045 """
1046 answer = 1
1048 # get the domain acm_label
1049 domain_label = None
1050 domain_policy = None
1051 for x in sxp.children(config):
1052 if sxp.name(x) == 'security':
1053 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1054 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1056 # if no domain label, use default
1057 if not domain_label and security.on():
1058 try:
1059 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1060 except:
1061 traceback.print_exc(limit=1)
1062 return 0
1063 domain_policy = 'NULL'
1064 elif not domain_label:
1065 domain_label = ""
1066 domain_policy = 'NULL'
1068 if verbose:
1069 print "Checking resources:"
1071 # build a list of all resources in the config file
1072 resources = []
1073 for x in sxp.children(config):
1074 if sxp.name(x) == 'device':
1075 if sxp.name(sxp.child0(x)) == 'vbd':
1076 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1078 # perform a security check on each resource
1079 for resource in resources:
1080 try:
1081 security.res_security_check(resource, domain_label)
1082 if verbose:
1083 print " %s: PERMITTED" % (resource)
1085 except security.ACMError:
1086 print " %s: DENIED" % (resource)
1087 (res_label, res_policy) = security.get_res_label(resource)
1088 print " --> res:"+res_label+" ("+res_policy+")"
1089 print " --> dom:"+domain_label+" ("+domain_policy+")"
1090 answer = 0
1092 return answer
1095 def create_security_check(config):
1096 passed = 0
1097 try:
1098 if check_domain_label(config, verbose=0):
1099 if config_security_check(config, verbose=0):
1100 passed = 1
1101 else:
1102 print "Checking resources: (skipped)"
1103 except security.ACMError:
1104 traceback.print_exc(limit=1)
1106 return passed
1109 def main(argv):
1110 try:
1111 (opts, config) = parseCommandLine(argv)
1112 except StandardError, ex:
1113 err(str(ex))
1115 if not opts:
1116 return
1118 if opts.vals.dryrun:
1119 PrettyPrint.prettyprint(config)
1120 else:
1121 if not create_security_check(config):
1122 print "Security configuration prevents domain from starting"
1123 else:
1124 dom = make_domain(opts, config)
1125 if opts.vals.console_autoconnect:
1126 console.execConsole(dom)
1128 if __name__ == '__main__':
1129 main(sys.argv)