direct-io.hg

view tools/python/xen/xm/create.py @ 15517:b476c756ad4f

xm: Improve help message for vif creation.
Signed-off-by: Masaki Kanno <kanno.masaki@jp.fujitsu.com>
author kfraser@localhost.localdomain
date Tue Jul 10 10:11:14 2007 +0100 (2007-07-10)
parents 7486ab2b6ae6
children d7e3224b661a
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('skipdtd', short='s',
108 fn=set_true, default=0,
109 use="Skip DTD checking - skips checks on XML before creating. "
110 " Experimental. Can decrease create time." )
112 gopts.opt('paused', short='p',
113 fn=set_true, default=0,
114 use='Leave the domain paused after it is created.')
116 gopts.opt('console_autoconnect', short='c',
117 fn=set_true, default=0,
118 use="Connect to the console after the domain is created.")
120 gopts.var('vncpasswd', val='NAME',
121 fn=set_value, default=None,
122 use="Password for VNC console on HVM domain.")
124 gopts.var('vncviewer', val='no|yes',
125 fn=set_bool, default=None,
126 use="Spawn a vncviewer listening for a vnc server in the domain.\n"
127 "The address of the vncviewer is passed to the domain on the "
128 "kernel command line using 'VNC_SERVER=<host>:<port>'. The port "
129 "used by vnc is 5500 + DISPLAY. A display value with a free port "
130 "is chosen if possible.\nOnly valid when vnc=1.")
132 gopts.var('vncconsole', val='no|yes',
133 fn=set_bool, default=None,
134 use="Spawn a vncviewer process for the domain's graphical console.\n"
135 "Only valid when vnc=1.")
137 gopts.var('name', val='NAME',
138 fn=set_value, default=None,
139 use="Domain name. Must be unique.")
141 gopts.var('bootloader', val='FILE',
142 fn=set_value, default=None,
143 use="Path to bootloader.")
145 gopts.var('bootargs', val='NAME',
146 fn=set_value, default=None,
147 use="Arguments to pass to boot loader")
149 gopts.var('bootentry', val='NAME',
150 fn=set_value, default=None,
151 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
153 gopts.var('kernel', val='FILE',
154 fn=set_value, default=None,
155 use="Path to kernel image.")
157 gopts.var('ramdisk', val='FILE',
158 fn=set_value, default='',
159 use="Path to ramdisk.")
161 gopts.var('features', val='FEATURES',
162 fn=set_value, default='',
163 use="Features to enable in guest kernel")
165 gopts.var('builder', val='FUNCTION',
166 fn=set_value, default='linux',
167 use="Function to use to build the domain.")
169 gopts.var('memory', val='MEMORY',
170 fn=set_int, default=128,
171 use="Domain memory in MB.")
173 gopts.var('maxmem', val='MEMORY',
174 fn=set_int, default=None,
175 use="Maximum domain memory in MB.")
177 gopts.var('shadow_memory', val='MEMORY',
178 fn=set_int, default=0,
179 use="Domain shadow memory in MB.")
181 gopts.var('cpu', val='CPU',
182 fn=set_int, default=None,
183 use="CPU to run the VCPU0 on.")
185 gopts.var('cpus', val='CPUS',
186 fn=set_value, default=None,
187 use="CPUS to run the domain on.")
189 gopts.var('rtc_timeoffset', val='RTC_TIMEOFFSET',
190 fn=set_value, default="0",
191 use="Set RTC offset.")
193 gopts.var('pae', val='PAE',
194 fn=set_int, default=1,
195 use="Disable or enable PAE of HVM domain.")
197 gopts.var('acpi', val='ACPI',
198 fn=set_int, default=1,
199 use="Disable or enable ACPI of HVM domain.")
201 gopts.var('apic', val='APIC',
202 fn=set_int, default=1,
203 use="Disable or enable APIC mode.")
205 gopts.var('vcpus', val='VCPUS',
206 fn=set_int, default=1,
207 use="# of Virtual CPUS in domain.")
209 gopts.var('vcpu_avail', val='VCPUS',
210 fn=set_long, default=None,
211 use="Bitmask for virtual CPUs to make available immediately.")
213 gopts.var('cpu_cap', val='CAP',
214 fn=set_int, default=None,
215 use="""Set the maximum amount of cpu.
216 CAP is a percentage that fixes the maximum amount of cpu.""")
218 gopts.var('cpu_weight', val='WEIGHT',
219 fn=set_int, default=None,
220 use="""Set the cpu time ratio to be allocated to the domain.""")
222 gopts.var('restart', val='onreboot|always|never',
223 fn=set_value, default=None,
224 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
225 instead.
227 Whether the domain should be restarted on exit.
228 - onreboot: restart on exit with shutdown code reboot
229 - always: always restart on exit, ignore exit code
230 - never: never restart on exit, ignore exit code""")
232 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
233 fn=set_value, default=None,
234 use="""Behaviour when a domain exits with reason 'poweroff'.
235 - destroy: the domain is cleaned up as normal;
236 - restart: a new domain is started in place of the old one;
237 - preserve: no clean-up is done until the domain is manually
238 destroyed (using xm destroy, for example);
239 - rename-restart: the old domain is not cleaned up, but is
240 renamed and a new domain started in its place.
241 """)
243 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
244 fn=set_value, default=None,
245 use="""Behaviour when a domain exits with reason 'reboot'.
246 - destroy: the domain is cleaned up as normal;
247 - restart: a new domain is started in place of the old one;
248 - preserve: no clean-up is done until the domain is manually
249 destroyed (using xm destroy, for example);
250 - rename-restart: the old domain is not cleaned up, but is
251 renamed and a new domain started in its place.
252 """)
254 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
255 fn=set_value, default=None,
256 use="""Behaviour when a domain exits with reason 'crash'.
257 - destroy: the domain is cleaned up as normal;
258 - restart: a new domain is started in place of the old one;
259 - preserve: no clean-up is done until the domain is manually
260 destroyed (using xm destroy, for example);
261 - rename-restart: the old domain is not cleaned up, but is
262 renamed and a new domain started in its place.
263 """)
265 gopts.var('blkif', val='no|yes',
266 fn=set_bool, default=0,
267 use="Make the domain a block device backend.")
269 gopts.var('netif', val='no|yes',
270 fn=set_bool, default=0,
271 use="Make the domain a network interface backend.")
273 gopts.var('tpmif', val='no|yes',
274 fn=append_value, default=0,
275 use="Make the domain a TPM interface backend.")
277 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
278 fn=append_value, default=[],
279 use="""Add a disk device to a domain. The physical device is DEV,
280 which is exported to the domain as VDEV. The disk is read-only if MODE
281 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
282 backend driver domain to use for the disk.
283 The option may be repeated to add more than one disk.""")
285 gopts.var('pci', val='BUS:DEV.FUNC',
286 fn=append_value, default=[],
287 use="""Add a PCI device to a domain, using given params (in hex).
288 For example 'pci=c0:02.1a'.
289 The option may be repeated to add more than one pci device.""")
291 gopts.var('ioports', val='FROM[-TO]',
292 fn=append_value, default=[],
293 use="""Add a legacy I/O range to a domain, using given params (in hex).
294 For example 'ioports=02f8-02ff'.
295 The option may be repeated to add more than one i/o range.""")
297 gopts.var('irq', val='IRQ',
298 fn=append_value, default=[],
299 use="""Add an IRQ (interrupt line) to a domain.
300 For example 'irq=7'.
301 This option may be repeated to add more than one IRQ.""")
303 gopts.var('usbport', val='PATH',
304 fn=append_value, default=[],
305 use="""Add a physical USB port to a domain, as specified by the path
306 to that port. This option may be repeated to add more than one port.""")
308 gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY,vncpasswd=PASSWORD",
309 fn=append_value, default=[],
310 use="""Make the domain a framebuffer backend.
311 The backend type should be either sdl or vnc.
312 For type=vnc, connect an external vncviewer. The server will listen
313 on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
314 domain id. If vncunused=1, the server will try to find an arbitrary
315 unused port above 5900. vncpasswd overrides the XenD configured
316 default password.
317 For type=sdl, a viewer will be started automatically using the
318 given DISPLAY and XAUTHORITY, which default to the current user's
319 ones.""")
321 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT," + \
322 "backend=DOM,vifname=NAME,rate=RATE,model=MODEL,accel=ACCEL",
323 fn=append_value, default=[],
324 use="""Add a network interface with the given MAC address and bridge.
325 The vif is configured by calling the given configuration script.
326 If type is not specified, default is netfront.
327 If mac is not specified a random MAC address is used.
328 If not specified then the network backend chooses it's own MAC address.
329 If bridge is not specified the first bridge found is used.
330 If script is not specified the default script is used.
331 If backend is not specified the default backend driver domain is used.
332 If vifname is not specified the backend virtual interface will have name vifD.N
333 where D is the domain id and N is the interface id.
334 If rate is not specified the default rate is used.
335 If model is not specified the default model is used.
336 If accel is not specified an accelerator plugin module is not used.
337 This option may be repeated to add more than one vif.
338 Specifying vifs will increase the number of interfaces as needed.""")
340 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
341 fn=append_value, default=[],
342 use="""Add a TPM interface. On the backend side use the given
343 instance as virtual TPM instance. The given number is merely the
344 preferred instance number. The hotplug script will determine
345 which instance number will actually be assigned to the domain.
346 The associtation between virtual machine and the TPM instance
347 number can be found in /etc/xen/vtpm.db. Use the backend in the
348 given domain.
349 The type parameter can be used to select a specific driver type
350 that the VM can use. To prevent a fully virtualized domain (HVM)
351 from being able to access an emulated device model, you may specify
352 'paravirtualized' here.""")
354 gopts.var('access_control', val="policy=POLICY,label=LABEL",
355 fn=append_value, default=[],
356 use="""Add a security label and the security policy reference that defines it.
357 The local ssid reference is calculated when starting/resuming the domain. At
358 this time, the policy is checked against the active policy as well. This way,
359 migrating through save/restore is covered and local labels are automatically
360 created correctly on the system where a domain is started / resumed.""")
362 gopts.var('nics', val="NUM",
363 fn=set_int, default=-1,
364 use="""DEPRECATED. Use empty vif entries instead.
366 Set the number of network interfaces.
367 Use the vif option to define interface parameters, otherwise
368 defaults are used. Specifying vifs will increase the
369 number of interfaces as needed.""")
371 gopts.var('root', val='DEVICE',
372 fn=set_value, default='',
373 use="""Set the root= parameter on the kernel command line.
374 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
376 gopts.var('extra', val="ARGS",
377 fn=set_value, default='',
378 use="Set extra arguments to append to the kernel command line.")
380 gopts.var('ip', val='IPADDR',
381 fn=set_value, default='',
382 use="Set the kernel IP interface address.")
384 gopts.var('gateway', val="IPADDR",
385 fn=set_value, default='',
386 use="Set the kernel IP gateway.")
388 gopts.var('netmask', val="MASK",
389 fn=set_value, default = '',
390 use="Set the kernel IP netmask.")
392 gopts.var('hostname', val="NAME",
393 fn=set_value, default='',
394 use="Set the kernel IP hostname.")
396 gopts.var('interface', val="INTF",
397 fn=set_value, default="eth0",
398 use="Set the kernel IP interface name.")
400 gopts.var('dhcp', val="off|dhcp",
401 fn=set_value, default='off',
402 use="Set the kernel dhcp option.")
404 gopts.var('nfs_server', val="IPADDR",
405 fn=set_value, default=None,
406 use="Set the address of the NFS server for NFS root.")
408 gopts.var('nfs_root', val="PATH",
409 fn=set_value, default=None,
410 use="Set the path of the root NFS directory.")
412 gopts.var('device_model', val='FILE',
413 fn=set_value, default='',
414 use="Path to device model program.")
416 gopts.var('fda', val='FILE',
417 fn=set_value, default='',
418 use="Path to fda")
420 gopts.var('fdb', val='FILE',
421 fn=set_value, default='',
422 use="Path to fdb")
424 gopts.var('serial', val='FILE',
425 fn=set_value, default='',
426 use="Path to serial or pty or vc")
428 gopts.var('monitor', val='no|yes',
429 fn=set_bool, default=0,
430 use="""Should the device model use monitor?""")
432 gopts.var('localtime', val='no|yes',
433 fn=set_bool, default=0,
434 use="Is RTC set to localtime?")
436 gopts.var('keymap', val='FILE',
437 fn=set_value, default='',
438 use="Set keyboard layout used")
440 gopts.var('usb', val='no|yes',
441 fn=set_bool, default=0,
442 use="Emulate USB devices?")
444 gopts.var('usbdevice', val='NAME',
445 fn=set_value, default='',
446 use="Name of USB device to add?")
448 gopts.var('stdvga', val='no|yes',
449 fn=set_bool, default=0,
450 use="Use std vga or cirrhus logic graphics")
452 gopts.var('isa', val='no|yes',
453 fn=set_bool, default=0,
454 use="Simulate an ISA only system?")
456 gopts.var('boot', val="a|b|c|d",
457 fn=set_value, default='c',
458 use="Default boot device")
460 gopts.var('nographic', val='no|yes',
461 fn=set_bool, default=0,
462 use="Should device models use graphics?")
464 gopts.var('soundhw', val='audiodev',
465 fn=set_value, default='',
466 use="Should device models enable audio device?")
468 gopts.var('vnc', val='',
469 fn=set_value, default=None,
470 use="""Should the device model use VNC?""")
472 gopts.var('vncdisplay', val='',
473 fn=set_value, default=None,
474 use="""VNC display to use""")
476 gopts.var('vnclisten', val='',
477 fn=set_value, default=None,
478 use="""Address for VNC server to listen on.""")
480 gopts.var('vncunused', val='',
481 fn=set_bool, default=1,
482 use="""Try to find an unused port for the VNC server.
483 Only valid when vnc=1.""")
485 gopts.var('sdl', val='',
486 fn=set_value, default=None,
487 use="""Should the device model use SDL?""")
489 gopts.var('display', val='DISPLAY',
490 fn=set_value, default=None,
491 use="X11 display to use")
493 gopts.var('xauthority', val='XAUTHORITY',
494 fn=set_value, default=None,
495 use="X11 Authority to use")
497 gopts.var('uuid', val='',
498 fn=set_value, default=None,
499 use="""xenstore UUID (universally unique identifier) to use. One
500 will be randomly generated if this option is not set, just like MAC
501 addresses for virtual network interfaces. This must be a unique
502 value across the entire cluster.""")
504 gopts.var('on_xend_start', val='ignore|start',
505 fn=set_value, default='ignore',
506 use='Action to perform when xend starts')
508 gopts.var('on_xend_stop', val='continue|shutdown|suspend',
509 fn=set_value, default="ignore",
510 use="""Behaviour when Xend stops:
511 - ignore: Domain continues to run;
512 - shutdown: Domain is shutdown;
513 - suspend: Domain is suspended;
514 """)
516 def err(msg):
517 """Print an error to stderr and exit.
518 """
519 print >>sys.stderr, "Error:", msg
520 sys.exit(1)
523 def warn(msg):
524 """Print a warning to stdout.
525 """
526 print >>sys.stderr, "Warning:", msg
529 def strip(pre, s):
530 """Strip prefix 'pre' if present.
531 """
532 if s.startswith(pre):
533 return s[len(pre):]
534 else:
535 return s
537 def configure_image(vals):
538 """Create the image config.
539 """
540 if not vals.builder:
541 return None
542 config_image = [ vals.builder ]
543 if vals.kernel:
544 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
545 if vals.ramdisk:
546 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
547 if vals.cmdline_ip:
548 cmdline_ip = strip('ip=', vals.cmdline_ip)
549 config_image.append(['ip', cmdline_ip])
550 if vals.root:
551 cmdline_root = strip('root=', vals.root)
552 config_image.append(['root', cmdline_root])
553 if vals.extra:
554 config_image.append(['args', vals.extra])
556 if vals.builder == 'hvm':
557 configure_hvm(config_image, vals)
559 return config_image
561 def configure_disks(config_devs, vals):
562 """Create the config for disks (virtual block devices).
563 """
564 for (uname, dev, mode, backend) in vals.disk:
565 if uname.startswith('tap:'):
566 cls = 'tap'
567 else:
568 cls = 'vbd'
570 config_vbd = [cls,
571 ['uname', uname],
572 ['dev', dev ],
573 ['mode', mode ] ]
574 if backend:
575 config_vbd.append(['backend', backend])
576 config_devs.append(['device', config_vbd])
578 def configure_pci(config_devs, vals):
579 """Create the config for pci devices.
580 """
581 config_pci = []
582 for (domain, bus, slot, func) in vals.pci:
583 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
584 ['slot', slot], ['func', func]])
586 if len(config_pci)>0:
587 config_pci.insert(0, 'pci')
588 config_devs.append(['device', config_pci])
590 def configure_ioports(config_devs, vals):
591 """Create the config for legacy i/o ranges.
592 """
593 for (io_from, io_to) in vals.ioports:
594 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
595 config_devs.append(['device', config_ioports])
597 def configure_irq(config_devs, vals):
598 """Create the config for irqs.
599 """
600 for irq in vals.irq:
601 config_irq = ['irq', ['irq', irq]]
602 config_devs.append(['device', config_irq])
604 def configure_usb(config_devs, vals):
605 for path in vals.usbport:
606 config_usb = ['usbport', ['path', path]]
607 config_devs.append(['device', config_usb])
609 def configure_vfbs(config_devs, vals):
610 for f in vals.vfb:
611 d = comma_sep_kv_to_dict(f)
612 config = ['vfb']
613 if not d.has_key("type"):
614 d['type'] = 'sdl'
615 for (k,v) in d.iteritems():
616 if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
617 'xauthority', 'type', 'vncpasswd' ]:
618 err("configuration option %s unknown to vfbs" % k)
619 config.append([k,v])
620 if not d.has_key("keymap"):
621 if vals.keymap:
622 config.append(['keymap',vals.keymap])
623 if not d.has_key("display") and os.environ.has_key("DISPLAY"):
624 config.append(["display", os.environ['DISPLAY']])
625 if not d.has_key("xauthority"):
626 config.append(["xauthority", get_xauthority()])
627 config_devs.append(['device', ['vkbd']])
628 config_devs.append(['device', config])
630 def configure_security(config, vals):
631 """Create the config for ACM security labels.
632 """
633 access_control = vals.access_control
634 num = len(access_control)
635 if num == 1:
636 d = access_control[0]
637 policy = d.get('policy')
638 label = d.get('label')
639 if policy != security.active_policy:
640 err("Security policy (" + policy + ") incompatible with enforced policy ("
641 + security.active_policy + ")." )
642 config_access_control = ['access_control',
643 ['policy', policy],
644 ['label', label] ]
646 #ssidref cannot be specified together with access_control
647 if sxp.child_value(config, 'ssidref'):
648 err("ERROR: SSIDREF and access_control are mutually exclusive but both specified!")
649 #else calculate ssidre from label
650 ssidref = security.label2ssidref(label, policy, 'dom')
651 if not ssidref :
652 err("ERROR calculating ssidref from access_control.")
653 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
654 config.append(security_label)
655 elif num == 0:
656 if hasattr(vals, 'ssidref'):
657 if not security.on():
658 err("ERROR: Security ssidref specified but no policy active.")
659 ssidref = getattr(vals, 'ssidref')
660 security_label = ['security', [ [ 'ssidref' , int(ssidref) ] ] ]
661 config.append(security_label)
662 elif num > 1:
663 err("VM config error: Multiple access_control definitions!")
666 def configure_vtpm(config_devs, vals):
667 """Create the config for virtual TPM interfaces.
668 """
669 vtpm = vals.vtpm
670 if len(vtpm) > 0:
671 d = vtpm[0]
672 instance = d.get('instance')
673 if instance == "VTPMD":
674 instance = "0"
675 else:
676 if instance != None:
677 try:
678 if int(instance) == 0:
679 err('VM config error: vTPM instance must not be 0.')
680 except ValueError:
681 err('Vm config error: could not parse instance number.')
682 backend = d.get('backend')
683 typ = d.get('type')
684 config_vtpm = ['vtpm']
685 if instance:
686 config_vtpm.append(['pref_instance', instance])
687 if backend:
688 config_vtpm.append(['backend', backend])
689 if typ:
690 config_vtpm.append(['type', type])
691 config_devs.append(['device', config_vtpm])
694 def configure_vifs(config_devs, vals):
695 """Create the config for virtual network interfaces.
696 """
698 vifs = vals.vif
699 vifs_n = len(vifs)
701 if hasattr(vals, 'nics'):
702 if vals.nics > 0:
703 warn("The nics option is deprecated. Please use an empty vif "
704 "entry instead:\n\n vif = [ '' ]\n")
705 for _ in range(vifs_n, vals.nics):
706 vifs.append('')
707 vifs_n = len(vifs)
708 elif vals.nics == 0:
709 warn("The nics option is deprecated. Please remove it.")
711 for c in vifs:
712 d = comma_sep_kv_to_dict(c)
713 config_vif = ['vif']
715 def f(k):
716 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
717 'vifname', 'rate', 'model', 'accel']:
718 err('Invalid vif option: ' + k)
720 config_vif.append([k, d[k]])
722 map(f, d.keys())
723 config_devs.append(['device', config_vif])
726 def configure_hvm(config_image, vals):
727 """Create the config for HVM devices.
728 """
729 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
730 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
731 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
732 'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
733 'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
734 for a in args:
735 if a in vals.__dict__ and vals.__dict__[a] is not None:
736 config_image.append([a, vals.__dict__[a]])
737 config_image.append(['vncpasswd', vals.vncpasswd])
740 def make_config(vals):
741 """Create the domain configuration.
742 """
744 config = ['vm']
746 def add_conf(n):
747 if hasattr(vals, n):
748 v = getattr(vals, n)
749 if v:
750 config.append([n, v])
752 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
753 'restart', 'on_poweroff',
754 'on_reboot', 'on_crash', 'vcpus', 'vcpu_avail', 'features',
755 'on_xend_start', 'on_xend_stop'])
757 if vals.uuid is not None:
758 config.append(['uuid', vals.uuid])
759 if vals.cpu is not None:
760 config.append(['cpu', vals.cpu])
761 if vals.cpus is not None:
762 config.append(['cpus', vals.cpus])
763 if vals.cpu_cap is not None:
764 config.append(['cpu_cap', vals.cpu_cap])
765 if vals.cpu_weight is not None:
766 config.append(['cpu_weight', vals.cpu_weight])
767 if vals.blkif:
768 config.append(['backend', ['blkif']])
769 if vals.netif:
770 config.append(['backend', ['netif']])
771 if vals.tpmif:
772 config.append(['backend', ['tpmif']])
773 if vals.localtime:
774 config.append(['localtime', vals.localtime])
776 config_image = configure_image(vals)
777 if vals.bootloader:
778 if vals.bootloader == "pygrub":
779 vals.bootloader = osdep.pygrub_path
781 config.append(['bootloader', vals.bootloader])
782 if vals.bootargs:
783 config.append(['bootloader_args', vals.bootargs])
784 else:
785 if vals.console_autoconnect:
786 config.append(['bootloader_args', ''])
787 else:
788 config.append(['bootloader_args', '-q'])
789 config.append(['image', config_image])
791 config_devs = []
792 configure_disks(config_devs, vals)
793 configure_pci(config_devs, vals)
794 configure_ioports(config_devs, vals)
795 configure_irq(config_devs, vals)
796 configure_vifs(config_devs, vals)
797 configure_usb(config_devs, vals)
798 configure_vtpm(config_devs, vals)
799 configure_vfbs(config_devs, vals)
800 configure_security(config, vals)
801 config += config_devs
803 return config
805 def preprocess_disk(vals):
806 if not vals.disk: return
807 disk = []
808 for v in vals.disk:
809 d = v.split(',')
810 n = len(d)
811 if n == 3:
812 d.append(None)
813 elif n == 4:
814 pass
815 else:
816 err('Invalid disk specifier: ' + v)
817 disk.append(d)
818 vals.disk = disk
820 def preprocess_pci(vals):
821 if not vals.pci: return
822 pci = []
823 for pci_dev_str in vals.pci:
824 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
825 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
826 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
827 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
828 if pci_match!=None:
829 pci_dev_info = pci_match.groupdict('0')
830 try:
831 pci.append( ('0x'+pci_dev_info['domain'], \
832 '0x'+pci_dev_info['bus'], \
833 '0x'+pci_dev_info['slot'], \
834 '0x'+pci_dev_info['func']))
835 except IndexError:
836 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
837 vals.pci = pci
839 def preprocess_ioports(vals):
840 if not vals.ioports: return
841 ioports = []
842 for v in vals.ioports:
843 d = v.split('-')
844 if len(d) < 1 or len(d) > 2:
845 err('Invalid i/o port range specifier: ' + v)
846 if len(d) == 1:
847 d.append(d[0])
848 # Components are in hex: add hex specifier.
849 hexd = ['0x' + x for x in d]
850 ioports.append(hexd)
851 vals.ioports = ioports
853 def preprocess_vtpm(vals):
854 if not vals.vtpm: return
855 vtpms = []
856 for vtpm in vals.vtpm:
857 d = {}
858 a = vtpm.split(',')
859 for b in a:
860 (k, v) = b.strip().split('=', 1)
861 k = k.strip()
862 v = v.strip()
863 if k not in ['backend', 'instance']:
864 err('Invalid vtpm specifier: ' + vtpm)
865 d[k] = v
866 vtpms.append(d)
867 vals.vtpm = vtpms
869 def preprocess_access_control(vals):
870 if not vals.access_control:
871 return
872 access_controls = []
873 num = len(vals.access_control)
874 if num == 1:
875 access_control = (vals.access_control)[0]
876 d = {}
877 a = access_control.split(',')
878 if len(a) > 2:
879 err('Too many elements in access_control specifier: ' + access_control)
880 for b in a:
881 (k, v) = b.strip().split('=', 1)
882 k = k.strip()
883 v = v.strip()
884 if k not in ['policy','label']:
885 err('Invalid access_control specifier: ' + access_control)
886 d[k] = v
887 access_controls.append(d)
888 vals.access_control = access_controls
889 elif num > 1:
890 err('Multiple access_control definitions.')
892 def preprocess_ip(vals):
893 if vals.ip or vals.dhcp != 'off':
894 dummy_nfs_server = '1.2.3.4'
895 ip = (vals.ip
896 + ':' + (vals.nfs_server or dummy_nfs_server)
897 + ':' + vals.gateway
898 + ':' + vals.netmask
899 + ':' + vals.hostname
900 + ':' + vals.interface
901 + ':' + vals.dhcp)
902 else:
903 ip = ''
904 vals.cmdline_ip = ip
906 def preprocess_nfs(vals):
907 if not vals.nfs_root: return
908 if not vals.nfs_server:
909 err('Must set nfs root and nfs server')
910 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
911 vals.extra = nfs + ' ' + vals.extra
914 def get_host_addr():
915 host = socket.gethostname()
916 addr = socket.gethostbyname(host)
917 return addr
919 VNC_BASE_PORT = 5500
921 def choose_vnc_display():
922 """Try to choose a free vnc display.
923 """
924 def netstat_local_ports():
925 """Run netstat to get a list of the local ports in use.
926 """
927 l = os.popen("netstat -nat").readlines()
928 r = []
929 # Skip 2 lines of header.
930 for x in l[2:]:
931 # Local port is field 3.
932 y = x.split()[3]
933 # Field is addr:port, split off the port.
934 y = y.split(':')[-1]
935 r.append(int(y))
936 return r
938 ports = netstat_local_ports()
939 for d in range(1, 100):
940 port = VNC_BASE_PORT + d
941 if port in ports: continue
942 return d
943 return None
944 vncpid = None
946 def daemonize(prog, args):
947 """Runs a program as a daemon with the list of arguments. Returns the PID
948 of the daemonized program, or returns 0 on error.
949 """
950 r, w = os.pipe()
951 pid = os.fork()
953 if pid == 0:
954 os.close(r)
955 w = os.fdopen(w, 'w')
956 os.setsid()
957 try:
958 pid2 = os.fork()
959 except:
960 pid2 = None
961 if pid2 == 0:
962 os.chdir("/")
963 for fd in range(0, 256):
964 try:
965 os.close(fd)
966 except:
967 pass
968 os.open("/dev/null", os.O_RDWR)
969 os.dup2(0, 1)
970 os.dup2(0, 2)
971 os.execvp(prog, args)
972 os._exit(1)
973 else:
974 w.write(str(pid2 or 0))
975 w.close()
976 os._exit(0)
977 os.close(w)
978 r = os.fdopen(r)
979 daemon_pid = int(r.read())
980 r.close()
981 os.waitpid(pid, 0)
982 return daemon_pid
984 def spawn_vnc(display):
985 """Spawns a vncviewer that listens on the specified display. On success,
986 returns the port that the vncviewer is listening on and sets the global
987 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
988 """
989 vncargs = (["vncviewer", "-log", "*:stdout:0",
990 "-listen", "%d" % (VNC_BASE_PORT + display) ])
991 global vncpid
992 vncpid = daemonize("vncviewer", vncargs)
993 if vncpid == 0:
994 return 0
996 return VNC_BASE_PORT + display
998 def preprocess_vnc(vals):
999 """If vnc was specified, spawn a vncviewer in listen mode
1000 and pass its address to the domain on the kernel command line.
1001 """
1002 if vals.dryrun: return
1003 if vals.vncviewer:
1004 vnc_display = choose_vnc_display()
1005 if not vnc_display:
1006 warn("No free vnc display")
1007 return
1008 print 'VNC=', vnc_display
1009 vnc_port = spawn_vnc(vnc_display)
1010 if vnc_port > 0:
1011 vnc_host = get_host_addr()
1012 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
1013 vals.extra = vnc + ' ' + vals.extra
1015 def preprocess(vals):
1016 preprocess_disk(vals)
1017 preprocess_pci(vals)
1018 preprocess_ioports(vals)
1019 preprocess_ip(vals)
1020 preprocess_nfs(vals)
1021 preprocess_vnc(vals)
1022 preprocess_vtpm(vals)
1023 preprocess_access_control(vals)
1026 def comma_sep_kv_to_dict(c):
1027 """Convert comma-separated, equals-separated key-value pairs into a
1028 dictionary.
1029 """
1030 d = {}
1031 c = c.strip()
1032 if len(c) > 0:
1033 a = c.split(',')
1034 for b in a:
1035 if b.find('=') == -1:
1036 err("%s should be a pair, separated by an equals sign." % b)
1037 (k, v) = b.split('=', 1)
1038 k = k.strip()
1039 v = v.strip()
1040 d[k] = v
1041 return d
1044 def make_domain(opts, config):
1045 """Create, build and start a domain.
1047 @param opts: options
1048 @param config: configuration
1049 @return: domain id
1050 @rtype: int
1051 """
1053 try:
1054 dominfo = server.xend.domain.create(config)
1055 except xmlrpclib.Fault, ex:
1056 import signal
1057 if vncpid:
1058 os.kill(vncpid, signal.SIGKILL)
1059 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
1060 err("the domain '%s' does not exist." % ex.faultString)
1061 else:
1062 err("%s" % ex.faultString)
1063 except Exception, ex:
1064 # main.py has good error messages that let the user know what failed.
1065 # unless the error is a create.py specific thing, it should be handled
1066 # at main. The purpose of this general-case 'Exception' handler is to
1067 # clean up create.py specific processes/data but since create.py does
1068 # not know what to do with the error, it should pass it up.
1069 import signal
1070 if vncpid:
1071 os.kill(vncpid, signal.SIGKILL)
1072 raise
1074 dom = sxp.child_value(dominfo, 'name')
1076 try:
1077 server.xend.domain.waitForDevices(dom)
1078 except xmlrpclib.Fault, ex:
1079 server.xend.domain.destroy(dom)
1080 err("%s" % ex.faultString)
1081 except:
1082 server.xend.domain.destroy(dom)
1083 err("Device creation failed for domain %s" % dom)
1085 if not opts.vals.paused:
1086 try:
1087 server.xend.domain.unpause(dom)
1088 except:
1089 server.xend.domain.destroy(dom)
1090 err("Failed to unpause domain %s" % dom)
1091 opts.info("Started domain %s" % (dom))
1092 return int(sxp.child_value(dominfo, 'domid'))
1095 def get_xauthority():
1096 xauth = os.getenv("XAUTHORITY")
1097 if not xauth:
1098 home = os.getenv("HOME")
1099 if not home:
1100 import posix, pwd
1101 home = pwd.getpwuid(posix.getuid())[5]
1102 xauth = home + "/.Xauthority"
1103 return xauth
1106 def parseCommandLine(argv):
1107 gopts.reset()
1108 args = gopts.parse(argv)
1110 if gopts.vals.help or gopts.vals.help_config:
1111 if gopts.vals.help_config:
1112 print gopts.val_usage()
1113 return (None, None)
1115 if not gopts.vals.display:
1116 gopts.vals.display = os.getenv("DISPLAY")
1118 if not gopts.vals.xauthority:
1119 gopts.vals.xauthority = get_xauthority()
1121 gopts.is_xml = False
1123 # Process remaining args as config variables.
1124 for arg in args:
1125 if '=' in arg:
1126 (var, val) = arg.strip().split('=', 1)
1127 gopts.setvar(var.strip(), val.strip())
1128 if gopts.vals.config:
1129 config = gopts.vals.config
1130 else:
1131 try:
1132 gopts.load_defconfig()
1133 preprocess(gopts.vals)
1134 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1135 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1136 config = make_config(gopts.vals)
1137 except XMLFileError, ex:
1138 XMLFile = ex.getFile()
1139 gopts.is_xml = True
1140 config = ex.getFile()
1142 return (gopts, config)
1145 def check_domain_label(config, verbose):
1146 """All that we need to check here is that the domain label exists and
1147 is not null when security is on. Other error conditions are
1148 handled when the config file is parsed.
1149 """
1150 answer = 0
1151 default_label = None
1152 secon = 0
1153 if security.on():
1154 default_label = security.ssidref2label(security.NULL_SSIDREF)
1155 secon = 1
1157 # get the domain acm_label
1158 dom_label = None
1159 dom_name = None
1160 for x in sxp.children(config):
1161 if sxp.name(x) == 'security':
1162 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1163 if sxp.name(x) == 'name':
1164 dom_name = sxp.child0(x)
1166 # sanity check on domain label
1167 if verbose:
1168 print "Checking domain:"
1169 if (not secon) and (not dom_label):
1170 answer = 1
1171 if verbose:
1172 print " %s: PERMITTED" % (dom_name)
1173 elif (secon) and (dom_label) and (dom_label != default_label):
1174 answer = 1
1175 if verbose:
1176 print " %s: PERMITTED" % (dom_name)
1177 else:
1178 print " %s: DENIED" % (dom_name)
1179 if not secon:
1180 print " --> Security off, but domain labeled"
1181 else:
1182 print " --> Domain not labeled"
1183 answer = 0
1185 return answer
1187 def config_security_check(config, verbose):
1188 """Checks each resource listed in the config to see if the active
1189 policy will permit creation of a new domain using the config.
1190 Returns 1 if the config passes all tests, otherwise 0.
1191 """
1192 answer = 1
1194 # get the domain acm_label
1195 domain_label = None
1196 domain_policy = None
1197 for x in sxp.children(config):
1198 if sxp.name(x) == 'security':
1199 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1200 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1202 # if no domain label, use default
1203 if not domain_label and security.on():
1204 try:
1205 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1206 except:
1207 import traceback
1208 traceback.print_exc(limit=1)
1209 return 0
1210 domain_policy = 'NULL'
1211 elif not domain_label:
1212 domain_label = ""
1213 domain_policy = 'NULL'
1215 if verbose:
1216 print "Checking resources:"
1218 # build a list of all resources in the config file
1219 resources = []
1220 for x in sxp.children(config):
1221 if sxp.name(x) == 'device':
1222 if sxp.name(sxp.child0(x)) == 'vbd':
1223 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1225 # perform a security check on each resource
1226 for resource in resources:
1227 try:
1228 security.res_security_check(resource, domain_label)
1229 if verbose:
1230 print " %s: PERMITTED" % (resource)
1232 except security.ACMError:
1233 print " %s: DENIED" % (resource)
1234 (res_label, res_policy) = security.get_res_label(resource)
1235 if not res_label:
1236 res_label = ""
1237 print " --> res: %s (%s)" % (str(res_label),
1238 str(res_policy))
1239 print " --> dom: %s (%s)" % (str(domain_label),
1240 str(domain_policy))
1242 answer = 0
1244 return answer
1246 def create_security_check(config):
1247 passed = 0
1248 try:
1249 if check_domain_label(config, verbose=0):
1250 if config_security_check(config, verbose=0):
1251 passed = 1
1252 else:
1253 print "Checking resources: (skipped)"
1254 except security.ACMError:
1255 sys.exit(-1)
1257 return passed
1259 def help():
1260 return str(gopts)
1262 def main(argv):
1263 is_xml = False
1265 try:
1266 (opts, config) = parseCommandLine(argv)
1267 except StandardError, ex:
1268 err(str(ex))
1270 if not opts:
1271 return
1273 if not opts.is_xml:
1274 if type(config) == str:
1275 try:
1276 config = sxp.parse(file(config))[0]
1277 except IOError, exn:
1278 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1280 if serverType == SERVER_XEN_API:
1281 from xen.xm.xenapi_create import sxp2xml
1282 sxp2xml_inst = sxp2xml()
1283 doc = sxp2xml_inst.convert_sxp_to_xml(config, transient=True)
1285 if opts.vals.dryrun and not opts.is_xml:
1286 SXPPrettyPrint.prettyprint(config)
1288 if opts.vals.xmldryrun and serverType == SERVER_XEN_API:
1289 from xml.dom.ext import PrettyPrint as XMLPrettyPrint
1290 XMLPrettyPrint(doc)
1292 if opts.vals.dryrun or opts.vals.xmldryrun:
1293 return
1295 if opts.vals.console_autoconnect:
1296 do_console(sxp.child_value(config, 'name', -1))
1298 if serverType == SERVER_XEN_API:
1299 from xen.xm.xenapi_create import xenapi_create
1300 xenapi_create_inst = xenapi_create()
1301 if opts.is_xml:
1302 vm_refs = xenapi_create_inst.create(filename = config,
1303 skipdtd = opts.vals.skipdtd)
1304 else:
1305 vm_refs = xenapi_create_inst.create(document = doc,
1306 skipdtd = opts.vals.skipdtd)
1308 map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs)
1309 elif not opts.is_xml:
1310 if not create_security_check(config):
1311 raise security.ACMError(
1312 'Security Configuration prevents domain from starting')
1313 dom = make_domain(opts, config)
1315 def do_console(domain_name):
1316 cpid = os.fork()
1317 if cpid != 0:
1318 for i in range(10):
1319 # Catch failure of the create process
1320 time.sleep(1)
1321 (p, rv) = os.waitpid(cpid, os.WNOHANG)
1322 if os.WIFEXITED(rv):
1323 if os.WEXITSTATUS(rv) != 0:
1324 sys.exit(os.WEXITSTATUS(rv))
1325 try:
1326 # Acquire the console of the created dom
1327 if serverType == SERVER_XEN_API:
1328 domid = server.xenapi.VM.get_domid(
1329 get_single_vm(domain_name))
1330 else:
1331 dom = server.xend.domain(domain_name)
1332 domid = int(sxp.child_value(dom, 'domid', '-1'))
1333 console.execConsole(domid)
1334 except:
1335 pass
1336 print("Could not start console\n");
1337 sys.exit(0)
1339 if __name__ == '__main__':
1340 main(sys.argv)