direct-io.hg

view tools/python/xen/xm/create.py @ 10674:6d8f2d78d7c8

Update xend to support network configuration for qemu 0.8.1 based ioemu.
Remove the ne2000 option, the network device type can now be selected
on a per-device basis by adding a model= property to the device's entry
in the vif list.

Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
author chris@kneesaa.uk.xensource.com
date Wed Jul 12 19:16:12 2006 +0100 (2006-07-12)
parents 8922c1fbe684
children a70c4f9657cc
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('name', val='NAME',
116 fn=set_value, default=None,
117 use="Domain name. Must be unique.")
119 gopts.var('bootloader', val='FILE',
120 fn=set_value, default=None,
121 use="Path to bootloader.")
123 gopts.var('bootargs', val='NAME',
124 fn=set_value, default=None,
125 use="Arguments to pass to boot loader")
127 gopts.var('bootentry', val='NAME',
128 fn=set_value, default=None,
129 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
131 gopts.var('kernel', val='FILE',
132 fn=set_value, default=None,
133 use="Path to kernel image.")
135 gopts.var('ramdisk', val='FILE',
136 fn=set_value, default='',
137 use="Path to ramdisk.")
139 gopts.var('features', val='FEATURES',
140 fn=set_value, default='',
141 use="Features to enable in guest kernel")
143 gopts.var('builder', val='FUNCTION',
144 fn=set_value, default='linux',
145 use="Function to use to build the domain.")
147 gopts.var('memory', val='MEMORY',
148 fn=set_int, default=128,
149 use="Domain memory in MB.")
151 gopts.var('maxmem', val='MEMORY',
152 fn=set_int, default=None,
153 use="Maximum domain memory in MB.")
155 gopts.var('cpu', val='CPU',
156 fn=set_int, default=None,
157 use="CPU to run the VCPU0 on.")
159 gopts.var('cpus', val='CPUS',
160 fn=set_value, default=None,
161 use="CPUS to run the domain on.")
163 gopts.var('pae', val='PAE',
164 fn=set_int, default=0,
165 use="Disable or enable PAE of HVM domain.")
167 gopts.var('acpi', val='ACPI',
168 fn=set_int, default=0,
169 use="Disable or enable ACPI of HVM domain.")
171 gopts.var('apic', val='APIC',
172 fn=set_int, default=0,
173 use="Disable or enable APIC of HVM domain.")
175 gopts.var('vcpus', val='VCPUS',
176 fn=set_int, default=1,
177 use="# of Virtual CPUS in domain.")
179 gopts.var('cpu_weight', val='WEIGHT',
180 fn=set_float, default=None,
181 use="""Set the new domain's cpu weight.
182 WEIGHT is a float that controls the domain's share of the cpu.""")
184 gopts.var('restart', val='onreboot|always|never',
185 fn=set_value, default=None,
186 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
187 instead.
189 Whether the domain should be restarted on exit.
190 - onreboot: restart on exit with shutdown code reboot
191 - always: always restart on exit, ignore exit code
192 - never: never restart on exit, ignore exit code""")
194 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
195 fn=set_value, default=None,
196 use="""Behaviour when a domain exits with reason 'poweroff'.
197 - destroy: the domain is cleaned up as normal;
198 - restart: a new domain is started in place of the old one;
199 - preserve: no clean-up is done until the domain is manually
200 destroyed (using xm destroy, for example);
201 - rename-restart: the old domain is not cleaned up, but is
202 renamed and a new domain started in its place.
203 """)
205 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
206 fn=set_value, default=None,
207 use="""Behaviour when a domain exits with reason 'reboot'.
208 - destroy: the domain is cleaned up as normal;
209 - restart: a new domain is started in place of the old one;
210 - preserve: no clean-up is done until the domain is manually
211 destroyed (using xm destroy, for example);
212 - rename-restart: the old domain is not cleaned up, but is
213 renamed and a new domain started in its place.
214 """)
216 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
217 fn=set_value, default=None,
218 use="""Behaviour when a domain exits with reason 'crash'.
219 - destroy: the domain is cleaned up as normal;
220 - restart: a new domain is started in place of the old one;
221 - preserve: no clean-up is done until the domain is manually
222 destroyed (using xm destroy, for example);
223 - rename-restart: the old domain is not cleaned up, but is
224 renamed and a new domain started in its place.
225 """)
227 gopts.var('blkif', val='no|yes',
228 fn=set_bool, default=0,
229 use="Make the domain a block device backend.")
231 gopts.var('netif', val='no|yes',
232 fn=set_bool, default=0,
233 use="Make the domain a network interface backend.")
235 gopts.var('tpmif', val='no|yes',
236 fn=append_value, default=0,
237 use="Make the domain a TPM interface backend.")
239 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
240 fn=append_value, default=[],
241 use="""Add a disk device to a domain. The physical device is DEV,
242 which is exported to the domain as VDEV. The disk is read-only if MODE
243 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
244 backend driver domain to use for the disk.
245 The option may be repeated to add more than one disk.""")
247 gopts.var('pci', val='BUS:DEV.FUNC',
248 fn=append_value, default=[],
249 use="""Add a PCI device to a domain, using given params (in hex).
250 For example 'pci=c0:02.1a'.
251 The option may be repeated to add more than one pci device.""")
253 gopts.var('ioports', val='FROM[-TO]',
254 fn=append_value, default=[],
255 use="""Add a legacy I/O range to a domain, using given params (in hex).
256 For example 'ioports=02f8-02ff'.
257 The option may be repeated to add more than one i/o range.""")
259 gopts.var('irq', val='IRQ',
260 fn=append_value, default=[],
261 use="""Add an IRQ (interrupt line) to a domain.
262 For example 'irq=7'.
263 This option may be repeated to add more than one IRQ.""")
265 gopts.var('usbport', val='PATH',
266 fn=append_value, default=[],
267 use="""Add a physical USB port to a domain, as specified by the path
268 to that port. This option may be repeated to add more than one port.""")
270 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
271 fn=append_value, default=[],
272 use="""Add a network interface with the given MAC address and bridge.
273 The vif is configured by calling the given configuration script.
274 If type is not specified, default is netfront not ioemu device.
275 If mac is not specified a random MAC address is used.
276 If not specified then the network backend chooses it's own MAC address.
277 If bridge is not specified the first bridge found is used.
278 If script is not specified the default script is used.
279 If backend is not specified the default backend driver domain is used.
280 If vifname is not specified the backend virtual interface will have name vifD.N
281 where D is the domain id and N is the interface id.
282 This option may be repeated to add more than one vif.
283 Specifying vifs will increase the number of interfaces as needed.""")
285 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
286 fn=append_value, default=[],
287 use="""Add a TPM interface. On the backend side use the given
288 instance as virtual TPM instance. The given number is merely the
289 preferred instance number. The hotplug script will determine
290 which instance number will actually be assigned to the domain.
291 The associtation between virtual machine and the TPM instance
292 number can be found in /etc/xen/vtpm.db. Use the backend in the
293 given domain.""")
295 gopts.var('access_control', val="policy=POLICY,label=LABEL",
296 fn=append_value, default=[],
297 use="""Add a security label and the security policy reference that defines it.
298 The local ssid reference is calculated when starting/resuming the domain. At
299 this time, the policy is checked against the active policy as well. This way,
300 migrating through save/restore is covered and local labels are automatically
301 created correctly on the system where a domain is started / resumed.""")
303 gopts.var('nics', val="NUM",
304 fn=set_int, default=-1,
305 use="""DEPRECATED. Use empty vif entries instead.
307 Set the number of network interfaces.
308 Use the vif option to define interface parameters, otherwise
309 defaults are used. Specifying vifs will increase the
310 number of interfaces as needed.""")
312 gopts.var('root', val='DEVICE',
313 fn=set_value, default='',
314 use="""Set the root= parameter on the kernel command line.
315 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
317 gopts.var('extra', val="ARGS",
318 fn=set_value, default='',
319 use="Set extra arguments to append to the kernel command line.")
321 gopts.var('ip', val='IPADDR',
322 fn=set_value, default='',
323 use="Set the kernel IP interface address.")
325 gopts.var('gateway', val="IPADDR",
326 fn=set_value, default='',
327 use="Set the kernel IP gateway.")
329 gopts.var('netmask', val="MASK",
330 fn=set_value, default = '',
331 use="Set the kernel IP netmask.")
333 gopts.var('hostname', val="NAME",
334 fn=set_value, default='',
335 use="Set the kernel IP hostname.")
337 gopts.var('interface', val="INTF",
338 fn=set_value, default="eth0",
339 use="Set the kernel IP interface name.")
341 gopts.var('dhcp', val="off|dhcp",
342 fn=set_value, default='off',
343 use="Set the kernel dhcp option.")
345 gopts.var('nfs_server', val="IPADDR",
346 fn=set_value, default=None,
347 use="Set the address of the NFS server for NFS root.")
349 gopts.var('nfs_root', val="PATH",
350 fn=set_value, default=None,
351 use="Set the path of the root NFS directory.")
353 gopts.var('device_model', val='FILE',
354 fn=set_value, default='',
355 use="Path to device model program.")
357 gopts.var('fda', val='FILE',
358 fn=set_value, default='',
359 use="Path to fda")
361 gopts.var('fdb', val='FILE',
362 fn=set_value, default='',
363 use="Path to fdb")
365 gopts.var('serial', val='FILE',
366 fn=set_value, default='',
367 use="Path to serial or pty or vc")
369 gopts.var('localtime', val='no|yes',
370 fn=set_bool, default=0,
371 use="Is RTC set to localtime?")
373 gopts.var('usb', val='no|yes',
374 fn=set_bool, default=0,
375 use="Emulate USB devices?")
377 gopts.var('usbdevice', val='NAME',
378 fn=set_value, default='',
379 use="Name of USB device to add?")
381 gopts.var('stdvga', val='no|yes',
382 fn=set_bool, default=0,
383 use="Use std vga or cirrhus logic graphics")
385 gopts.var('isa', val='no|yes',
386 fn=set_bool, default=0,
387 use="Simulate an ISA only system?")
389 gopts.var('cdrom', val='FILE',
390 fn=set_value, default='',
391 use="Path to cdrom")
393 gopts.var('boot', val="a|b|c|d",
394 fn=set_value, default='c',
395 use="Default boot device")
397 gopts.var('nographic', val='no|yes',
398 fn=set_bool, default=0,
399 use="Should device models use graphics?")
401 gopts.var('audio', val='no|yes',
402 fn=set_bool, default=0,
403 use="Should device models enable audio?")
405 gopts.var('vnc', val='',
406 fn=set_value, default=None,
407 use="""Should the device model use VNC?""")
409 gopts.var('sdl', val='',
410 fn=set_value, default=None,
411 use="""Should the device model use SDL?""")
413 gopts.var('display', val='DISPLAY',
414 fn=set_value, default=None,
415 use="X11 display to use")
417 gopts.var('xauthority', val='XAUTHORITY',
418 fn=set_value, default=None,
419 use="X11 Authority to use")
421 gopts.var('uuid', val='',
422 fn=set_value, default=None,
423 use="""xenstore UUID (universally unique identifier) to use. One
424 will be randomly generated if this option is not set, just like MAC
425 addresses for virtual network interfaces. This must be a unique
426 value across the entire cluster.""")
429 def err(msg):
430 """Print an error to stderr and exit.
431 """
432 print >>sys.stderr, "Error:", msg
433 sys.exit(1)
436 def warn(msg):
437 """Print a warning to stdout.
438 """
439 print >>sys.stderr, "Warning:", msg
442 def strip(pre, s):
443 """Strip prefix 'pre' if present.
444 """
445 if s.startswith(pre):
446 return s[len(pre):]
447 else:
448 return s
450 def configure_image(vals):
451 """Create the image config.
452 """
453 if not vals.builder:
454 return None
455 config_image = [ vals.builder ]
456 if vals.kernel:
457 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
458 if vals.ramdisk:
459 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
460 if vals.cmdline_ip:
461 cmdline_ip = strip('ip=', vals.cmdline_ip)
462 config_image.append(['ip', cmdline_ip])
463 if vals.root:
464 cmdline_root = strip('root=', vals.root)
465 config_image.append(['root', cmdline_root])
466 if vals.extra:
467 config_image.append(['args', vals.extra])
469 if vals.builder == 'hvm':
470 configure_hvm(config_image, vals)
472 return config_image
474 def configure_disks(config_devs, vals):
475 """Create the config for disks (virtual block devices).
476 """
477 for (uname, dev, mode, backend) in vals.disk:
478 config_vbd = ['vbd',
479 ['uname', uname],
480 ['dev', dev ],
481 ['mode', mode ] ]
482 if backend:
483 config_vbd.append(['backend', backend])
484 config_devs.append(['device', config_vbd])
486 def configure_pci(config_devs, vals):
487 """Create the config for pci devices.
488 """
489 config_pci = []
490 for (domain, bus, slot, func) in vals.pci:
491 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
492 ['slot', slot], ['func', func]])
494 if len(config_pci)>0:
495 config_pci.insert(0, 'pci')
496 config_devs.append(['device', config_pci])
498 def configure_ioports(config_devs, vals):
499 """Create the config for legacy i/o ranges.
500 """
501 for (io_from, io_to) in vals.ioports:
502 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
503 config_devs.append(['device', config_ioports])
505 def configure_irq(config_devs, vals):
506 """Create the config for irqs.
507 """
508 for irq in vals.irq:
509 config_irq = ['irq', ['irq', irq]]
510 config_devs.append(['device', config_irq])
512 def configure_usb(config_devs, vals):
513 for path in vals.usbport:
514 config_usb = ['usbport', ['path', path]]
515 config_devs.append(['device', config_usb])
518 def configure_security(config, vals):
519 """Create the config for ACM security labels.
520 """
521 access_control = vals.access_control
522 num = len(access_control)
523 if num == 1:
524 d = access_control[0]
525 policy = d.get('policy')
526 label = d.get('label')
527 if policy != security.active_policy:
528 err("Security policy (" + policy + ") incompatible with enforced policy ("
529 + security.active_policy + ")." )
530 config_access_control = ['access_control',
531 ['policy', policy],
532 ['label', label] ]
534 #ssidref cannot be specified together with access_control
535 if sxp.child_value(config, 'ssidref'):
536 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
537 #else calculate ssidre from label
538 ssidref = security.label2ssidref(label, policy, 'dom')
539 if not ssidref :
540 err("ERROR calculating ssidref from access_control.")
541 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
542 config.append(security_label)
543 elif num == 0:
544 if hasattr(vals, 'ssidref'):
545 if not security.on():
546 err("ERROR: Security ssidref specified but no policy active.")
547 ssidref = getattr(vals, 'ssidref')
548 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
549 config.append(security_label)
550 elif num > 1:
551 err("VM config error: Multiple access_control definitions!")
554 def configure_vtpm(config_devs, vals):
555 """Create the config for virtual TPM interfaces.
556 """
557 vtpm = vals.vtpm
558 vtpm_n = 1
559 for idx in range(0, vtpm_n):
560 if idx < len(vtpm):
561 d = vtpm[idx]
562 instance = d.get('instance')
563 if instance == "VTPMD":
564 instance = "0"
565 else:
566 if instance != None:
567 try:
568 if int(instance) == 0:
569 err('VM config error: vTPM instance must not be 0.')
570 except ValueError:
571 err('Vm config error: could not parse instance number.')
572 backend = d.get('backend')
573 config_vtpm = ['vtpm']
574 if instance:
575 config_vtpm.append(['pref_instance', instance])
576 if backend:
577 config_vtpm.append(['backend', backend])
578 config_devs.append(['device', config_vtpm])
581 def configure_vifs(config_devs, vals):
582 """Create the config for virtual network interfaces.
583 """
585 vifs = vals.vif
586 vifs_n = len(vifs)
588 if hasattr(vals, 'nics'):
589 if vals.nics > 0:
590 warn("The nics option is deprecated. Please use an empty vif "
591 "entry instead:\n\n vif = [ '' ]\n")
592 for _ in range(vifs_n, vals.nics):
593 vifs.append('')
594 vifs_n = len(vifs)
595 elif vals.nics == 0:
596 warn("The nics option is deprecated. Please remove it.")
598 for c in vifs:
599 d = comma_sep_kv_to_dict(c)
600 config_vif = ['vif']
602 def f(k):
603 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
604 'vifname', 'rate', 'model']:
605 err('Invalid vif option: ' + k)
607 config_vif.append([k, d[k]])
609 map(f, d.keys())
610 config_devs.append(['device', config_vif])
613 def configure_hvm(config_image, vals):
614 """Create the config for HVM devices.
615 """
616 args = [ 'device_model', 'pae', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
617 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'audio',
618 'vnc', 'vncviewer', 'sdl', 'display', 'acpi', 'apic',
619 'xauthority', 'usb', 'usbdevice' ]
620 for a in args:
621 if (vals.__dict__[a]):
622 config_image.append([a, vals.__dict__[a]])
624 def run_bootloader(vals, config_image):
625 if not os.access(vals.bootloader, os.X_OK):
626 err("Bootloader isn't executable")
627 if len(vals.disk) < 1:
628 err("No disks configured and boot loader requested")
629 (uname, dev, mode, backend) = vals.disk[0]
630 file = blkif.blkdev_uname_to_file(uname)
632 if vals.bootentry:
633 warn("The bootentry option is deprecated. Use bootargs and pass "
634 "--entry= directly.")
635 vals.bootargs = "--entry=%s" %(vals.bootentry,)
637 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
638 vals.bootargs, config_image)
640 def make_config(vals):
641 """Create the domain configuration.
642 """
644 config = ['vm']
646 def add_conf(n):
647 if hasattr(vals, n):
648 v = getattr(vals, n)
649 if v:
650 config.append([n, v])
652 map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
653 'on_reboot', 'on_crash', 'vcpus', 'features'])
655 if vals.uuid is not None:
656 config.append(['uuid', vals.uuid])
657 if vals.cpu is not None:
658 config.append(['cpu', vals.cpu])
659 if vals.cpus is not None:
660 config.append(['cpus', vals.cpus])
661 if vals.cpu_weight is not None:
662 config.append(['cpu_weight', vals.cpu_weight])
663 if vals.blkif:
664 config.append(['backend', ['blkif']])
665 if vals.netif:
666 config.append(['backend', ['netif']])
667 if vals.tpmif:
668 config.append(['backend', ['tpmif']])
669 if vals.localtime:
670 config.append(['localtime', vals.localtime])
672 config_image = configure_image(vals)
673 if vals.bootloader:
674 config_image = run_bootloader(vals, config_image)
675 config.append(['bootloader', vals.bootloader])
676 if vals.bootargs:
677 config.append(['bootloader_args'], vals.bootargs)
678 config.append(['image', config_image])
680 config_devs = []
681 configure_disks(config_devs, vals)
682 configure_pci(config_devs, vals)
683 configure_ioports(config_devs, vals)
684 configure_irq(config_devs, vals)
685 configure_vifs(config_devs, vals)
686 configure_usb(config_devs, vals)
687 configure_vtpm(config_devs, vals)
688 configure_security(config, vals)
689 config += config_devs
691 return config
693 def preprocess_disk(vals):
694 if not vals.disk: return
695 disk = []
696 for v in vals.disk:
697 d = v.split(',')
698 n = len(d)
699 if n == 3:
700 d.append(None)
701 elif n == 4:
702 pass
703 else:
704 err('Invalid disk specifier: ' + v)
705 disk.append(d)
706 vals.disk = disk
708 def preprocess_pci(vals):
709 if not vals.pci: return
710 pci = []
711 for pci_dev_str in vals.pci:
712 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
713 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
714 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
715 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
716 if pci_match!=None:
717 pci_dev_info = pci_match.groupdict('0')
718 try:
719 pci.append( ('0x'+pci_dev_info['domain'], \
720 '0x'+pci_dev_info['bus'], \
721 '0x'+pci_dev_info['slot'], \
722 '0x'+pci_dev_info['func']))
723 except IndexError:
724 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
725 vals.pci = pci
727 def preprocess_ioports(vals):
728 if not vals.ioports: return
729 ioports = []
730 for v in vals.ioports:
731 d = v.split('-')
732 if len(d) < 1 or len(d) > 2:
733 err('Invalid i/o port range specifier: ' + v)
734 if len(d) == 1:
735 d.append(d[0])
736 # Components are in hex: add hex specifier.
737 hexd = map(lambda v: '0x'+v, d)
738 ioports.append(hexd)
739 vals.ioports = ioports
741 def preprocess_vtpm(vals):
742 if not vals.vtpm: return
743 vtpms = []
744 for vtpm in vals.vtpm:
745 d = {}
746 a = vtpm.split(',')
747 for b in a:
748 (k, v) = b.strip().split('=', 1)
749 k = k.strip()
750 v = v.strip()
751 if k not in ['backend', 'instance']:
752 err('Invalid vtpm specifier: ' + vtpm)
753 d[k] = v
754 vtpms.append(d)
755 vals.vtpm = vtpms
757 def preprocess_access_control(vals):
758 if not vals.access_control:
759 return
760 access_controls = []
761 num = len(vals.access_control)
762 if num == 1:
763 access_control = (vals.access_control)[0]
764 d = {}
765 a = access_control.split(',')
766 if len(a) > 2:
767 err('Too many elements in access_control specifier: ' + access_control)
768 for b in a:
769 (k, v) = b.strip().split('=', 1)
770 k = k.strip()
771 v = v.strip()
772 if k not in ['policy','label']:
773 err('Invalid access_control specifier: ' + access_control)
774 d[k] = v
775 access_controls.append(d)
776 vals.access_control = access_controls
777 elif num > 1:
778 err('Multiple access_control definitions.')
780 def preprocess_ip(vals):
781 if vals.ip or vals.dhcp != 'off':
782 dummy_nfs_server = '1.2.3.4'
783 ip = (vals.ip
784 + ':' + (vals.nfs_server or dummy_nfs_server)
785 + ':' + vals.gateway
786 + ':' + vals.netmask
787 + ':' + vals.hostname
788 + ':' + vals.interface
789 + ':' + vals.dhcp)
790 else:
791 ip = ''
792 vals.cmdline_ip = ip
794 def preprocess_nfs(vals):
795 if not vals.nfs_root: return
796 if not vals.nfs_server:
797 err('Must set nfs root and nfs server')
798 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
799 vals.extra = nfs + ' ' + vals.extra
802 def get_host_addr():
803 host = socket.gethostname()
804 addr = socket.gethostbyname(host)
805 return addr
807 VNC_BASE_PORT = 5500
809 def choose_vnc_display():
810 """Try to choose a free vnc display.
811 """
812 def netstat_local_ports():
813 """Run netstat to get a list of the local ports in use.
814 """
815 l = os.popen("netstat -nat").readlines()
816 r = []
817 # Skip 2 lines of header.
818 for x in l[2:]:
819 # Local port is field 3.
820 y = x.split()[3]
821 # Field is addr:port, split off the port.
822 y = y.split(':')[-1]
823 r.append(int(y))
824 return r
826 ports = netstat_local_ports()
827 for d in range(1, 100):
828 port = VNC_BASE_PORT + d
829 if port in ports: continue
830 return d
831 return None
833 vncpid = None
835 def spawn_vnc(display):
836 vncargs = (["vncviewer", "-log", "*:stdout:0",
837 "-listen", "%d" % (VNC_BASE_PORT + display) ])
838 global vncpid
839 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
841 return VNC_BASE_PORT + display
843 def preprocess_vnc(vals):
844 """If vnc was specified, spawn a vncviewer in listen mode
845 and pass its address to the domain on the kernel command line.
846 """
847 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
848 vnc_display = choose_vnc_display()
849 if not vnc_display:
850 warn("No free vnc display")
851 return
852 print 'VNC=', vnc_display
853 vnc_port = spawn_vnc(vnc_display)
854 if vnc_port > 0:
855 vnc_host = get_host_addr()
856 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
857 vals.extra = vnc + ' ' + vals.extra
859 def preprocess(vals):
860 if not vals.kernel and not vals.bootloader:
861 err("No kernel specified")
862 preprocess_disk(vals)
863 preprocess_pci(vals)
864 preprocess_ioports(vals)
865 preprocess_ip(vals)
866 preprocess_nfs(vals)
867 preprocess_vnc(vals)
868 preprocess_vtpm(vals)
869 preprocess_access_control(vals)
872 def comma_sep_kv_to_dict(c):
873 """Convert comma-separated, equals-separated key-value pairs into a
874 dictionary.
875 """
876 d = {}
877 c = c.strip()
878 if len(c) > 0:
879 a = c.split(',')
880 for b in a:
881 if b.find('=') == -1:
882 err("%s should be a pair, separated by an equals sign." % b)
883 (k, v) = b.split('=', 1)
884 k = k.strip()
885 v = v.strip()
886 d[k] = v
887 return d
890 def make_domain(opts, config):
891 """Create, build and start a domain.
893 @param opts: options
894 @param config: configuration
895 @return: domain id
896 @rtype: int
897 """
899 try:
900 dominfo = server.xend.domain.create(config)
901 except xmlrpclib.Fault, ex:
902 import signal
903 if vncpid:
904 os.kill(vncpid, signal.SIGKILL)
905 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
906 err("the domain '%s' does not exist." % ex.faultString)
907 else:
908 err("%s" % ex.faultString)
909 except Exception, ex:
910 # main.py has good error messages that let the user know what failed.
911 # unless the error is a create.py specific thing, it should be handled
912 # at main. The purpose of this general-case 'Exception' handler is to
913 # clean up create.py specific processes/data but since create.py does
914 # not know what to do with the error, it should pass it up.
915 import signal
916 if vncpid:
917 os.kill(vncpid, signal.SIGKILL)
918 raise ex
920 dom = sxp.child_value(dominfo, 'name')
922 try:
923 server.xend.domain.waitForDevices(dom)
924 except xmlrpclib.Fault, ex:
925 server.xend.domain.destroy(dom)
926 err("%s" % ex.faultString)
927 except:
928 server.xend.domain.destroy(dom)
929 err("Device creation failed for domain %s" % dom)
931 if not opts.vals.paused:
932 try:
933 server.xend.domain.unpause(dom)
934 except:
935 server.xend.domain.destroy(dom)
936 err("Failed to unpause domain %s" % dom)
937 opts.info("Started domain %s" % (dom))
938 return int(sxp.child_value(dominfo, 'domid'))
941 def get_xauthority():
942 xauth = os.getenv("XAUTHORITY")
943 if not xauth:
944 home = os.getenv("HOME")
945 if not home:
946 import posix, pwd
947 home = pwd.getpwuid(posix.getuid())[5]
948 xauth = home + "/.Xauthority"
949 return xauth
952 def parseCommandLine(argv):
953 gopts.reset()
954 args = gopts.parse(argv)
955 if gopts.vals.help:
956 gopts.usage()
957 if gopts.vals.help or gopts.vals.help_config:
958 gopts.load_defconfig(help=1)
959 if gopts.vals.help or gopts.vals.help_config:
960 return (None, None)
962 if not gopts.vals.display:
963 gopts.vals.display = os.getenv("DISPLAY")
965 if not gopts.vals.xauthority:
966 gopts.vals.xauthority = get_xauthority()
968 # Process remaining args as config variables.
969 for arg in args:
970 if '=' in arg:
971 (var, val) = arg.strip().split('=', 1)
972 gopts.setvar(var.strip(), val.strip())
973 if gopts.vals.config:
974 config = gopts.vals.config
975 else:
976 gopts.load_defconfig()
977 preprocess(gopts.vals)
978 if not gopts.getopt('name') and gopts.getopt('defconfig'):
979 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
980 config = make_config(gopts.vals)
982 return (gopts, config)
985 def check_domain_label(config, verbose):
986 """All that we need to check here is that the domain label exists and
987 is not null when security is on. Other error conditions are
988 handled when the config file is parsed.
989 """
990 answer = 0
991 default_label = None
992 secon = 0
993 if security.on():
994 default_label = security.ssidref2label(security.NULL_SSIDREF)
995 secon = 1
997 # get the domain acm_label
998 dom_label = None
999 dom_name = None
1000 for x in sxp.children(config):
1001 if sxp.name(x) == 'security':
1002 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1003 if sxp.name(x) == 'name':
1004 dom_name = sxp.child0(x)
1006 # sanity check on domain label
1007 if verbose:
1008 print "Checking domain:"
1009 if (not secon) and (not dom_label):
1010 answer = 1
1011 if verbose:
1012 print " %s: PERMITTED" % (dom_name)
1013 elif (secon) and (dom_label) and (dom_label != default_label):
1014 answer = 1
1015 if verbose:
1016 print " %s: PERMITTED" % (dom_name)
1017 else:
1018 print " %s: DENIED" % (dom_name)
1019 if not secon:
1020 print " --> Security off, but domain labeled"
1021 else:
1022 print " --> Domain not labeled"
1023 answer = 0
1025 return answer
1028 def config_security_check(config, verbose):
1029 """Checks each resource listed in the config to see if the active
1030 policy will permit creation of a new domain using the config.
1031 Returns 1 if the config passes all tests, otherwise 0.
1032 """
1033 answer = 1
1035 # get the domain acm_label
1036 domain_label = None
1037 domain_policy = None
1038 for x in sxp.children(config):
1039 if sxp.name(x) == 'security':
1040 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1041 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1043 # if no domain label, use default
1044 if not domain_label and security.on():
1045 try:
1046 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1047 except:
1048 traceback.print_exc(limit=1)
1049 return 0
1050 domain_policy = 'NULL'
1051 elif not domain_label:
1052 domain_label = ""
1053 domain_policy = 'NULL'
1055 if verbose:
1056 print "Checking resources:"
1058 # build a list of all resources in the config file
1059 resources = []
1060 for x in sxp.children(config):
1061 if sxp.name(x) == 'device':
1062 if sxp.name(sxp.child0(x)) == 'vbd':
1063 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1065 # perform a security check on each resource
1066 for resource in resources:
1067 try:
1068 security.res_security_check(resource, domain_label)
1069 if verbose:
1070 print " %s: PERMITTED" % (resource)
1072 except security.ACMError:
1073 print " %s: DENIED" % (resource)
1074 (res_label, res_policy) = security.get_res_label(resource)
1075 print " --> res:"+res_label+" ("+res_policy+")"
1076 print " --> dom:"+domain_label+" ("+domain_policy+")"
1077 answer = 0
1079 return answer
1082 def create_security_check(config):
1083 passed = 0
1084 try:
1085 if check_domain_label(config, verbose=0):
1086 if config_security_check(config, verbose=0):
1087 passed = 1
1088 else:
1089 print "Checking resources: (skipped)"
1090 except security.ACMError:
1091 traceback.print_exc(limit=1)
1093 return passed
1096 def main(argv):
1097 try:
1098 (opts, config) = parseCommandLine(argv)
1099 except StandardError, ex:
1100 err(str(ex))
1102 if not opts:
1103 return
1105 if opts.vals.dryrun:
1106 PrettyPrint.prettyprint(config)
1107 else:
1108 if not create_security_check(config):
1109 print "Security configuration prevents domain from starting"
1110 else:
1111 dom = make_domain(opts, config)
1112 if opts.vals.console_autoconnect:
1113 console.execConsole(dom)
1115 if __name__ == '__main__':
1116 main(sys.argv)