direct-io.hg

view tools/python/xen/xm/create.py @ 14438:126a2c3c6a9d

Only import xenapi_create when using the Xen-API server -- this has additional
dependencies that we don't want to require in legacy mode.

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