ia64/xen-unstable

view tools/python/xen/xm/create.py @ 10720:8922c1fbe684

[XM][ACM] Add xm subcommands to work with security resource labels.

This patch adds new xm subcommands to support working with resource
labels. The new subcommands are 'xm resources', 'xm rmlabel', 'xm
getlabel' and 'xm dry-run'. In addition, the 'xm addlabel' subcommand
now uses an updated syntax to support labeling both domains and
resources. See the xm man page for details on each subcommand.

Beyond the new subcommands, this patch allows users to immediately see
when security checks will fail by pushing some basic security checking
into the beginning of 'xm create' and 'xm block-attach'. ACM security
attributes for block devices are added to XenStore in order to support
the final security enforcement, which will be performed in the kernel
and included in a separate patch.

Signed-off-by: Bryan D. Payne <bdpayne@us.ibm.com>
Signed-off-by: Reiner Sailer <sailer@us.ibm.com>
author kfraser@localhost.localdomain
date Mon Jul 10 17:18:07 2006 +0100 (2006-07-10)
parents 9dbcf482f600
children 6d8f2d78d7c8
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('ne2000', val='no|yes',
402 fn=set_bool, default=0,
403 use="Should device models use ne2000?")
405 gopts.var('audio', val='no|yes',
406 fn=set_bool, default=0,
407 use="Should device models enable audio?")
409 gopts.var('vnc', val='',
410 fn=set_value, default=None,
411 use="""Should the device model use VNC?""")
413 gopts.var('sdl', val='',
414 fn=set_value, default=None,
415 use="""Should the device model use SDL?""")
417 gopts.var('display', val='DISPLAY',
418 fn=set_value, default=None,
419 use="X11 display to use")
421 gopts.var('xauthority', val='XAUTHORITY',
422 fn=set_value, default=None,
423 use="X11 Authority to use")
425 gopts.var('uuid', val='',
426 fn=set_value, default=None,
427 use="""xenstore UUID (universally unique identifier) to use. One
428 will be randomly generated if this option is not set, just like MAC
429 addresses for virtual network interfaces. This must be a unique
430 value across the entire cluster.""")
433 def err(msg):
434 """Print an error to stderr and exit.
435 """
436 print >>sys.stderr, "Error:", msg
437 sys.exit(1)
440 def warn(msg):
441 """Print a warning to stdout.
442 """
443 print >>sys.stderr, "Warning:", msg
446 def strip(pre, s):
447 """Strip prefix 'pre' if present.
448 """
449 if s.startswith(pre):
450 return s[len(pre):]
451 else:
452 return s
454 def configure_image(vals):
455 """Create the image config.
456 """
457 if not vals.builder:
458 return None
459 config_image = [ vals.builder ]
460 if vals.kernel:
461 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
462 if vals.ramdisk:
463 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
464 if vals.cmdline_ip:
465 cmdline_ip = strip('ip=', vals.cmdline_ip)
466 config_image.append(['ip', cmdline_ip])
467 if vals.root:
468 cmdline_root = strip('root=', vals.root)
469 config_image.append(['root', cmdline_root])
470 if vals.extra:
471 config_image.append(['args', vals.extra])
473 if vals.builder == 'hvm':
474 configure_hvm(config_image, vals)
476 return config_image
478 def configure_disks(config_devs, vals):
479 """Create the config for disks (virtual block devices).
480 """
481 for (uname, dev, mode, backend) in vals.disk:
482 config_vbd = ['vbd',
483 ['uname', uname],
484 ['dev', dev ],
485 ['mode', mode ] ]
486 if backend:
487 config_vbd.append(['backend', backend])
488 config_devs.append(['device', config_vbd])
490 def configure_pci(config_devs, vals):
491 """Create the config for pci devices.
492 """
493 config_pci = []
494 for (domain, bus, slot, func) in vals.pci:
495 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
496 ['slot', slot], ['func', func]])
498 if len(config_pci)>0:
499 config_pci.insert(0, 'pci')
500 config_devs.append(['device', config_pci])
502 def configure_ioports(config_devs, vals):
503 """Create the config for legacy i/o ranges.
504 """
505 for (io_from, io_to) in vals.ioports:
506 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
507 config_devs.append(['device', config_ioports])
509 def configure_irq(config_devs, vals):
510 """Create the config for irqs.
511 """
512 for irq in vals.irq:
513 config_irq = ['irq', ['irq', irq]]
514 config_devs.append(['device', config_irq])
516 def configure_usb(config_devs, vals):
517 for path in vals.usbport:
518 config_usb = ['usbport', ['path', path]]
519 config_devs.append(['device', config_usb])
522 def configure_security(config, vals):
523 """Create the config for ACM security labels.
524 """
525 access_control = vals.access_control
526 num = len(access_control)
527 if num == 1:
528 d = access_control[0]
529 policy = d.get('policy')
530 label = d.get('label')
531 if policy != security.active_policy:
532 err("Security policy (" + policy + ") incompatible with enforced policy ("
533 + security.active_policy + ")." )
534 config_access_control = ['access_control',
535 ['policy', policy],
536 ['label', label] ]
538 #ssidref cannot be specified together with access_control
539 if sxp.child_value(config, 'ssidref'):
540 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
541 #else calculate ssidre from label
542 ssidref = security.label2ssidref(label, policy, 'dom')
543 if not ssidref :
544 err("ERROR calculating ssidref from access_control.")
545 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
546 config.append(security_label)
547 elif num == 0:
548 if hasattr(vals, 'ssidref'):
549 if not security.on():
550 err("ERROR: Security ssidref specified but no policy active.")
551 ssidref = getattr(vals, 'ssidref')
552 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
553 config.append(security_label)
554 elif num > 1:
555 err("VM config error: Multiple access_control definitions!")
558 def configure_vtpm(config_devs, vals):
559 """Create the config for virtual TPM interfaces.
560 """
561 vtpm = vals.vtpm
562 vtpm_n = 1
563 for idx in range(0, vtpm_n):
564 if idx < len(vtpm):
565 d = vtpm[idx]
566 instance = d.get('instance')
567 if instance == "VTPMD":
568 instance = "0"
569 else:
570 if instance != None:
571 try:
572 if int(instance) == 0:
573 err('VM config error: vTPM instance must not be 0.')
574 except ValueError:
575 err('Vm config error: could not parse instance number.')
576 backend = d.get('backend')
577 config_vtpm = ['vtpm']
578 if instance:
579 config_vtpm.append(['pref_instance', instance])
580 if backend:
581 config_vtpm.append(['backend', backend])
582 config_devs.append(['device', config_vtpm])
585 def configure_vifs(config_devs, vals):
586 """Create the config for virtual network interfaces.
587 """
589 vifs = vals.vif
590 vifs_n = len(vifs)
592 if hasattr(vals, 'nics'):
593 if vals.nics > 0:
594 warn("The nics option is deprecated. Please use an empty vif "
595 "entry instead:\n\n vif = [ '' ]\n")
596 for _ in range(vifs_n, vals.nics):
597 vifs.append('')
598 vifs_n = len(vifs)
599 elif vals.nics == 0:
600 warn("The nics option is deprecated. Please remove it.")
602 for c in vifs:
603 d = comma_sep_kv_to_dict(c)
604 config_vif = ['vif']
606 def f(k):
607 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
608 'vifname', 'rate']:
609 err('Invalid vif option: ' + k)
611 config_vif.append([k, d[k]])
613 map(f, d.keys())
614 config_devs.append(['device', config_vif])
617 def configure_hvm(config_image, vals):
618 """Create the config for HVM devices.
619 """
620 args = [ 'device_model', 'pae', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
621 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'audio',
622 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'acpi', 'apic',
623 'xauthority', 'usb', 'usbdevice' ]
624 for a in args:
625 if (vals.__dict__[a]):
626 config_image.append([a, vals.__dict__[a]])
628 def run_bootloader(vals, config_image):
629 if not os.access(vals.bootloader, os.X_OK):
630 err("Bootloader isn't executable")
631 if len(vals.disk) < 1:
632 err("No disks configured and boot loader requested")
633 (uname, dev, mode, backend) = vals.disk[0]
634 file = blkif.blkdev_uname_to_file(uname)
636 if vals.bootentry:
637 warn("The bootentry option is deprecated. Use bootargs and pass "
638 "--entry= directly.")
639 vals.bootargs = "--entry=%s" %(vals.bootentry,)
641 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
642 vals.bootargs, config_image)
644 def make_config(vals):
645 """Create the domain configuration.
646 """
648 config = ['vm']
650 def add_conf(n):
651 if hasattr(vals, n):
652 v = getattr(vals, n)
653 if v:
654 config.append([n, v])
656 map(add_conf, ['name', 'memory', 'maxmem', 'restart', 'on_poweroff',
657 'on_reboot', 'on_crash', 'vcpus', 'features'])
659 if vals.uuid is not None:
660 config.append(['uuid', vals.uuid])
661 if vals.cpu is not None:
662 config.append(['cpu', vals.cpu])
663 if vals.cpus is not None:
664 config.append(['cpus', vals.cpus])
665 if vals.cpu_weight is not None:
666 config.append(['cpu_weight', vals.cpu_weight])
667 if vals.blkif:
668 config.append(['backend', ['blkif']])
669 if vals.netif:
670 config.append(['backend', ['netif']])
671 if vals.tpmif:
672 config.append(['backend', ['tpmif']])
673 if vals.localtime:
674 config.append(['localtime', vals.localtime])
676 config_image = configure_image(vals)
677 if vals.bootloader:
678 config_image = run_bootloader(vals, config_image)
679 config.append(['bootloader', vals.bootloader])
680 if vals.bootargs:
681 config.append(['bootloader_args'], vals.bootargs)
682 config.append(['image', config_image])
684 config_devs = []
685 configure_disks(config_devs, vals)
686 configure_pci(config_devs, vals)
687 configure_ioports(config_devs, vals)
688 configure_irq(config_devs, vals)
689 configure_vifs(config_devs, vals)
690 configure_usb(config_devs, vals)
691 configure_vtpm(config_devs, vals)
692 configure_security(config, vals)
693 config += config_devs
695 return config
697 def preprocess_disk(vals):
698 if not vals.disk: return
699 disk = []
700 for v in vals.disk:
701 d = v.split(',')
702 n = len(d)
703 if n == 3:
704 d.append(None)
705 elif n == 4:
706 pass
707 else:
708 err('Invalid disk specifier: ' + v)
709 disk.append(d)
710 vals.disk = disk
712 def preprocess_pci(vals):
713 if not vals.pci: return
714 pci = []
715 for pci_dev_str in vals.pci:
716 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
717 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
718 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
719 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
720 if pci_match!=None:
721 pci_dev_info = pci_match.groupdict('0')
722 try:
723 pci.append( ('0x'+pci_dev_info['domain'], \
724 '0x'+pci_dev_info['bus'], \
725 '0x'+pci_dev_info['slot'], \
726 '0x'+pci_dev_info['func']))
727 except IndexError:
728 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
729 vals.pci = pci
731 def preprocess_ioports(vals):
732 if not vals.ioports: return
733 ioports = []
734 for v in vals.ioports:
735 d = v.split('-')
736 if len(d) < 1 or len(d) > 2:
737 err('Invalid i/o port range specifier: ' + v)
738 if len(d) == 1:
739 d.append(d[0])
740 # Components are in hex: add hex specifier.
741 hexd = map(lambda v: '0x'+v, d)
742 ioports.append(hexd)
743 vals.ioports = ioports
745 def preprocess_vtpm(vals):
746 if not vals.vtpm: return
747 vtpms = []
748 for vtpm in vals.vtpm:
749 d = {}
750 a = vtpm.split(',')
751 for b in a:
752 (k, v) = b.strip().split('=', 1)
753 k = k.strip()
754 v = v.strip()
755 if k not in ['backend', 'instance']:
756 err('Invalid vtpm specifier: ' + vtpm)
757 d[k] = v
758 vtpms.append(d)
759 vals.vtpm = vtpms
761 def preprocess_access_control(vals):
762 if not vals.access_control:
763 return
764 access_controls = []
765 num = len(vals.access_control)
766 if num == 1:
767 access_control = (vals.access_control)[0]
768 d = {}
769 a = access_control.split(',')
770 if len(a) > 2:
771 err('Too many elements in access_control specifier: ' + access_control)
772 for b in a:
773 (k, v) = b.strip().split('=', 1)
774 k = k.strip()
775 v = v.strip()
776 if k not in ['policy','label']:
777 err('Invalid access_control specifier: ' + access_control)
778 d[k] = v
779 access_controls.append(d)
780 vals.access_control = access_controls
781 elif num > 1:
782 err('Multiple access_control definitions.')
784 def preprocess_ip(vals):
785 if vals.ip or vals.dhcp != 'off':
786 dummy_nfs_server = '1.2.3.4'
787 ip = (vals.ip
788 + ':' + (vals.nfs_server or dummy_nfs_server)
789 + ':' + vals.gateway
790 + ':' + vals.netmask
791 + ':' + vals.hostname
792 + ':' + vals.interface
793 + ':' + vals.dhcp)
794 else:
795 ip = ''
796 vals.cmdline_ip = ip
798 def preprocess_nfs(vals):
799 if not vals.nfs_root: return
800 if not vals.nfs_server:
801 err('Must set nfs root and nfs server')
802 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
803 vals.extra = nfs + ' ' + vals.extra
806 def get_host_addr():
807 host = socket.gethostname()
808 addr = socket.gethostbyname(host)
809 return addr
811 VNC_BASE_PORT = 5500
813 def choose_vnc_display():
814 """Try to choose a free vnc display.
815 """
816 def netstat_local_ports():
817 """Run netstat to get a list of the local ports in use.
818 """
819 l = os.popen("netstat -nat").readlines()
820 r = []
821 # Skip 2 lines of header.
822 for x in l[2:]:
823 # Local port is field 3.
824 y = x.split()[3]
825 # Field is addr:port, split off the port.
826 y = y.split(':')[-1]
827 r.append(int(y))
828 return r
830 ports = netstat_local_ports()
831 for d in range(1, 100):
832 port = VNC_BASE_PORT + d
833 if port in ports: continue
834 return d
835 return None
837 vncpid = None
839 def spawn_vnc(display):
840 vncargs = (["vncviewer", "-log", "*:stdout:0",
841 "-listen", "%d" % (VNC_BASE_PORT + display) ])
842 global vncpid
843 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
845 return VNC_BASE_PORT + display
847 def preprocess_vnc(vals):
848 """If vnc was specified, spawn a vncviewer in listen mode
849 and pass its address to the domain on the kernel command line.
850 """
851 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
852 vnc_display = choose_vnc_display()
853 if not vnc_display:
854 warn("No free vnc display")
855 return
856 print 'VNC=', vnc_display
857 vnc_port = spawn_vnc(vnc_display)
858 if vnc_port > 0:
859 vnc_host = get_host_addr()
860 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
861 vals.extra = vnc + ' ' + vals.extra
863 def preprocess(vals):
864 if not vals.kernel and not vals.bootloader:
865 err("No kernel specified")
866 preprocess_disk(vals)
867 preprocess_pci(vals)
868 preprocess_ioports(vals)
869 preprocess_ip(vals)
870 preprocess_nfs(vals)
871 preprocess_vnc(vals)
872 preprocess_vtpm(vals)
873 preprocess_access_control(vals)
876 def comma_sep_kv_to_dict(c):
877 """Convert comma-separated, equals-separated key-value pairs into a
878 dictionary.
879 """
880 d = {}
881 c = c.strip()
882 if len(c) > 0:
883 a = c.split(',')
884 for b in a:
885 if b.find('=') == -1:
886 err("%s should be a pair, separated by an equals sign." % b)
887 (k, v) = b.split('=', 1)
888 k = k.strip()
889 v = v.strip()
890 d[k] = v
891 return d
894 def make_domain(opts, config):
895 """Create, build and start a domain.
897 @param opts: options
898 @param config: configuration
899 @return: domain id
900 @rtype: int
901 """
903 try:
904 dominfo = server.xend.domain.create(config)
905 except xmlrpclib.Fault, ex:
906 import signal
907 if vncpid:
908 os.kill(vncpid, signal.SIGKILL)
909 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
910 err("the domain '%s' does not exist." % ex.faultString)
911 else:
912 err("%s" % ex.faultString)
913 except Exception, ex:
914 # main.py has good error messages that let the user know what failed.
915 # unless the error is a create.py specific thing, it should be handled
916 # at main. The purpose of this general-case 'Exception' handler is to
917 # clean up create.py specific processes/data but since create.py does
918 # not know what to do with the error, it should pass it up.
919 import signal
920 if vncpid:
921 os.kill(vncpid, signal.SIGKILL)
922 raise ex
924 dom = sxp.child_value(dominfo, 'name')
926 try:
927 server.xend.domain.waitForDevices(dom)
928 except xmlrpclib.Fault, ex:
929 server.xend.domain.destroy(dom)
930 err("%s" % ex.faultString)
931 except:
932 server.xend.domain.destroy(dom)
933 err("Device creation failed for domain %s" % dom)
935 if not opts.vals.paused:
936 try:
937 server.xend.domain.unpause(dom)
938 except:
939 server.xend.domain.destroy(dom)
940 err("Failed to unpause domain %s" % dom)
941 opts.info("Started domain %s" % (dom))
942 return int(sxp.child_value(dominfo, 'domid'))
945 def get_xauthority():
946 xauth = os.getenv("XAUTHORITY")
947 if not xauth:
948 home = os.getenv("HOME")
949 if not home:
950 import posix, pwd
951 home = pwd.getpwuid(posix.getuid())[5]
952 xauth = home + "/.Xauthority"
953 return xauth
956 def parseCommandLine(argv):
957 gopts.reset()
958 args = gopts.parse(argv)
959 if gopts.vals.help:
960 gopts.usage()
961 if gopts.vals.help or gopts.vals.help_config:
962 gopts.load_defconfig(help=1)
963 if gopts.vals.help or gopts.vals.help_config:
964 return (None, None)
966 if not gopts.vals.display:
967 gopts.vals.display = os.getenv("DISPLAY")
969 if not gopts.vals.xauthority:
970 gopts.vals.xauthority = get_xauthority()
972 # Process remaining args as config variables.
973 for arg in args:
974 if '=' in arg:
975 (var, val) = arg.strip().split('=', 1)
976 gopts.setvar(var.strip(), val.strip())
977 if gopts.vals.config:
978 config = gopts.vals.config
979 else:
980 gopts.load_defconfig()
981 preprocess(gopts.vals)
982 if not gopts.getopt('name') and gopts.getopt('defconfig'):
983 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
984 config = make_config(gopts.vals)
986 return (gopts, config)
989 def check_domain_label(config, verbose):
990 """All that we need to check here is that the domain label exists and
991 is not null when security is on. Other error conditions are
992 handled when the config file is parsed.
993 """
994 answer = 0
995 default_label = None
996 secon = 0
997 if security.on():
998 default_label = security.ssidref2label(security.NULL_SSIDREF)
999 secon = 1
1001 # get the domain acm_label
1002 dom_label = None
1003 dom_name = None
1004 for x in sxp.children(config):
1005 if sxp.name(x) == 'security':
1006 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1007 if sxp.name(x) == 'name':
1008 dom_name = sxp.child0(x)
1010 # sanity check on domain label
1011 if verbose:
1012 print "Checking domain:"
1013 if (not secon) and (not dom_label):
1014 answer = 1
1015 if verbose:
1016 print " %s: PERMITTED" % (dom_name)
1017 elif (secon) and (dom_label) and (dom_label != default_label):
1018 answer = 1
1019 if verbose:
1020 print " %s: PERMITTED" % (dom_name)
1021 else:
1022 print " %s: DENIED" % (dom_name)
1023 if not secon:
1024 print " --> Security off, but domain labeled"
1025 else:
1026 print " --> Domain not labeled"
1027 answer = 0
1029 return answer
1032 def config_security_check(config, verbose):
1033 """Checks each resource listed in the config to see if the active
1034 policy will permit creation of a new domain using the config.
1035 Returns 1 if the config passes all tests, otherwise 0.
1036 """
1037 answer = 1
1039 # get the domain acm_label
1040 domain_label = None
1041 domain_policy = None
1042 for x in sxp.children(config):
1043 if sxp.name(x) == 'security':
1044 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1045 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1047 # if no domain label, use default
1048 if not domain_label and security.on():
1049 try:
1050 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1051 except:
1052 traceback.print_exc(limit=1)
1053 return 0
1054 domain_policy = 'NULL'
1055 elif not domain_label:
1056 domain_label = ""
1057 domain_policy = 'NULL'
1059 if verbose:
1060 print "Checking resources:"
1062 # build a list of all resources in the config file
1063 resources = []
1064 for x in sxp.children(config):
1065 if sxp.name(x) == 'device':
1066 if sxp.name(sxp.child0(x)) == 'vbd':
1067 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1069 # perform a security check on each resource
1070 for resource in resources:
1071 try:
1072 security.res_security_check(resource, domain_label)
1073 if verbose:
1074 print " %s: PERMITTED" % (resource)
1076 except security.ACMError:
1077 print " %s: DENIED" % (resource)
1078 (res_label, res_policy) = security.get_res_label(resource)
1079 print " --> res:"+res_label+" ("+res_policy+")"
1080 print " --> dom:"+domain_label+" ("+domain_policy+")"
1081 answer = 0
1083 return answer
1086 def create_security_check(config):
1087 passed = 0
1088 try:
1089 if check_domain_label(config, verbose=0):
1090 if config_security_check(config, verbose=0):
1091 passed = 1
1092 else:
1093 print "Checking resources: (skipped)"
1094 except security.ACMError:
1095 traceback.print_exc(limit=1)
1097 return passed
1100 def main(argv):
1101 try:
1102 (opts, config) = parseCommandLine(argv)
1103 except StandardError, ex:
1104 err(str(ex))
1106 if not opts:
1107 return
1109 if opts.vals.dryrun:
1110 PrettyPrint.prettyprint(config)
1111 else:
1112 if not create_security_check(config):
1113 print "Security configuration prevents domain from starting"
1114 else:
1115 dom = make_domain(opts, config)
1116 if opts.vals.console_autoconnect:
1117 console.execConsole(dom)
1119 if __name__ == '__main__':
1120 main(sys.argv)