ia64/xen-unstable

view tools/python/xen/xm/create.py @ 13341:3040ba0f2d3d

When booting via xm, only run the bootloader if it's in non-interactive mode:
otherwise we lose the user's named kernel and try to bootload the temporary
file pygrub returned.

Signed-off-by: John Levon <john.levon@sun.com>
author Tim Deegan <Tim.Deegan@xensource.com>
date Tue Jan 09 13:24:45 2007 +0000 (2007-01-09)
parents 3f8930dc7d90
children d2505c4ca32b
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
29 from xen.xend import sxp
30 from xen.xend import PrettyPrint
31 from xen.xend import osdep
32 import xen.xend.XendClient
33 from xen.xend.XendBootloader import bootloader
34 from xen.util import blkif
35 from xen.util import security
37 from xen.xm.opts import *
39 from main import server
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 the available configuration variables (vars) for the "
61 "configuration script.")
63 gopts.opt('quiet', short='q',
64 fn=set_true, default=0,
65 use="Quiet.")
67 gopts.opt('path', val='PATH',
68 fn=set_value, default='.:/etc/xen',
69 use="Search path for configuration scripts. "
70 "The value of PATH is a colon-separated directory list.")
72 gopts.opt('defconfig', short='f', val='FILE',
73 fn=set_value, default='xmdefconfig',
74 use="Use the given Python configuration script."
75 "The configuration script is loaded after arguments have been "
76 "processed. Each command-line option sets a configuration "
77 "variable named after its long option name, and these "
78 "variables are placed in the environment of the script before "
79 "it is loaded. Variables for options that may be repeated have "
80 "list values. Other variables can be set using VAR=VAL on the "
81 "command line. "
82 "After the script is loaded, option values that were not set "
83 "on the command line are replaced by the values set in the script.")
85 gopts.default('defconfig')
87 gopts.opt('config', short='F', val='FILE',
88 fn=set_value, default=None,
89 use="Domain configuration to use (SXP).\n"
90 "SXP is the underlying configuration format used by Xen.\n"
91 "SXP configurations can be hand-written or generated from Python "
92 "configuration scripts, using the -n (dryrun) option to print "
93 "the configuration.")
95 gopts.opt('dryrun', short='n',
96 fn=set_true, default=0,
97 use="Dry run - prints the resulting configuration in SXP but "
98 "does not create the domain.")
100 gopts.opt('paused', short='p',
101 fn=set_true, default=0,
102 use='Leave the domain paused after it is created.')
104 gopts.opt('console_autoconnect', short='c',
105 fn=set_true, default=0,
106 use="Connect to the console after the domain is created.")
108 gopts.var('vncpasswd', val='NAME',
109 fn=set_value, default=None,
110 use="Password for VNC console on HVM domain.")
112 gopts.var('vncviewer', val='no|yes',
113 fn=set_bool, default=None,
114 use="Spawn a vncviewer listening for a vnc server in the domain.\n"
115 "The address of the vncviewer is passed to the domain on the "
116 "kernel command line using 'VNC_SERVER=<host>:<port>'. The port "
117 "used by vnc is 5500 + DISPLAY. A display value with a free port "
118 "is chosen if possible.\nOnly valid when vnc=1.")
120 gopts.var('vncconsole', val='no|yes',
121 fn=set_bool, default=None,
122 use="Spawn a vncviewer process for the domain's graphical console.\n"
123 "Only valid when vnc=1.")
125 gopts.var('name', val='NAME',
126 fn=set_value, default=None,
127 use="Domain name. Must be unique.")
129 gopts.var('bootloader', val='FILE',
130 fn=set_value, default=None,
131 use="Path to bootloader.")
133 gopts.var('bootargs', val='NAME',
134 fn=set_value, default=None,
135 use="Arguments to pass to boot loader")
137 gopts.var('bootentry', val='NAME',
138 fn=set_value, default=None,
139 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
141 gopts.var('kernel', val='FILE',
142 fn=set_value, default=None,
143 use="Path to kernel image.")
145 gopts.var('ramdisk', val='FILE',
146 fn=set_value, default='',
147 use="Path to ramdisk.")
149 gopts.var('features', val='FEATURES',
150 fn=set_value, default='',
151 use="Features to enable in guest kernel")
153 gopts.var('builder', val='FUNCTION',
154 fn=set_value, default='linux',
155 use="Function to use to build the domain.")
157 gopts.var('memory', val='MEMORY',
158 fn=set_int, default=128,
159 use="Domain memory in MB.")
161 gopts.var('maxmem', val='MEMORY',
162 fn=set_int, default=None,
163 use="Maximum domain memory in MB.")
165 gopts.var('shadow_memory', val='MEMORY',
166 fn=set_int, default=0,
167 use="Domain shadow memory in MB.")
169 gopts.var('cpu', val='CPU',
170 fn=set_int, default=None,
171 use="CPU to run the VCPU0 on.")
173 gopts.var('cpus', val='CPUS',
174 fn=set_value, default=None,
175 use="CPUS to run the domain on.")
177 gopts.var('pae', val='PAE',
178 fn=set_int, default=1,
179 use="Disable or enable PAE of HVM domain.")
181 gopts.var('acpi', val='ACPI',
182 fn=set_int, default=1,
183 use="Disable or enable ACPI of HVM domain.")
185 gopts.var('apic', val='APIC',
186 fn=set_int, default=1,
187 use="Disable or enable APIC mode.")
189 gopts.var('vcpus', val='VCPUS',
190 fn=set_int, default=1,
191 use="# of Virtual CPUS in domain.")
193 gopts.var('cpu_cap', val='CAP',
194 fn=set_int, default=None,
195 use="""Set the maximum amount of cpu.
196 CAP is a percentage that fixes the maximum amount of cpu.""")
198 gopts.var('cpu_weight', val='WEIGHT',
199 fn=set_int, default=None,
200 use="""Set the cpu time ratio to be allocated to the domain.""")
202 gopts.var('restart', val='onreboot|always|never',
203 fn=set_value, default=None,
204 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
205 instead.
207 Whether the domain should be restarted on exit.
208 - onreboot: restart on exit with shutdown code reboot
209 - always: always restart on exit, ignore exit code
210 - never: never restart on exit, ignore exit code""")
212 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
213 fn=set_value, default=None,
214 use="""Behaviour when a domain exits with reason 'poweroff'.
215 - destroy: the domain is cleaned up as normal;
216 - restart: a new domain is started in place of the old one;
217 - preserve: no clean-up is done until the domain is manually
218 destroyed (using xm destroy, for example);
219 - rename-restart: the old domain is not cleaned up, but is
220 renamed and a new domain started in its place.
221 """)
223 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
224 fn=set_value, default=None,
225 use="""Behaviour when a domain exits with reason 'reboot'.
226 - destroy: the domain is cleaned up as normal;
227 - restart: a new domain is started in place of the old one;
228 - preserve: no clean-up is done until the domain is manually
229 destroyed (using xm destroy, for example);
230 - rename-restart: the old domain is not cleaned up, but is
231 renamed and a new domain started in its place.
232 """)
234 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
235 fn=set_value, default=None,
236 use="""Behaviour when a domain exits with reason 'crash'.
237 - destroy: the domain is cleaned up as normal;
238 - restart: a new domain is started in place of the old one;
239 - preserve: no clean-up is done until the domain is manually
240 destroyed (using xm destroy, for example);
241 - rename-restart: the old domain is not cleaned up, but is
242 renamed and a new domain started in its place.
243 """)
245 gopts.var('blkif', val='no|yes',
246 fn=set_bool, default=0,
247 use="Make the domain a block device backend.")
249 gopts.var('netif', val='no|yes',
250 fn=set_bool, default=0,
251 use="Make the domain a network interface backend.")
253 gopts.var('tpmif', val='no|yes',
254 fn=append_value, default=0,
255 use="Make the domain a TPM interface backend.")
257 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
258 fn=append_value, default=[],
259 use="""Add a disk device to a domain. The physical device is DEV,
260 which is exported to the domain as VDEV. The disk is read-only if MODE
261 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
262 backend driver domain to use for the disk.
263 The option may be repeated to add more than one disk.""")
265 gopts.var('pci', val='BUS:DEV.FUNC',
266 fn=append_value, default=[],
267 use="""Add a PCI device to a domain, using given params (in hex).
268 For example 'pci=c0:02.1a'.
269 The option may be repeated to add more than one pci device.""")
271 gopts.var('ioports', val='FROM[-TO]',
272 fn=append_value, default=[],
273 use="""Add a legacy I/O range to a domain, using given params (in hex).
274 For example 'ioports=02f8-02ff'.
275 The option may be repeated to add more than one i/o range.""")
277 gopts.var('irq', val='IRQ',
278 fn=append_value, default=[],
279 use="""Add an IRQ (interrupt line) to a domain.
280 For example 'irq=7'.
281 This option may be repeated to add more than one IRQ.""")
283 gopts.var('usbport', val='PATH',
284 fn=append_value, default=[],
285 use="""Add a physical USB port to a domain, as specified by the path
286 to that port. This option may be repeated to add more than one port.""")
288 gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY,vncpasswd=PASSWORD",
289 fn=append_value, default=[],
290 use="""Make the domain a framebuffer backend.
291 The backend type should be either sdl or vnc.
292 For type=vnc, connect an external vncviewer. The server will listen
293 on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
294 domain id. If vncunused=1, the server will try to find an arbitrary
295 unused port above 5900.
296 For type=sdl, a viewer will be started automatically using the
297 given DISPLAY and XAUTHORITY, which default to the current user's
298 ones.""")
300 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
301 fn=append_value, default=[],
302 use="""Add a network interface with the given MAC address and bridge.
303 The vif is configured by calling the given configuration script.
304 If type is not specified, default is netfront.
305 If mac is not specified a random MAC address is used.
306 If not specified then the network backend chooses it's own MAC address.
307 If bridge is not specified the first bridge found is used.
308 If script is not specified the default script is used.
309 If backend is not specified the default backend driver domain is used.
310 If vifname is not specified the backend virtual interface will have name vifD.N
311 where D is the domain id and N is the interface id.
312 This option may be repeated to add more than one vif.
313 Specifying vifs will increase the number of interfaces as needed.""")
315 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
316 fn=append_value, default=[],
317 use="""Add a TPM interface. On the backend side use the given
318 instance as virtual TPM instance. The given number is merely the
319 preferred instance number. The hotplug script will determine
320 which instance number will actually be assigned to the domain.
321 The associtation between virtual machine and the TPM instance
322 number can be found in /etc/xen/vtpm.db. Use the backend in the
323 given domain.
324 The type parameter can be used to select a specific driver type
325 that the VM can use. To prevent a fully virtualized domain (HVM)
326 from being able to access an emulated device model, you may specify
327 'paravirtualized' here.""")
329 gopts.var('access_control', val="policy=POLICY,label=LABEL",
330 fn=append_value, default=[],
331 use="""Add a security label and the security policy reference that defines it.
332 The local ssid reference is calculated when starting/resuming the domain. At
333 this time, the policy is checked against the active policy as well. This way,
334 migrating through save/restore is covered and local labels are automatically
335 created correctly on the system where a domain is started / resumed.""")
337 gopts.var('nics', val="NUM",
338 fn=set_int, default=-1,
339 use="""DEPRECATED. Use empty vif entries instead.
341 Set the number of network interfaces.
342 Use the vif option to define interface parameters, otherwise
343 defaults are used. Specifying vifs will increase the
344 number of interfaces as needed.""")
346 gopts.var('root', val='DEVICE',
347 fn=set_value, default='',
348 use="""Set the root= parameter on the kernel command line.
349 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
351 gopts.var('extra', val="ARGS",
352 fn=set_value, default='',
353 use="Set extra arguments to append to the kernel command line.")
355 gopts.var('ip', val='IPADDR',
356 fn=set_value, default='',
357 use="Set the kernel IP interface address.")
359 gopts.var('gateway', val="IPADDR",
360 fn=set_value, default='',
361 use="Set the kernel IP gateway.")
363 gopts.var('netmask', val="MASK",
364 fn=set_value, default = '',
365 use="Set the kernel IP netmask.")
367 gopts.var('hostname', val="NAME",
368 fn=set_value, default='',
369 use="Set the kernel IP hostname.")
371 gopts.var('interface', val="INTF",
372 fn=set_value, default="eth0",
373 use="Set the kernel IP interface name.")
375 gopts.var('dhcp', val="off|dhcp",
376 fn=set_value, default='off',
377 use="Set the kernel dhcp option.")
379 gopts.var('nfs_server', val="IPADDR",
380 fn=set_value, default=None,
381 use="Set the address of the NFS server for NFS root.")
383 gopts.var('nfs_root', val="PATH",
384 fn=set_value, default=None,
385 use="Set the path of the root NFS directory.")
387 gopts.var('device_model', val='FILE',
388 fn=set_value, default='',
389 use="Path to device model program.")
391 gopts.var('fda', val='FILE',
392 fn=set_value, default='',
393 use="Path to fda")
395 gopts.var('fdb', val='FILE',
396 fn=set_value, default='',
397 use="Path to fdb")
399 gopts.var('serial', val='FILE',
400 fn=set_value, default='',
401 use="Path to serial or pty or vc")
403 gopts.var('localtime', val='no|yes',
404 fn=set_bool, default=0,
405 use="Is RTC set to localtime?")
407 gopts.var('keymap', val='FILE',
408 fn=set_value, default='',
409 use="Set keyboard layout used")
411 gopts.var('usb', val='no|yes',
412 fn=set_bool, default=0,
413 use="Emulate USB devices?")
415 gopts.var('usbdevice', val='NAME',
416 fn=set_value, default='',
417 use="Name of USB device to add?")
419 gopts.var('stdvga', val='no|yes',
420 fn=set_bool, default=0,
421 use="Use std vga or cirrhus logic graphics")
423 gopts.var('isa', val='no|yes',
424 fn=set_bool, default=0,
425 use="Simulate an ISA only system?")
427 gopts.var('boot', val="a|b|c|d",
428 fn=set_value, default='c',
429 use="Default boot device")
431 gopts.var('nographic', val='no|yes',
432 fn=set_bool, default=0,
433 use="Should device models use graphics?")
435 gopts.var('soundhw', val='audiodev',
436 fn=set_value, default='',
437 use="Should device models enable audio device?")
439 gopts.var('vnc', val='',
440 fn=set_value, default=None,
441 use="""Should the device model use VNC?""")
443 gopts.var('vncdisplay', val='',
444 fn=set_value, default=None,
445 use="""VNC display to use""")
447 gopts.var('vnclisten', val='',
448 fn=set_value, default=None,
449 use="""Address for VNC server to listen on.""")
451 gopts.var('vncunused', val='',
452 fn=set_bool, default=1,
453 use="""Try to find an unused port for the VNC server.
454 Only valid when vnc=1.""")
456 gopts.var('sdl', val='',
457 fn=set_value, default=None,
458 use="""Should the device model use SDL?""")
460 gopts.var('display', val='DISPLAY',
461 fn=set_value, default=None,
462 use="X11 display to use")
464 gopts.var('xauthority', val='XAUTHORITY',
465 fn=set_value, default=None,
466 use="X11 Authority to use")
468 gopts.var('uuid', val='',
469 fn=set_value, default=None,
470 use="""xenstore UUID (universally unique identifier) to use. One
471 will be randomly generated if this option is not set, just like MAC
472 addresses for virtual network interfaces. This must be a unique
473 value across the entire cluster.""")
475 gopts.var('on_xend_start', val='ignore|start',
476 fn=set_value, default='ignore',
477 use='Action to perform when xend starts')
479 gopts.var('on_xend_stop', val='continue|shutdown|suspend',
480 fn=set_value, default="ignore",
481 use="""Behaviour when Xend stops:
482 - ignore: Domain continues to run;
483 - shutdown: Domain is shutdown;
484 - suspend: Domain is suspended;
485 """)
487 def err(msg):
488 """Print an error to stderr and exit.
489 """
490 print >>sys.stderr, "Error:", msg
491 sys.exit(1)
494 def warn(msg):
495 """Print a warning to stdout.
496 """
497 print >>sys.stderr, "Warning:", msg
500 def strip(pre, s):
501 """Strip prefix 'pre' if present.
502 """
503 if s.startswith(pre):
504 return s[len(pre):]
505 else:
506 return s
508 def configure_image(vals):
509 """Create the image config.
510 """
511 if not vals.builder:
512 return None
513 config_image = [ vals.builder ]
514 if vals.kernel:
515 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
516 if vals.ramdisk:
517 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
518 if vals.cmdline_ip:
519 cmdline_ip = strip('ip=', vals.cmdline_ip)
520 config_image.append(['ip', cmdline_ip])
521 if vals.root:
522 cmdline_root = strip('root=', vals.root)
523 config_image.append(['root', cmdline_root])
524 if vals.extra:
525 config_image.append(['args', vals.extra])
527 if vals.builder == 'hvm':
528 configure_hvm(config_image, vals)
530 return config_image
532 def configure_disks(config_devs, vals):
533 """Create the config for disks (virtual block devices).
534 """
535 for (uname, dev, mode, backend) in vals.disk:
536 if uname.startswith('tap:'):
537 cls = 'tap'
538 else:
539 cls = 'vbd'
541 config_vbd = [cls,
542 ['uname', uname],
543 ['dev', dev ],
544 ['mode', mode ] ]
545 if backend:
546 config_vbd.append(['backend', backend])
547 config_devs.append(['device', config_vbd])
549 def configure_pci(config_devs, vals):
550 """Create the config for pci devices.
551 """
552 config_pci = []
553 for (domain, bus, slot, func) in vals.pci:
554 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
555 ['slot', slot], ['func', func]])
557 if len(config_pci)>0:
558 config_pci.insert(0, 'pci')
559 config_devs.append(['device', config_pci])
561 def configure_ioports(config_devs, vals):
562 """Create the config for legacy i/o ranges.
563 """
564 for (io_from, io_to) in vals.ioports:
565 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
566 config_devs.append(['device', config_ioports])
568 def configure_irq(config_devs, vals):
569 """Create the config for irqs.
570 """
571 for irq in vals.irq:
572 config_irq = ['irq', ['irq', irq]]
573 config_devs.append(['device', config_irq])
575 def configure_usb(config_devs, vals):
576 for path in vals.usbport:
577 config_usb = ['usbport', ['path', path]]
578 config_devs.append(['device', config_usb])
580 def configure_vfbs(config_devs, vals):
581 for f in vals.vfb:
582 d = comma_sep_kv_to_dict(f)
583 config = ['vfb']
584 if not d.has_key("type"):
585 d['type'] = 'sdl'
586 for (k,v) in d.iteritems():
587 if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
588 'xauthority', 'type', 'vncpasswd' ]:
589 err("configuration option %s unknown to vfbs" % k)
590 config.append([k,v])
591 if not d.has_key("display") and os.environ.has_key("DISPLAY"):
592 config.append(["display", os.environ['DISPLAY']])
593 if not d.has_key("xauthority"):
594 config.append(["xauthority", get_xauthority()])
595 config_devs.append(['device', ['vkbd']])
596 config_devs.append(['device', config])
598 def configure_security(config, vals):
599 """Create the config for ACM security labels.
600 """
601 access_control = vals.access_control
602 num = len(access_control)
603 if num == 1:
604 d = access_control[0]
605 policy = d.get('policy')
606 label = d.get('label')
607 if policy != security.active_policy:
608 err("Security policy (" + policy + ") incompatible with enforced policy ("
609 + security.active_policy + ")." )
610 config_access_control = ['access_control',
611 ['policy', policy],
612 ['label', label] ]
614 #ssidref cannot be specified together with access_control
615 if sxp.child_value(config, 'ssidref'):
616 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
617 #else calculate ssidre from label
618 ssidref = security.label2ssidref(label, policy, 'dom')
619 if not ssidref :
620 err("ERROR calculating ssidref from access_control.")
621 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
622 config.append(security_label)
623 elif num == 0:
624 if hasattr(vals, 'ssidref'):
625 if not security.on():
626 err("ERROR: Security ssidref specified but no policy active.")
627 ssidref = getattr(vals, 'ssidref')
628 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
629 config.append(security_label)
630 elif num > 1:
631 err("VM config error: Multiple access_control definitions!")
634 def configure_vtpm(config_devs, vals):
635 """Create the config for virtual TPM interfaces.
636 """
637 vtpm = vals.vtpm
638 if len(vtpm) > 0:
639 d = vtpm[0]
640 instance = d.get('instance')
641 if instance == "VTPMD":
642 instance = "0"
643 else:
644 if instance != None:
645 try:
646 if int(instance) == 0:
647 err('VM config error: vTPM instance must not be 0.')
648 except ValueError:
649 err('Vm config error: could not parse instance number.')
650 backend = d.get('backend')
651 typ = d.get('type')
652 config_vtpm = ['vtpm']
653 if instance:
654 config_vtpm.append(['pref_instance', instance])
655 if backend:
656 config_vtpm.append(['backend', backend])
657 if typ:
658 config_vtpm.append(['type', type])
659 config_devs.append(['device', config_vtpm])
662 def configure_vifs(config_devs, vals):
663 """Create the config for virtual network interfaces.
664 """
666 vifs = vals.vif
667 vifs_n = len(vifs)
669 if hasattr(vals, 'nics'):
670 if vals.nics > 0:
671 warn("The nics option is deprecated. Please use an empty vif "
672 "entry instead:\n\n vif = [ '' ]\n")
673 for _ in range(vifs_n, vals.nics):
674 vifs.append('')
675 vifs_n = len(vifs)
676 elif vals.nics == 0:
677 warn("The nics option is deprecated. Please remove it.")
679 for c in vifs:
680 d = comma_sep_kv_to_dict(c)
681 config_vif = ['vif']
683 def f(k):
684 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
685 'vifname', 'rate', 'model']:
686 err('Invalid vif option: ' + k)
688 config_vif.append([k, d[k]])
690 map(f, d.keys())
691 config_devs.append(['device', config_vif])
694 def configure_hvm(config_image, vals):
695 """Create the config for HVM devices.
696 """
697 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
698 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
699 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
700 'sdl', 'display', 'xauthority',
701 'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
702 for a in args:
703 if a in vals.__dict__ and vals.__dict__[a] is not None:
704 config_image.append([a, vals.__dict__[a]])
705 config_image.append(['vncpasswd', vals.vncpasswd])
707 def run_bootloader(vals, config_image):
708 if not os.access(vals.bootloader, os.F_OK):
709 err("Bootloader '%s' does not exist" % vals.bootloader)
710 if not os.access(vals.bootloader, os.X_OK):
711 err("Bootloader '%s' isn't executable" % vals.bootloader)
712 if len(vals.disk) < 1:
713 err("No disks configured and boot loader requested")
714 (uname, dev, mode, backend) = vals.disk[0]
715 file = blkif.blkdev_uname_to_file(uname)
717 if vals.bootentry:
718 warn("The bootentry option is deprecated. Use bootargs and pass "
719 "--entry= directly.")
720 vals.bootargs = "--entry=%s" %(vals.bootentry,)
722 kernel = sxp.child_value(config_image, 'kernel')
723 ramdisk = sxp.child_value(config_image, 'ramdisk')
724 args = sxp.child_value(config_image, 'args')
725 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
726 vals.bootargs, kernel, ramdisk, args)
728 def make_config(vals):
729 """Create the domain configuration.
730 """
732 config = ['vm']
734 def add_conf(n):
735 if hasattr(vals, n):
736 v = getattr(vals, n)
737 if v:
738 config.append([n, v])
740 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
741 'restart', 'on_poweroff',
742 'on_reboot', 'on_crash', 'vcpus', 'features',
743 'on_xend_start', 'on_xend_stop'])
745 if vals.uuid is not None:
746 config.append(['uuid', vals.uuid])
747 if vals.cpu is not None:
748 config.append(['cpu', vals.cpu])
749 if vals.cpus is not None:
750 config.append(['cpus', vals.cpus])
751 if vals.cpu_cap is not None:
752 config.append(['cpu_cap', vals.cpu_cap])
753 if vals.cpu_weight is not None:
754 config.append(['cpu_weight', vals.cpu_weight])
755 if vals.blkif:
756 config.append(['backend', ['blkif']])
757 if vals.netif:
758 config.append(['backend', ['netif']])
759 if vals.tpmif:
760 config.append(['backend', ['tpmif']])
761 if vals.localtime:
762 config.append(['localtime', vals.localtime])
764 config_image = configure_image(vals)
765 if vals.bootloader:
766 if vals.bootloader == "pygrub":
767 vals.bootloader = osdep.pygrub_path
769 # if a kernel is specified, we're using the bootloader
770 # non-interactively, and need to let xend run it so we preserve the
771 # real kernel choice.
772 if not vals.kernel:
773 config_image = run_bootloader(vals, config_image)
774 config.append(['bootloader', vals.bootloader])
775 if vals.bootargs:
776 config.append(['bootloader_args', vals.bootargs])
777 config.append(['image', config_image])
779 config_devs = []
780 configure_disks(config_devs, vals)
781 configure_pci(config_devs, vals)
782 configure_ioports(config_devs, vals)
783 configure_irq(config_devs, vals)
784 configure_vifs(config_devs, vals)
785 configure_usb(config_devs, vals)
786 configure_vtpm(config_devs, vals)
787 configure_vfbs(config_devs, vals)
788 configure_security(config, vals)
789 config += config_devs
791 return config
793 def preprocess_disk(vals):
794 if not vals.disk: return
795 disk = []
796 for v in vals.disk:
797 d = v.split(',')
798 n = len(d)
799 if n == 3:
800 d.append(None)
801 elif n == 4:
802 pass
803 else:
804 err('Invalid disk specifier: ' + v)
805 disk.append(d)
806 vals.disk = disk
808 def preprocess_pci(vals):
809 if not vals.pci: return
810 pci = []
811 for pci_dev_str in vals.pci:
812 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
813 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
814 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
815 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
816 if pci_match!=None:
817 pci_dev_info = pci_match.groupdict('0')
818 try:
819 pci.append( ('0x'+pci_dev_info['domain'], \
820 '0x'+pci_dev_info['bus'], \
821 '0x'+pci_dev_info['slot'], \
822 '0x'+pci_dev_info['func']))
823 except IndexError:
824 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
825 vals.pci = pci
827 def preprocess_ioports(vals):
828 if not vals.ioports: return
829 ioports = []
830 for v in vals.ioports:
831 d = v.split('-')
832 if len(d) < 1 or len(d) > 2:
833 err('Invalid i/o port range specifier: ' + v)
834 if len(d) == 1:
835 d.append(d[0])
836 # Components are in hex: add hex specifier.
837 hexd = ['0x' + x for x in d]
838 ioports.append(hexd)
839 vals.ioports = ioports
841 def preprocess_vtpm(vals):
842 if not vals.vtpm: return
843 vtpms = []
844 for vtpm in vals.vtpm:
845 d = {}
846 a = vtpm.split(',')
847 for b in a:
848 (k, v) = b.strip().split('=', 1)
849 k = k.strip()
850 v = v.strip()
851 if k not in ['backend', 'instance']:
852 err('Invalid vtpm specifier: ' + vtpm)
853 d[k] = v
854 vtpms.append(d)
855 vals.vtpm = vtpms
857 def preprocess_access_control(vals):
858 if not vals.access_control:
859 return
860 access_controls = []
861 num = len(vals.access_control)
862 if num == 1:
863 access_control = (vals.access_control)[0]
864 d = {}
865 a = access_control.split(',')
866 if len(a) > 2:
867 err('Too many elements in access_control specifier: ' + access_control)
868 for b in a:
869 (k, v) = b.strip().split('=', 1)
870 k = k.strip()
871 v = v.strip()
872 if k not in ['policy','label']:
873 err('Invalid access_control specifier: ' + access_control)
874 d[k] = v
875 access_controls.append(d)
876 vals.access_control = access_controls
877 elif num > 1:
878 err('Multiple access_control definitions.')
880 def preprocess_ip(vals):
881 if vals.ip or vals.dhcp != 'off':
882 dummy_nfs_server = '1.2.3.4'
883 ip = (vals.ip
884 + ':' + (vals.nfs_server or dummy_nfs_server)
885 + ':' + vals.gateway
886 + ':' + vals.netmask
887 + ':' + vals.hostname
888 + ':' + vals.interface
889 + ':' + vals.dhcp)
890 else:
891 ip = ''
892 vals.cmdline_ip = ip
894 def preprocess_nfs(vals):
895 if not vals.nfs_root: return
896 if not vals.nfs_server:
897 err('Must set nfs root and nfs server')
898 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
899 vals.extra = nfs + ' ' + vals.extra
902 def get_host_addr():
903 host = socket.gethostname()
904 addr = socket.gethostbyname(host)
905 return addr
907 VNC_BASE_PORT = 5500
909 def choose_vnc_display():
910 """Try to choose a free vnc display.
911 """
912 def netstat_local_ports():
913 """Run netstat to get a list of the local ports in use.
914 """
915 l = os.popen("netstat -nat").readlines()
916 r = []
917 # Skip 2 lines of header.
918 for x in l[2:]:
919 # Local port is field 3.
920 y = x.split()[3]
921 # Field is addr:port, split off the port.
922 y = y.split(':')[-1]
923 r.append(int(y))
924 return r
926 ports = netstat_local_ports()
927 for d in range(1, 100):
928 port = VNC_BASE_PORT + d
929 if port in ports: continue
930 return d
931 return None
932 vncpid = None
934 def daemonize(prog, args):
935 """Runs a program as a daemon with the list of arguments. Returns the PID
936 of the daemonized program, or returns 0 on error.
937 """
938 r, w = os.pipe()
939 pid = os.fork()
941 if pid == 0:
942 os.close(r)
943 w = os.fdopen(w, 'w')
944 os.setsid()
945 try:
946 pid2 = os.fork()
947 except:
948 pid2 = None
949 if pid2 == 0:
950 os.chdir("/")
951 for fd in range(0, 256):
952 try:
953 os.close(fd)
954 except:
955 pass
956 os.open("/dev/null", os.O_RDWR)
957 os.dup2(0, 1)
958 os.dup2(0, 2)
959 os.execvp(prog, args)
960 os._exit(1)
961 else:
962 w.write(str(pid2 or 0))
963 w.close()
964 os._exit(0)
965 os.close(w)
966 r = os.fdopen(r)
967 daemon_pid = int(r.read())
968 r.close()
969 os.waitpid(pid, 0)
970 return daemon_pid
972 def spawn_vnc(display):
973 """Spawns a vncviewer that listens on the specified display. On success,
974 returns the port that the vncviewer is listening on and sets the global
975 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
976 """
977 vncargs = (["vncviewer", "-log", "*:stdout:0",
978 "-listen", "%d" % (VNC_BASE_PORT + display) ])
979 global vncpid
980 vncpid = daemonize("vncviewer", vncargs)
981 if vncpid == 0:
982 return 0
984 return VNC_BASE_PORT + display
986 def preprocess_vnc(vals):
987 """If vnc was specified, spawn a vncviewer in listen mode
988 and pass its address to the domain on the kernel command line.
989 """
990 if vals.dryrun: return
991 if vals.vncviewer:
992 vnc_display = choose_vnc_display()
993 if not vnc_display:
994 warn("No free vnc display")
995 return
996 print 'VNC=', vnc_display
997 vnc_port = spawn_vnc(vnc_display)
998 if vnc_port > 0:
999 vnc_host = get_host_addr()
1000 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
1001 vals.extra = vnc + ' ' + vals.extra
1003 def preprocess(vals):
1004 preprocess_disk(vals)
1005 preprocess_pci(vals)
1006 preprocess_ioports(vals)
1007 preprocess_ip(vals)
1008 preprocess_nfs(vals)
1009 preprocess_vnc(vals)
1010 preprocess_vtpm(vals)
1011 preprocess_access_control(vals)
1014 def comma_sep_kv_to_dict(c):
1015 """Convert comma-separated, equals-separated key-value pairs into a
1016 dictionary.
1017 """
1018 d = {}
1019 c = c.strip()
1020 if len(c) > 0:
1021 a = c.split(',')
1022 for b in a:
1023 if b.find('=') == -1:
1024 err("%s should be a pair, separated by an equals sign." % b)
1025 (k, v) = b.split('=', 1)
1026 k = k.strip()
1027 v = v.strip()
1028 d[k] = v
1029 return d
1032 def make_domain(opts, config):
1033 """Create, build and start a domain.
1035 @param opts: options
1036 @param config: configuration
1037 @return: domain id
1038 @rtype: int
1039 """
1041 try:
1042 dominfo = server.xend.domain.create(config)
1043 except xmlrpclib.Fault, ex:
1044 import signal
1045 if vncpid:
1046 os.kill(vncpid, signal.SIGKILL)
1047 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
1048 err("the domain '%s' does not exist." % ex.faultString)
1049 else:
1050 err("%s" % ex.faultString)
1051 except Exception, ex:
1052 # main.py has good error messages that let the user know what failed.
1053 # unless the error is a create.py specific thing, it should be handled
1054 # at main. The purpose of this general-case 'Exception' handler is to
1055 # clean up create.py specific processes/data but since create.py does
1056 # not know what to do with the error, it should pass it up.
1057 import signal
1058 if vncpid:
1059 os.kill(vncpid, signal.SIGKILL)
1060 raise
1062 dom = sxp.child_value(dominfo, 'name')
1064 try:
1065 server.xend.domain.waitForDevices(dom)
1066 except xmlrpclib.Fault, ex:
1067 server.xend.domain.destroy(dom)
1068 err("%s" % ex.faultString)
1069 except:
1070 server.xend.domain.destroy(dom)
1071 err("Device creation failed for domain %s" % dom)
1073 if not opts.vals.paused:
1074 try:
1075 server.xend.domain.unpause(dom)
1076 except:
1077 server.xend.domain.destroy(dom)
1078 err("Failed to unpause domain %s" % dom)
1079 opts.info("Started domain %s" % (dom))
1080 return int(sxp.child_value(dominfo, 'domid'))
1083 def get_xauthority():
1084 xauth = os.getenv("XAUTHORITY")
1085 if not xauth:
1086 home = os.getenv("HOME")
1087 if not home:
1088 import posix, pwd
1089 home = pwd.getpwuid(posix.getuid())[5]
1090 xauth = home + "/.Xauthority"
1091 return xauth
1094 def parseCommandLine(argv):
1095 gopts.reset()
1096 args = gopts.parse(argv)
1098 if gopts.vals.help or gopts.vals.help_config:
1099 if gopts.vals.help_config:
1100 print gopts.val_usage()
1101 return (None, None)
1103 if not gopts.vals.display:
1104 gopts.vals.display = os.getenv("DISPLAY")
1106 if not gopts.vals.xauthority:
1107 gopts.vals.xauthority = get_xauthority()
1109 # Process remaining args as config variables.
1110 for arg in args:
1111 if '=' in arg:
1112 (var, val) = arg.strip().split('=', 1)
1113 gopts.setvar(var.strip(), val.strip())
1114 if gopts.vals.config:
1115 config = gopts.vals.config
1116 else:
1117 gopts.load_defconfig()
1118 preprocess(gopts.vals)
1119 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1120 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1121 config = make_config(gopts.vals)
1123 return (gopts, config)
1126 def check_domain_label(config, verbose):
1127 """All that we need to check here is that the domain label exists and
1128 is not null when security is on. Other error conditions are
1129 handled when the config file is parsed.
1130 """
1131 answer = 0
1132 default_label = None
1133 secon = 0
1134 if security.on():
1135 default_label = security.ssidref2label(security.NULL_SSIDREF)
1136 secon = 1
1138 # get the domain acm_label
1139 dom_label = None
1140 dom_name = None
1141 for x in sxp.children(config):
1142 if sxp.name(x) == 'security':
1143 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1144 if sxp.name(x) == 'name':
1145 dom_name = sxp.child0(x)
1147 # sanity check on domain label
1148 if verbose:
1149 print "Checking domain:"
1150 if (not secon) and (not dom_label):
1151 answer = 1
1152 if verbose:
1153 print " %s: PERMITTED" % (dom_name)
1154 elif (secon) and (dom_label) and (dom_label != default_label):
1155 answer = 1
1156 if verbose:
1157 print " %s: PERMITTED" % (dom_name)
1158 else:
1159 print " %s: DENIED" % (dom_name)
1160 if not secon:
1161 print " --> Security off, but domain labeled"
1162 else:
1163 print " --> Domain not labeled"
1164 answer = 0
1166 return answer
1168 def config_security_check(config, verbose):
1169 """Checks each resource listed in the config to see if the active
1170 policy will permit creation of a new domain using the config.
1171 Returns 1 if the config passes all tests, otherwise 0.
1172 """
1173 answer = 1
1175 # get the domain acm_label
1176 domain_label = None
1177 domain_policy = None
1178 for x in sxp.children(config):
1179 if sxp.name(x) == 'security':
1180 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1181 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1183 # if no domain label, use default
1184 if not domain_label and security.on():
1185 try:
1186 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1187 except:
1188 import traceback
1189 traceback.print_exc(limit=1)
1190 return 0
1191 domain_policy = 'NULL'
1192 elif not domain_label:
1193 domain_label = ""
1194 domain_policy = 'NULL'
1196 if verbose:
1197 print "Checking resources:"
1199 # build a list of all resources in the config file
1200 resources = []
1201 for x in sxp.children(config):
1202 if sxp.name(x) == 'device':
1203 if sxp.name(sxp.child0(x)) == 'vbd':
1204 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1206 # perform a security check on each resource
1207 for resource in resources:
1208 try:
1209 security.res_security_check(resource, domain_label)
1210 if verbose:
1211 print " %s: PERMITTED" % (resource)
1213 except security.ACMError:
1214 print " %s: DENIED" % (resource)
1215 (res_label, res_policy) = security.get_res_label(resource)
1216 if not res_label:
1217 res_label = ""
1218 print " --> res: %s (%s)" % (str(res_label),
1219 str(res_policy))
1220 print " --> dom: %s (%s)" % (str(domain_label),
1221 str(domain_policy))
1223 answer = 0
1225 return answer
1227 def create_security_check(config):
1228 passed = 0
1229 try:
1230 if check_domain_label(config, verbose=0):
1231 if config_security_check(config, verbose=0):
1232 passed = 1
1233 else:
1234 print "Checking resources: (skipped)"
1235 except security.ACMError:
1236 sys.exit(-1)
1238 return passed
1240 def help():
1241 return str(gopts)
1243 def main(argv):
1244 try:
1245 (opts, config) = parseCommandLine(argv)
1246 except StandardError, ex:
1247 err(str(ex))
1249 if not opts:
1250 return
1252 if type(config) == str:
1253 try:
1254 config = sxp.parse(file(config))[0]
1255 except IOError, exn:
1256 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1258 if opts.vals.dryrun:
1259 PrettyPrint.prettyprint(config)
1260 else:
1261 if not create_security_check(config):
1262 raise security.ACMError('Security Configuration prevents domain from starting')
1263 else:
1264 dom = make_domain(opts, config)
1265 if opts.vals.console_autoconnect:
1266 console.execConsole(dom)
1268 if __name__ == '__main__':
1269 main(sys.argv)