ia64/xen-unstable

view tools/python/xen/xm/create.py @ 18119:34ad9e2c0d10

xend: Fix keymap handling

Patch allows keymap to be specified inside vfb description and
allows VM configured keymap setting to override XenD default

Signed-off-by: Pat Campbell <plc@novell.com>
author Keir Fraser <keir.fraser@citrix.com>
date Mon Jul 21 09:47:07 2008 +0100 (2008-07-21)
parents f40c310dca31
children f150da74abc3
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 vscsi_util
37 import xen.util.xsm.xsm as security
38 from xen.xm.main import serverType, SERVER_XEN_API, get_single_vm
40 from xen.xm.opts import *
42 from main import server
43 import console
46 gopts = Opts(use="""[options] [vars]
48 Create a domain.
50 Domain creation parameters can be set by command-line switches, from
51 a python configuration script or an SXP config file. See documentation
52 for --defconfig, --config. Configuration variables can be set using
53 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
55 """)
57 gopts.opt('help', short='h',
58 fn=set_true, default=0,
59 use="Print this help.")
61 gopts.opt('help_config',
62 fn=set_true, default=0,
63 use="Print the available configuration variables (vars) for the "
64 "configuration script.")
66 gopts.opt('quiet', short='q',
67 fn=set_true, default=0,
68 use="Quiet.")
70 gopts.opt('path', val='PATH',
71 fn=set_value, default='.:/etc/xen',
72 use="Search path for configuration scripts. "
73 "The value of PATH is a colon-separated directory list.")
75 gopts.opt('defconfig', short='f', val='FILE',
76 fn=set_value, default='xmdefconfig',
77 use="Use the given Python configuration script."
78 "The configuration script is loaded after arguments have been "
79 "processed. Each command-line option sets a configuration "
80 "variable named after its long option name, and these "
81 "variables are placed in the environment of the script before "
82 "it is loaded. Variables for options that may be repeated have "
83 "list values. Other variables can be set using VAR=VAL on the "
84 "command line. "
85 "After the script is loaded, option values that were not set "
86 "on the command line are replaced by the values set in the script.")
88 gopts.default('defconfig')
90 gopts.opt('config', short='F', val='FILE',
91 fn=set_value, default=None,
92 use="Domain configuration to use (SXP).\n"
93 "SXP is the underlying configuration format used by Xen.\n"
94 "SXP configurations can be hand-written or generated from Python "
95 "configuration scripts, using the -n (dryrun) option to print "
96 "the configuration.")
98 gopts.opt('dryrun', short='n',
99 fn=set_true, default=0,
100 use="Dry run - prints the resulting configuration in SXP but "
101 "does not create the domain.")
103 gopts.opt('xmldryrun', short='x',
104 fn=set_true, default=0,
105 use="XML dry run - prints the resulting configuration in XML but "
106 "does not create the domain.")
108 gopts.opt('skipdtd', short='s',
109 fn=set_true, default=0,
110 use="Skip DTD checking - skips checks on XML before creating. "
111 " Experimental. Can decrease create time." )
113 gopts.opt('paused', short='p',
114 fn=set_true, default=0,
115 use='Leave the domain paused after it is created.')
117 gopts.opt('console_autoconnect', short='c',
118 fn=set_true, default=0,
119 use="Connect to the console after the domain is created.")
121 gopts.var('vncpasswd', val='NAME',
122 fn=set_value, default=None,
123 use="Password for VNC console on HVM domain.")
125 gopts.var('vncviewer', val='no|yes',
126 fn=set_bool, default=None,
127 use="Spawn a vncviewer listening for a vnc server in the domain.\n"
128 "The address of the vncviewer is passed to the domain on the "
129 "kernel command line using 'VNC_SERVER=<host>:<port>'. The port "
130 "used by vnc is 5500 + DISPLAY. A display value with a free port "
131 "is chosen if possible.\nOnly valid when vnc=1.")
133 gopts.var('vncconsole', val='no|yes',
134 fn=set_bool, default=None,
135 use="Spawn a vncviewer process for the domain's graphical console.\n"
136 "Only valid when vnc=1.")
138 gopts.var('name', val='NAME',
139 fn=set_value, default=None,
140 use="Domain name. Must be unique.")
142 gopts.var('bootloader', val='FILE',
143 fn=set_value, default=None,
144 use="Path to bootloader.")
146 gopts.var('bootargs', val='NAME',
147 fn=set_value, default=None,
148 use="Arguments to pass to boot loader")
150 gopts.var('bootentry', val='NAME',
151 fn=set_value, default=None,
152 use="DEPRECATED. Entry to boot via boot loader. Use bootargs.")
154 gopts.var('kernel', val='FILE',
155 fn=set_value, default=None,
156 use="Path to kernel image.")
158 gopts.var('ramdisk', val='FILE',
159 fn=set_value, default='',
160 use="Path to ramdisk.")
162 gopts.var('loader', val='FILE',
163 fn=set_value, default='',
164 use="Path to HVM firmware.")
166 gopts.var('features', val='FEATURES',
167 fn=set_value, default='',
168 use="Features to enable in guest kernel")
170 gopts.var('builder', val='FUNCTION',
171 fn=set_value, default='linux',
172 use="Function to use to build the domain.")
174 gopts.var('memory', val='MEMORY',
175 fn=set_int, default=128,
176 use="Domain memory in MB.")
178 gopts.var('maxmem', val='MEMORY',
179 fn=set_int, default=None,
180 use="Maximum domain memory in MB.")
182 gopts.var('shadow_memory', val='MEMORY',
183 fn=set_int, default=0,
184 use="Domain shadow memory in MB.")
186 gopts.var('cpu', val='CPU',
187 fn=set_int, default=None,
188 use="CPU to run the VCPU0 on.")
190 gopts.var('cpus', val='CPUS',
191 fn=set_value, default=None,
192 use="CPUS to run the domain on.")
194 gopts.var('rtc_timeoffset', val='RTC_TIMEOFFSET',
195 fn=set_value, default="0",
196 use="Set RTC offset.")
198 gopts.var('pae', val='PAE',
199 fn=set_int, default=1,
200 use="Disable or enable PAE of HVM domain.")
202 gopts.var('hpet', val='HPET',
203 fn=set_int, default=0,
204 use="Enable virtual high-precision event timer.")
206 gopts.var('timer_mode', val='TIMER_MODE',
207 fn=set_int, default=0,
208 use="""Timer mode (0=delay virtual time when ticks are missed;
209 1=virtual time is always wallclock time.""")
211 gopts.var('acpi', val='ACPI',
212 fn=set_int, default=1,
213 use="Disable or enable ACPI of HVM domain.")
215 gopts.var('apic', val='APIC',
216 fn=set_int, default=1,
217 use="Disable or enable APIC mode.")
219 gopts.var('vcpus', val='VCPUS',
220 fn=set_int, default=1,
221 use="# of Virtual CPUS in domain.")
223 gopts.var('vcpu_avail', val='VCPUS',
224 fn=set_long, default=None,
225 use="Bitmask for virtual CPUs to make available immediately.")
227 gopts.var('vhpt', val='VHPT',
228 fn=set_int, default=0,
229 use="Log2 of domain VHPT size for IA64.")
231 gopts.var('cpu_cap', val='CAP',
232 fn=set_int, default=None,
233 use="""Set the maximum amount of cpu.
234 CAP is a percentage that fixes the maximum amount of cpu.""")
236 gopts.var('cpu_weight', val='WEIGHT',
237 fn=set_int, default=None,
238 use="""Set the cpu time ratio to be allocated to the domain.""")
240 gopts.var('restart', val='onreboot|always|never',
241 fn=set_value, default=None,
242 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
243 instead.
245 Whether the domain should be restarted on exit.
246 - onreboot: restart on exit with shutdown code reboot
247 - always: always restart on exit, ignore exit code
248 - never: never restart on exit, ignore exit code""")
250 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
251 fn=set_value, default=None,
252 use="""Behaviour when a domain exits with reason 'poweroff'.
253 - destroy: the domain is cleaned up as normal;
254 - restart: a new domain is started in place of the old one;
255 - preserve: no clean-up is done until the domain is manually
256 destroyed (using xm destroy, for example);
257 - rename-restart: the old domain is not cleaned up, but is
258 renamed and a new domain started in its place.
259 """)
261 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
262 fn=set_value, default=None,
263 use="""Behaviour when a domain exits with reason 'reboot'.
264 - destroy: the domain is cleaned up as normal;
265 - restart: a new domain is started in place of the old one;
266 - preserve: no clean-up is done until the domain is manually
267 destroyed (using xm destroy, for example);
268 - rename-restart: the old domain is not cleaned up, but is
269 renamed and a new domain started in its place.
270 """)
272 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart|coredump-destroy|ciredump-restart',
273 fn=set_value, default=None,
274 use="""Behaviour when a domain exits with reason 'crash'.
275 - destroy: the domain is cleaned up as normal;
276 - restart: a new domain is started in place of the old one;
277 - preserve: no clean-up is done until the domain is manually
278 destroyed (using xm destroy, for example);
279 - rename-restart: the old domain is not cleaned up, but is
280 renamed and a new domain started in its place.
281 - coredump-destroy: dump the domain's core, followed by destroy
282 - coredump-restart: dump the domain's core, followed by restart
283 """)
285 gopts.var('blkif', val='no|yes',
286 fn=set_bool, default=0,
287 use="Make the domain a block device backend.")
289 gopts.var('netif', val='no|yes',
290 fn=set_bool, default=0,
291 use="Make the domain a network interface backend.")
293 gopts.var('tpmif', val='no|yes',
294 fn=append_value, default=0,
295 use="Make the domain a TPM interface backend.")
297 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
298 fn=append_value, default=[],
299 use="""Add a disk device to a domain. The physical device is DEV,
300 which is exported to the domain as VDEV. The disk is read-only if MODE
301 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
302 backend driver domain to use for the disk.
303 The option may be repeated to add more than one disk.""")
305 gopts.var('pci', val='BUS:DEV.FUNC',
306 fn=append_value, default=[],
307 use="""Add a PCI device to a domain, using given params (in hex).
308 For example 'pci=c0:02.1'.
309 The option may be repeated to add more than one pci device.""")
311 gopts.var('vscsi', val='PDEV,VDEV[,DOM]',
312 fn=append_value, default=[],
313 use="""Add a SCSI device to a domain. The physical device is PDEV,
314 which is exported to the domain as VDEV(X:X:X:X).""")
316 gopts.var('ioports', val='FROM[-TO]',
317 fn=append_value, default=[],
318 use="""Add a legacy I/O range to a domain, using given params (in hex).
319 For example 'ioports=02f8-02ff'.
320 The option may be repeated to add more than one i/o range.""")
322 gopts.var('irq', val='IRQ',
323 fn=append_value, default=[],
324 use="""Add an IRQ (interrupt line) to a domain.
325 For example 'irq=7'.
326 This option may be repeated to add more than one IRQ.""")
328 gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,display=DISPLAY,xauthority=XAUTHORITY,vncpasswd=PASSWORD,opengl=1,keymap=FILE",
329 fn=append_value, default=[],
330 use="""Make the domain a framebuffer backend.
331 The backend type should be either sdl or vnc.
332 For type=vnc, connect an external vncviewer. The server will listen
333 on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
334 domain id. If vncunused=1, the server will try to find an arbitrary
335 unused port above 5900. vncpasswd overrides the XenD configured
336 default password.
337 For type=sdl, a viewer will be started automatically using the
338 given DISPLAY and XAUTHORITY, which default to the current user's
339 ones. OpenGL will be used by default unless opengl is set to 0.
340 keymap overrides the XendD configured default layout file.""")
342 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT," + \
343 "backend=DOM,vifname=NAME,rate=RATE,model=MODEL,accel=ACCEL",
344 fn=append_value, default=[],
345 use="""Add a network interface with the given MAC address and bridge.
346 The vif is configured by calling the given configuration script.
347 If type is not specified, default is netfront.
348 If mac is not specified a random MAC address is used.
349 If not specified then the network backend chooses it's own MAC address.
350 If bridge is not specified the first bridge found is used.
351 If script is not specified the default script is used.
352 If backend is not specified the default backend driver domain is used.
353 If vifname is not specified the backend virtual interface will have name vifD.N
354 where D is the domain id and N is the interface id.
355 If rate is not specified the default rate is used.
356 If model is not specified the default model is used.
357 If accel is not specified an accelerator plugin module is not used.
358 This option may be repeated to add more than one vif.
359 Specifying vifs will increase the number of interfaces as needed.""")
361 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
362 fn=append_value, default=[],
363 use="""Add a TPM interface. On the backend side use the given
364 instance as virtual TPM instance. The given number is merely the
365 preferred instance number. The hotplug script will determine
366 which instance number will actually be assigned to the domain.
367 The associtation between virtual machine and the TPM instance
368 number can be found in /etc/xen/vtpm.db. Use the backend in the
369 given domain.
370 The type parameter can be used to select a specific driver type
371 that the VM can use. To prevent a fully virtualized domain (HVM)
372 from being able to access an emulated device model, you may specify
373 'paravirtualized' here.""")
375 gopts.var('access_control', val="policy=POLICY,label=LABEL",
376 fn=append_value, default=[],
377 use="""Add a security label and the security policy reference that defines it.
378 The local ssid reference is calculated when starting/resuming the domain. At
379 this time, the policy is checked against the active policy as well. This way,
380 migrating through save/restore is covered and local labels are automatically
381 created correctly on the system where a domain is started / resumed.""")
383 gopts.var('nics', val="NUM",
384 fn=set_int, default=-1,
385 use="""DEPRECATED. Use empty vif entries instead.
387 Set the number of network interfaces.
388 Use the vif option to define interface parameters, otherwise
389 defaults are used. Specifying vifs will increase the
390 number of interfaces as needed.""")
392 gopts.var('root', val='DEVICE',
393 fn=set_value, default='',
394 use="""Set the root= parameter on the kernel command line.
395 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
397 gopts.var('extra', val="ARGS",
398 fn=set_value, default='',
399 use="Set extra arguments to append to the kernel command line.")
401 gopts.var('ip', val='IPADDR',
402 fn=set_value, default='',
403 use="Set the kernel IP interface address.")
405 gopts.var('gateway', val="IPADDR",
406 fn=set_value, default='',
407 use="Set the kernel IP gateway.")
409 gopts.var('netmask', val="MASK",
410 fn=set_value, default = '',
411 use="Set the kernel IP netmask.")
413 gopts.var('hostname', val="NAME",
414 fn=set_value, default='',
415 use="Set the kernel IP hostname.")
417 gopts.var('interface', val="INTF",
418 fn=set_value, default="eth0",
419 use="Set the kernel IP interface name.")
421 gopts.var('dhcp', val="off|dhcp",
422 fn=set_value, default='off',
423 use="Set the kernel dhcp option.")
425 gopts.var('nfs_server', val="IPADDR",
426 fn=set_value, default=None,
427 use="Set the address of the NFS server for NFS root.")
429 gopts.var('nfs_root', val="PATH",
430 fn=set_value, default=None,
431 use="Set the path of the root NFS directory.")
433 gopts.var('device_model', val='FILE',
434 fn=set_value, default=None,
435 use="Path to device model program.")
437 gopts.var('fda', val='FILE',
438 fn=set_value, default='',
439 use="Path to fda")
441 gopts.var('fdb', val='FILE',
442 fn=set_value, default='',
443 use="Path to fdb")
445 gopts.var('serial', val='FILE',
446 fn=set_value, default='',
447 use="Path to serial or pty or vc")
449 gopts.var('monitor', val='no|yes',
450 fn=set_bool, default=0,
451 use="""Should the device model use monitor?""")
453 gopts.var('localtime', val='no|yes',
454 fn=set_bool, default=0,
455 use="Is RTC set to localtime?")
457 gopts.var('keymap', val='FILE',
458 fn=set_value, default='',
459 use="Set keyboard layout used")
461 gopts.var('usb', val='no|yes',
462 fn=set_bool, default=0,
463 use="Emulate USB devices?")
465 gopts.var('usbdevice', val='NAME',
466 fn=set_value, default='',
467 use="Name of USB device to add?")
469 gopts.var('guest_os_type', val='NAME',
470 fn=set_value, default='default',
471 use="Guest OS type running in HVM")
473 gopts.var('stdvga', val='no|yes',
474 fn=set_bool, default=0,
475 use="Use std vga or cirrhus logic graphics")
477 gopts.var('isa', val='no|yes',
478 fn=set_bool, default=0,
479 use="Simulate an ISA only system?")
481 gopts.var('boot', val="a|b|c|d",
482 fn=set_value, default='c',
483 use="Default boot device")
485 gopts.var('nographic', val='no|yes',
486 fn=set_bool, default=0,
487 use="Should device models use graphics?")
489 gopts.var('soundhw', val='audiodev',
490 fn=set_value, default='',
491 use="Should device models enable audio device?")
493 gopts.var('vnc', val='',
494 fn=set_value, default=None,
495 use="""Should the device model use VNC?""")
497 gopts.var('vncdisplay', val='',
498 fn=set_value, default=None,
499 use="""VNC display to use""")
501 gopts.var('vnclisten', val='',
502 fn=set_value, default=None,
503 use="""Address for VNC server to listen on.""")
505 gopts.var('vncunused', val='',
506 fn=set_bool, default=1,
507 use="""Try to find an unused port for the VNC server.
508 Only valid when vnc=1.""")
510 gopts.var('videoram', val='',
511 fn=set_value, default=None,
512 use="""Maximum amount of videoram PV guest can allocate
513 for frame buffer.""")
515 gopts.var('sdl', val='',
516 fn=set_value, default=None,
517 use="""Should the device model use SDL?""")
519 gopts.var('opengl', val='',
520 fn=set_value, default=None,
521 use="""Enable\Disable OpenGL""")
523 gopts.var('display', val='DISPLAY',
524 fn=set_value, default=None,
525 use="X11 display to use")
527 gopts.var('xauthority', val='XAUTHORITY',
528 fn=set_value, default=None,
529 use="X11 Authority to use")
531 gopts.var('uuid', val='',
532 fn=set_value, default=None,
533 use="""xenstore UUID (universally unique identifier) to use. One
534 will be randomly generated if this option is not set, just like MAC
535 addresses for virtual network interfaces. This must be a unique
536 value across the entire cluster.""")
538 gopts.var('on_xend_start', val='ignore|start',
539 fn=set_value, default='ignore',
540 use='Action to perform when xend starts')
542 gopts.var('on_xend_stop', val='ignore|shutdown|suspend',
543 fn=set_value, default="ignore",
544 use="""Behaviour when Xend stops:
545 - ignore: Domain continues to run;
546 - shutdown: Domain is shutdown;
547 - suspend: Domain is suspended;
548 """)
550 gopts.var('target', val='TARGET',
551 fn=set_int, default=0,
552 use="Set domain target.")
554 gopts.var('hap', val='HAP',
555 fn=set_int, default=1,
556 use="""Hap status (0=hap is disabled;
557 1=hap is enabled.""")
559 gopts.var('cpuid', val="IN[,SIN]:eax=EAX,ebx=EBX,exc=ECX,edx=EDX",
560 fn=append_value, default=[],
561 use="""Cpuid description.""")
563 gopts.var('cpuid_check', val="IN[,SIN]:eax=EAX,ebx=EBX,exc=ECX,edx=EDX",
564 fn=append_value, default=[],
565 use="""Cpuid check description.""")
567 gopts.var('machine_address_size', val='BITS',
568 fn=set_int, default=None,
569 use="""Maximum machine address size""")
571 def err(msg):
572 """Print an error to stderr and exit.
573 """
574 print >>sys.stderr, "Error:", msg
575 sys.exit(1)
578 def warn(msg):
579 """Print a warning to stdout.
580 """
581 print >>sys.stderr, "Warning:", msg
584 def strip(pre, s):
585 """Strip prefix 'pre' if present.
586 """
587 if s.startswith(pre):
588 return s[len(pre):]
589 else:
590 return s
592 def configure_image(vals):
593 """Create the image config.
594 """
595 if not vals.builder:
596 return None
597 config_image = [ vals.builder ]
598 if vals.kernel:
599 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
600 if vals.ramdisk:
601 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
602 if vals.loader:
603 config_image.append([ 'loader', os.path.abspath(vals.loader) ])
604 if vals.cmdline_ip:
605 cmdline_ip = strip('ip=', vals.cmdline_ip)
606 config_image.append(['ip', cmdline_ip])
607 if vals.root:
608 cmdline_root = strip('root=', vals.root)
609 config_image.append(['root', cmdline_root])
610 if vals.extra:
611 config_image.append(['args', vals.extra])
613 if vals.builder == 'hvm':
614 configure_hvm(config_image, vals)
616 if vals.vhpt != 0:
617 config_image.append(['vhpt', vals.vhpt])
619 if vals.machine_address_size:
620 config_image.append(['machine_address_size', vals.machine_address_size])
622 return config_image
624 def configure_disks(config_devs, vals):
625 """Create the config for disks (virtual block devices).
626 """
627 for (uname, dev, mode, backend, protocol) in vals.disk:
628 if uname.startswith('tap:'):
629 cls = 'tap'
630 else:
631 cls = 'vbd'
633 config_vbd = [cls,
634 ['uname', uname],
635 ['dev', dev ],
636 ['mode', mode ] ]
637 if backend:
638 config_vbd.append(['backend', backend])
639 if protocol:
640 config_vbd.append(['protocol', protocol])
641 config_devs.append(['device', config_vbd])
643 def configure_pci(config_devs, vals):
644 """Create the config for pci devices.
645 """
646 config_pci = []
647 for (domain, bus, slot, func) in vals.pci:
648 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
649 ['slot', slot], ['func', func]])
651 if len(config_pci)>0:
652 config_pci.insert(0, 'pci')
653 config_devs.append(['device', config_pci])
655 def vscsi_convert_sxp_to_dict(dev_sxp):
656 dev_dict = {}
657 for opt_val in dev_sxp[1:]:
658 try:
659 opt, val = opt_val
660 dev_dict[opt] = val
661 except TypeError:
662 pass
663 return dev_dict
665 def vscsi_lookup_devid(devlist, req_devid):
666 if len(devlist) == 0:
667 return 0
668 else:
669 for devid, backend in devlist:
670 if devid == req_devid:
671 return 1
672 return 0
674 def configure_vscsis(config_devs, vals):
675 """Create the config for vscsis (virtual scsi devices).
676 """
677 devidlist = []
678 config_scsi = []
679 if len(vals.vscsi) == 0:
680 return 0
682 scsi_devices = vscsi_util.vscsi_get_scsidevices()
683 for (p_dev, v_dev, backend) in vals.vscsi:
684 tmp = p_dev.split(':')
685 if len(tmp) == 4:
686 (p_hctl, block) = vscsi_util._vscsi_hctl_block(p_dev, scsi_devices)
687 else:
688 (p_hctl, block) = vscsi_util._vscsi_block_scsiid_to_hctl(p_dev, scsi_devices)
690 if p_hctl == None:
691 raise ValueError("Cannot find device \"%s\"" % p_dev)
693 for config in config_scsi:
694 dev = vscsi_convert_sxp_to_dict(config)
695 if dev['v-dev'] == v_dev:
696 raise ValueError('The virtual device "%s" is already defined' % v_dev)
698 v_hctl = v_dev.split(':')
699 devid = int(v_hctl[0])
700 config_scsi.append(['dev', \
701 ['state', 'Initialising'], \
702 ['devid', devid], \
703 ['p-dev', p_hctl], \
704 ['p-devname', block], \
705 ['v-dev', v_dev] ])
707 if vscsi_lookup_devid(devidlist, devid) == 0:
708 devidlist.append([devid, backend])
710 for devid, backend in devidlist:
711 tmp = []
712 for config in config_scsi:
713 dev = vscsi_convert_sxp_to_dict(config)
714 if dev['devid'] == devid:
715 tmp.append(config)
717 tmp.insert(0, 'vscsi')
718 if backend:
719 tmp.append(['backend', backend])
720 config_devs.append(['device', tmp])
722 def configure_ioports(config_devs, vals):
723 """Create the config for legacy i/o ranges.
724 """
725 for (io_from, io_to) in vals.ioports:
726 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
727 config_devs.append(['device', config_ioports])
729 def configure_irq(config_devs, vals):
730 """Create the config for irqs.
731 """
732 for irq in vals.irq:
733 config_irq = ['irq', ['irq', irq]]
734 config_devs.append(['device', config_irq])
736 def configure_vfbs(config_devs, vals):
737 for f in vals.vfb:
738 d = comma_sep_kv_to_dict(f)
739 config = ['vfb']
740 if not d.has_key("type"):
741 d['type'] = 'sdl'
742 for (k,v) in d.iteritems():
743 if not k in [ 'vnclisten', 'vncunused', 'vncdisplay', 'display',
744 'videoram', 'xauthority', 'type', 'vncpasswd',
745 'opengl', 'keymap' ]:
746 err("configuration option %s unknown to vfbs" % k)
747 config.append([k,v])
748 if not d.has_key("keymap"):
749 if vals.keymap:
750 config.append(['keymap',vals.keymap])
751 if not d.has_key("display") and os.environ.has_key("DISPLAY"):
752 config.append(["display", os.environ['DISPLAY']])
753 if not d.has_key("xauthority"):
754 config.append(["xauthority", get_xauthority()])
755 config_devs.append(['device', ['vkbd']])
756 config_devs.append(['device', config])
758 def configure_security(config, vals):
759 """Create the config for ACM security labels.
760 """
761 access_control = vals.access_control
762 num = len(access_control)
763 if num == 1:
764 d = access_control[0]
765 policy = d.get('policy')
766 label = d.get('label')
767 if policy != security.active_policy:
768 err("Security policy (" + policy + ") incompatible with enforced policy ("
769 + security.active_policy + ")." )
770 config_access_control = ['access_control',
771 ['policy', policy],
772 ['label', label] ]
774 security_label = ['security', [ config_access_control ] ]
775 config.append(security_label)
776 elif num > 1:
777 err("VM config error: Multiple access_control definitions!")
780 def configure_vtpm(config_devs, vals):
781 """Create the config for virtual TPM interfaces.
782 """
783 vtpm = vals.vtpm
784 if len(vtpm) > 0:
785 d = vtpm[0]
786 instance = d.get('instance')
787 if instance == "VTPMD":
788 instance = "0"
789 else:
790 if instance != None:
791 try:
792 if int(instance) == 0:
793 err('VM config error: vTPM instance must not be 0.')
794 except ValueError:
795 err('Vm config error: could not parse instance number.')
796 backend = d.get('backend')
797 typ = d.get('type')
798 config_vtpm = ['vtpm']
799 if instance:
800 config_vtpm.append(['pref_instance', instance])
801 if backend:
802 config_vtpm.append(['backend', backend])
803 if typ:
804 config_vtpm.append(['type', type])
805 config_devs.append(['device', config_vtpm])
808 def configure_vifs(config_devs, vals):
809 """Create the config for virtual network interfaces.
810 """
812 vifs = vals.vif
813 vifs_n = len(vifs)
815 if hasattr(vals, 'nics'):
816 if vals.nics > 0:
817 warn("The nics option is deprecated. Please use an empty vif "
818 "entry instead:\n\n vif = [ '' ]\n")
819 for _ in range(vifs_n, vals.nics):
820 vifs.append('')
821 vifs_n = len(vifs)
822 elif vals.nics == 0:
823 warn("The nics option is deprecated. Please remove it.")
825 for c in vifs:
826 d = comma_sep_kv_to_dict(c)
827 config_vif = ['vif']
829 def f(k):
830 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
831 'vifname', 'rate', 'model', 'accel',
832 'policy', 'label']:
833 err('Invalid vif option: ' + k)
835 config_vif.append([k, d[k]])
837 map(f, d.keys())
838 config_devs.append(['device', config_vif])
841 def configure_hvm(config_image, vals):
842 """Create the config for HVM devices.
843 """
844 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb', 'timer_mode',
845 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
846 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
847 'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
848 'acpi', 'apic', 'usb', 'usbdevice', 'keymap', 'pci', 'hpet',
849 'guest_os_type', 'hap', 'opengl', 'cpuid', 'cpuid_check']
851 for a in args:
852 if a in vals.__dict__ and vals.__dict__[a] is not None:
853 config_image.append([a, vals.__dict__[a]])
854 if vals.vncpasswd is not None:
855 config_image.append(['vncpasswd', vals.vncpasswd])
858 def make_config(vals):
859 """Create the domain configuration.
860 """
862 config = ['vm']
864 def add_conf(n):
865 if hasattr(vals, n):
866 v = getattr(vals, n)
867 if v:
868 config.append([n, v])
870 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
871 'restart', 'on_poweroff',
872 'on_reboot', 'on_crash', 'vcpus', 'vcpu_avail', 'features',
873 'on_xend_start', 'on_xend_stop', 'target', 'cpuid',
874 'cpuid_check', 'machine_address_size'])
876 if vals.uuid is not None:
877 config.append(['uuid', vals.uuid])
878 if vals.cpu is not None:
879 config.append(['cpu', vals.cpu])
880 if vals.cpus is not None:
881 config.append(['cpus', vals.cpus])
882 if vals.cpu_cap is not None:
883 config.append(['cpu_cap', vals.cpu_cap])
884 if vals.cpu_weight is not None:
885 config.append(['cpu_weight', vals.cpu_weight])
886 if vals.blkif:
887 config.append(['backend', ['blkif']])
888 if vals.netif:
889 config.append(['backend', ['netif']])
890 if vals.tpmif:
891 config.append(['backend', ['tpmif']])
892 if vals.localtime:
893 config.append(['localtime', vals.localtime])
895 config_image = configure_image(vals)
896 if vals.bootloader:
897 if vals.bootloader == "pygrub":
898 vals.bootloader = osdep.pygrub_path
900 config.append(['bootloader', vals.bootloader])
901 if vals.bootargs:
902 config.append(['bootloader_args', vals.bootargs])
903 else:
904 if vals.console_autoconnect:
905 config.append(['bootloader_args', ''])
906 else:
907 config.append(['bootloader_args', '-q'])
908 config.append(['image', config_image])
910 config_devs = []
911 configure_disks(config_devs, vals)
912 configure_pci(config_devs, vals)
913 configure_vscsis(config_devs, vals)
914 configure_ioports(config_devs, vals)
915 configure_irq(config_devs, vals)
916 configure_vifs(config_devs, vals)
917 configure_vtpm(config_devs, vals)
918 configure_vfbs(config_devs, vals)
919 configure_security(config, vals)
920 config += config_devs
922 return config
924 def preprocess_disk(vals):
925 if not vals.disk: return
926 disk = []
927 for v in vals.disk:
928 d = v.split(',')
929 n = len(d)
930 if n == 3:
931 d.append(None)
932 d.append(None)
933 elif n == 4:
934 d.append(None)
935 elif n == 5:
936 pass
937 else:
938 err('Invalid disk specifier: ' + v)
939 disk.append(d)
940 vals.disk = disk
942 def preprocess_cpuid(vals, attr_name):
943 if not vals.cpuid: return
944 cpuid = {}
945 for cpuid_input in getattr(vals, attr_name):
946 input_re = "(0x)?[0-9A-Fa-f]+(,(0x)?[0-9A-Fa-f]+)?"
947 cpuid_match = re.match(r'(?P<input>%s):(?P<regs>.*)' % \
948 input_re, cpuid_input)
949 if cpuid_match != None:
950 res_cpuid = cpuid_match.groupdict()
951 input = res_cpuid['input']
952 regs = res_cpuid['regs'].split(',')
953 cpuid[input]= {} # New input
954 for reg in regs:
955 reg_match = re.match(r"(?P<reg>eax|ebx|ecx|edx)=(?P<val>.*)", reg)
956 if reg_match == None:
957 err("cpuid's syntax is (eax|ebx|ecx|edx)=value")
958 res = reg_match.groupdict()
959 cpuid[input][res['reg']] = res['val'] # new register
960 setattr(vals, attr_name, cpuid)
962 def preprocess_pci(vals):
963 if not vals.pci: return
964 pci = []
965 for pci_dev_str in vals.pci:
966 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
967 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
968 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
969 r"(?P<func>[0-7])$", pci_dev_str)
970 if pci_match!=None:
971 pci_dev_info = pci_match.groupdict('0')
972 try:
973 pci.append( ('0x'+pci_dev_info['domain'], \
974 '0x'+pci_dev_info['bus'], \
975 '0x'+pci_dev_info['slot'], \
976 '0x'+pci_dev_info['func']))
977 except IndexError:
978 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
979 vals.pci = pci
981 def preprocess_vscsi(vals):
982 if not vals.vscsi: return
983 scsi = []
984 for scsi_str in vals.vscsi:
985 d = scsi_str.split(',')
986 n = len(d)
987 if n == 2:
988 tmp = d[1].split(':')
989 if len(tmp) != 4:
990 err('vscsi syntax error "%s"' % d[1])
991 else:
992 d.append(None)
993 elif n == 3:
994 pass
995 else:
996 err('vscsi syntax error "%s"' % scsi_str)
997 scsi.append(d)
998 vals.vscsi = scsi
1000 def preprocess_ioports(vals):
1001 if not vals.ioports: return
1002 ioports = []
1003 for v in vals.ioports:
1004 d = v.split('-')
1005 if len(d) < 1 or len(d) > 2:
1006 err('Invalid i/o port range specifier: ' + v)
1007 if len(d) == 1:
1008 d.append(d[0])
1009 # Components are in hex: add hex specifier.
1010 hexd = ['0x' + x for x in d]
1011 ioports.append(hexd)
1012 vals.ioports = ioports
1014 def preprocess_vtpm(vals):
1015 if not vals.vtpm: return
1016 vtpms = []
1017 for vtpm in vals.vtpm:
1018 d = {}
1019 a = vtpm.split(',')
1020 for b in a:
1021 (k, v) = b.strip().split('=', 1)
1022 k = k.strip()
1023 v = v.strip()
1024 if k not in ['backend', 'instance']:
1025 err('Invalid vtpm specifier: ' + vtpm)
1026 d[k] = v
1027 vtpms.append(d)
1028 vals.vtpm = vtpms
1030 def preprocess_access_control(vals):
1031 if not vals.access_control:
1032 return
1033 access_controls = []
1034 num = len(vals.access_control)
1035 if num == 1:
1036 access_control = (vals.access_control)[0]
1037 d = {}
1038 a = access_control.split(',')
1039 if len(a) > 2:
1040 err('Too many elements in access_control specifier: ' + access_control)
1041 for b in a:
1042 (k, v) = b.strip().split('=', 1)
1043 k = k.strip()
1044 v = v.strip()
1045 if k not in ['policy','label']:
1046 err('Invalid access_control specifier: ' + access_control)
1047 d[k] = v
1048 access_controls.append(d)
1049 vals.access_control = access_controls
1050 elif num > 1:
1051 err('Multiple access_control definitions.')
1053 def preprocess_ip(vals):
1054 if vals.ip or vals.dhcp != 'off':
1055 dummy_nfs_server = '127.0.255.255'
1056 ip = (vals.ip
1057 + ':' + (vals.nfs_server or dummy_nfs_server)
1058 + ':' + vals.gateway
1059 + ':' + vals.netmask
1060 + ':' + vals.hostname
1061 + ':' + vals.interface
1062 + ':' + vals.dhcp)
1063 else:
1064 ip = ''
1065 vals.cmdline_ip = ip
1067 def preprocess_nfs(vals):
1068 if not vals.nfs_root: return
1069 if not vals.nfs_server:
1070 err('Must set nfs root and nfs server')
1071 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
1072 vals.extra = nfs + ' ' + vals.extra
1075 def get_host_addr():
1076 host = socket.gethostname()
1077 addr = socket.gethostbyname(host)
1078 return addr
1080 VNC_BASE_PORT = 5500
1082 def choose_vnc_display():
1083 """Try to choose a free vnc display.
1084 """
1085 def netstat_local_ports():
1086 """Run netstat to get a list of the local ports in use.
1087 """
1088 l = os.popen("netstat -nat").readlines()
1089 r = []
1090 # Skip 2 lines of header.
1091 for x in l[2:]:
1092 # Local port is field 3.
1093 y = x.split()[3]
1094 # Field is addr:port, split off the port.
1095 y = y.split(':')[-1]
1096 r.append(int(y))
1097 return r
1099 ports = netstat_local_ports()
1100 for d in range(1, 100):
1101 port = VNC_BASE_PORT + d
1102 if port in ports: continue
1103 return d
1104 return None
1105 vncpid = None
1107 def daemonize(prog, args):
1108 """Runs a program as a daemon with the list of arguments. Returns the PID
1109 of the daemonized program, or returns 0 on error.
1110 """
1111 r, w = os.pipe()
1112 pid = os.fork()
1114 if pid == 0:
1115 os.close(r)
1116 w = os.fdopen(w, 'w')
1117 os.setsid()
1118 try:
1119 pid2 = os.fork()
1120 except:
1121 pid2 = None
1122 if pid2 == 0:
1123 os.chdir("/")
1124 for fd in range(0, 256):
1125 try:
1126 os.close(fd)
1127 except:
1128 pass
1129 os.open("/dev/null", os.O_RDWR)
1130 os.dup2(0, 1)
1131 os.dup2(0, 2)
1132 os.execvp(prog, args)
1133 os._exit(1)
1134 else:
1135 w.write(str(pid2 or 0))
1136 w.close()
1137 os._exit(0)
1138 os.close(w)
1139 r = os.fdopen(r)
1140 daemon_pid = int(r.read())
1141 r.close()
1142 os.waitpid(pid, 0)
1143 return daemon_pid
1145 def spawn_vnc(display):
1146 """Spawns a vncviewer that listens on the specified display. On success,
1147 returns the port that the vncviewer is listening on and sets the global
1148 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
1149 """
1150 vncargs = (["vncviewer", "-log", "*:stdout:0",
1151 "-listen", "%d" % (VNC_BASE_PORT + display) ])
1152 global vncpid
1153 vncpid = daemonize("vncviewer", vncargs)
1154 if vncpid == 0:
1155 return 0
1157 return VNC_BASE_PORT + display
1159 def preprocess_vnc(vals):
1160 """If vnc was specified, spawn a vncviewer in listen mode
1161 and pass its address to the domain on the kernel command line.
1162 """
1163 if vals.dryrun: return
1164 if vals.vncviewer:
1165 vnc_display = choose_vnc_display()
1166 if not vnc_display:
1167 warn("No free vnc display")
1168 return
1169 print 'VNC=', vnc_display
1170 vnc_port = spawn_vnc(vnc_display)
1171 if vnc_port > 0:
1172 vnc_host = get_host_addr()
1173 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
1174 vals.extra = vnc + ' ' + vals.extra
1176 def preprocess(vals):
1177 preprocess_disk(vals)
1178 preprocess_pci(vals)
1179 preprocess_vscsi(vals)
1180 preprocess_ioports(vals)
1181 preprocess_ip(vals)
1182 preprocess_nfs(vals)
1183 preprocess_vnc(vals)
1184 preprocess_vtpm(vals)
1185 preprocess_access_control(vals)
1186 preprocess_cpuid(vals, 'cpuid')
1187 preprocess_cpuid(vals, 'cpuid_check')
1190 def comma_sep_kv_to_dict(c):
1191 """Convert comma-separated, equals-separated key-value pairs into a
1192 dictionary.
1193 """
1194 d = {}
1195 c = c.strip()
1196 if len(c) > 0:
1197 a = c.split(',')
1198 for b in a:
1199 if b.find('=') == -1:
1200 err("%s should be a pair, separated by an equals sign." % b)
1201 (k, v) = b.split('=', 1)
1202 k = k.strip()
1203 v = v.strip()
1204 d[k] = v
1205 return d
1208 def make_domain(opts, config):
1209 """Create, build and start a domain.
1211 @param opts: options
1212 @param config: configuration
1213 @return: domain id
1214 @rtype: int
1215 """
1217 try:
1218 dominfo = server.xend.domain.create(config)
1219 except xmlrpclib.Fault, ex:
1220 import signal
1221 if vncpid:
1222 os.kill(vncpid, signal.SIGKILL)
1223 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
1224 err("the domain '%s' does not exist." % ex.faultString)
1225 else:
1226 err("%s" % ex.faultString)
1227 except Exception, ex:
1228 # main.py has good error messages that let the user know what failed.
1229 # unless the error is a create.py specific thing, it should be handled
1230 # at main. The purpose of this general-case 'Exception' handler is to
1231 # clean up create.py specific processes/data but since create.py does
1232 # not know what to do with the error, it should pass it up.
1233 import signal
1234 if vncpid:
1235 os.kill(vncpid, signal.SIGKILL)
1236 raise
1238 dom = sxp.child_value(dominfo, 'name')
1240 try:
1241 server.xend.domain.waitForDevices(dom)
1242 except xmlrpclib.Fault, ex:
1243 server.xend.domain.destroy(dom)
1244 err("%s" % ex.faultString)
1245 except:
1246 server.xend.domain.destroy(dom)
1247 err("Device creation failed for domain %s" % dom)
1249 if not opts.vals.paused:
1250 try:
1251 server.xend.domain.unpause(dom)
1252 except:
1253 server.xend.domain.destroy(dom)
1254 err("Failed to unpause domain %s" % dom)
1255 opts.info("Started domain %s" % (dom))
1256 return int(sxp.child_value(dominfo, 'domid'))
1259 def get_xauthority():
1260 xauth = os.getenv("XAUTHORITY")
1261 if not xauth:
1262 home = os.getenv("HOME")
1263 if not home:
1264 import posix, pwd
1265 home = pwd.getpwuid(posix.getuid())[5]
1266 xauth = home + "/.Xauthority"
1267 return xauth
1270 def parseCommandLine(argv):
1271 gopts.reset()
1272 args = gopts.parse(argv)
1274 if gopts.vals.help or gopts.vals.help_config:
1275 if gopts.vals.help_config:
1276 print gopts.val_usage()
1277 return (None, None)
1279 if not gopts.vals.display:
1280 gopts.vals.display = os.getenv("DISPLAY")
1282 if not gopts.vals.xauthority:
1283 gopts.vals.xauthority = get_xauthority()
1285 gopts.is_xml = False
1287 # Process remaining args as config variables.
1288 for arg in args:
1289 if '=' in arg:
1290 (var, val) = arg.strip().split('=', 1)
1291 gopts.setvar(var.strip(), val.strip())
1292 if gopts.vals.config:
1293 config = gopts.vals.config
1294 else:
1295 try:
1296 gopts.load_defconfig()
1297 preprocess(gopts.vals)
1298 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1299 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1300 config = make_config(gopts.vals)
1301 except XMLFileError, ex:
1302 XMLFile = ex.getFile()
1303 gopts.is_xml = True
1304 config = ex.getFile()
1306 return (gopts, config)
1308 def help():
1309 return str(gopts)
1311 def main(argv):
1312 is_xml = False
1314 try:
1315 (opts, config) = parseCommandLine(argv)
1316 except StandardError, ex:
1317 err(str(ex))
1319 if not opts:
1320 return
1322 if not opts.is_xml:
1323 if type(config) == str:
1324 try:
1325 config = sxp.parse(file(config))[0]
1326 except IOError, exn:
1327 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1329 if serverType == SERVER_XEN_API:
1330 from xen.xm.xenapi_create import sxp2xml
1331 sxp2xml_inst = sxp2xml()
1332 doc = sxp2xml_inst.convert_sxp_to_xml(config, transient=True)
1334 if opts.vals.dryrun and not opts.is_xml:
1335 SXPPrettyPrint.prettyprint(config)
1337 if opts.vals.xmldryrun and serverType == SERVER_XEN_API:
1338 from xml.dom.ext import PrettyPrint as XMLPrettyPrint
1339 XMLPrettyPrint(doc)
1341 if opts.vals.dryrun or opts.vals.xmldryrun:
1342 return
1344 if opts.vals.console_autoconnect:
1345 do_console(sxp.child_value(config, 'name', -1))
1347 if serverType == SERVER_XEN_API:
1348 from xen.xm.xenapi_create import xenapi_create
1349 xenapi_create_inst = xenapi_create()
1350 if opts.is_xml:
1351 vm_refs = xenapi_create_inst.create(filename = config,
1352 skipdtd = opts.vals.skipdtd)
1353 else:
1354 vm_refs = xenapi_create_inst.create(document = doc,
1355 skipdtd = opts.vals.skipdtd)
1357 map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs)
1358 elif not opts.is_xml:
1359 dom = make_domain(opts, config)
1361 def do_console(domain_name):
1362 cpid = os.fork()
1363 if cpid != 0:
1364 for i in range(10):
1365 # Catch failure of the create process
1366 time.sleep(1)
1367 (p, rv) = os.waitpid(cpid, os.WNOHANG)
1368 if os.WIFEXITED(rv):
1369 if os.WEXITSTATUS(rv) != 0:
1370 sys.exit(os.WEXITSTATUS(rv))
1371 try:
1372 # Acquire the console of the created dom
1373 if serverType == SERVER_XEN_API:
1374 domid = server.xenapi.VM.get_domid(
1375 get_single_vm(domain_name))
1376 else:
1377 dom = server.xend.domain(domain_name)
1378 domid = int(sxp.child_value(dom, 'domid', '-1'))
1379 console.execConsole(domid)
1380 except:
1381 pass
1382 print("Could not start console\n");
1383 sys.exit(0)
1385 if __name__ == '__main__':
1386 main(sys.argv)