ia64/xen-unstable

view tools/python/xen/xm/create.py @ 15785:2aee2e4eacc8

Fix VNC server after HVM save/restore/migrate, when no vncpasswd specified.

The basic problem was that the "image" section of the sxp had a "None"
in it, which means that on the restore (either on the local machine or
remote machine), the vnc server in the qemu device model literally
needed the string "None" to properly connect. This simple patch only
puts a vncpasswd entry in the image if it is *not* None in the python
code, thus avoiding the whole issue.

Signed-off-by: Chris Lalancette <clalance@redhat.com>
author kfraser@localhost.localdomain
date Tue Aug 28 15:58:46 2007 +0100 (2007-08-28)
parents f1aed243f3f0
children 993655d24b55
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 #calculate ssidref from label
647 ssidref = security.label2ssidref(label, policy, 'dom')
648 if not ssidref :
649 err("ERROR calculating ssidref from access_control.")
650 security_label = ['security', [ config_access_control, ['ssidref' , ssidref ] ] ]
651 config.append(security_label)
652 elif num > 1:
653 err("VM config error: Multiple access_control definitions!")
656 def configure_vtpm(config_devs, vals):
657 """Create the config for virtual TPM interfaces.
658 """
659 vtpm = vals.vtpm
660 if len(vtpm) > 0:
661 d = vtpm[0]
662 instance = d.get('instance')
663 if instance == "VTPMD":
664 instance = "0"
665 else:
666 if instance != None:
667 try:
668 if int(instance) == 0:
669 err('VM config error: vTPM instance must not be 0.')
670 except ValueError:
671 err('Vm config error: could not parse instance number.')
672 backend = d.get('backend')
673 typ = d.get('type')
674 config_vtpm = ['vtpm']
675 if instance:
676 config_vtpm.append(['pref_instance', instance])
677 if backend:
678 config_vtpm.append(['backend', backend])
679 if typ:
680 config_vtpm.append(['type', type])
681 config_devs.append(['device', config_vtpm])
684 def configure_vifs(config_devs, vals):
685 """Create the config for virtual network interfaces.
686 """
688 vifs = vals.vif
689 vifs_n = len(vifs)
691 if hasattr(vals, 'nics'):
692 if vals.nics > 0:
693 warn("The nics option is deprecated. Please use an empty vif "
694 "entry instead:\n\n vif = [ '' ]\n")
695 for _ in range(vifs_n, vals.nics):
696 vifs.append('')
697 vifs_n = len(vifs)
698 elif vals.nics == 0:
699 warn("The nics option is deprecated. Please remove it.")
701 for c in vifs:
702 d = comma_sep_kv_to_dict(c)
703 config_vif = ['vif']
705 def f(k):
706 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
707 'vifname', 'rate', 'model', 'accel',
708 'policy', 'label']:
709 err('Invalid vif option: ' + k)
711 config_vif.append([k, d[k]])
713 map(f, d.keys())
714 config_devs.append(['device', config_vif])
717 def configure_hvm(config_image, vals):
718 """Create the config for HVM devices.
719 """
720 args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
721 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
722 'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
723 'sdl', 'display', 'xauthority', 'rtc_timeoffset', 'monitor',
724 'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
725 for a in args:
726 if a in vals.__dict__ and vals.__dict__[a] is not None:
727 config_image.append([a, vals.__dict__[a]])
728 if vals.vncpasswd is not None:
729 config_image.append(['vncpasswd', vals.vncpasswd])
732 def make_config(vals):
733 """Create the domain configuration.
734 """
736 config = ['vm']
738 def add_conf(n):
739 if hasattr(vals, n):
740 v = getattr(vals, n)
741 if v:
742 config.append([n, v])
744 map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
745 'restart', 'on_poweroff',
746 'on_reboot', 'on_crash', 'vcpus', 'vcpu_avail', 'features',
747 'on_xend_start', 'on_xend_stop'])
749 if vals.uuid is not None:
750 config.append(['uuid', vals.uuid])
751 if vals.cpu is not None:
752 config.append(['cpu', vals.cpu])
753 if vals.cpus is not None:
754 config.append(['cpus', vals.cpus])
755 if vals.cpu_cap is not None:
756 config.append(['cpu_cap', vals.cpu_cap])
757 if vals.cpu_weight is not None:
758 config.append(['cpu_weight', vals.cpu_weight])
759 if vals.blkif:
760 config.append(['backend', ['blkif']])
761 if vals.netif:
762 config.append(['backend', ['netif']])
763 if vals.tpmif:
764 config.append(['backend', ['tpmif']])
765 if vals.localtime:
766 config.append(['localtime', vals.localtime])
768 config_image = configure_image(vals)
769 if vals.bootloader:
770 if vals.bootloader == "pygrub":
771 vals.bootloader = osdep.pygrub_path
773 config.append(['bootloader', vals.bootloader])
774 if vals.bootargs:
775 config.append(['bootloader_args', vals.bootargs])
776 else:
777 if vals.console_autoconnect:
778 config.append(['bootloader_args', ''])
779 else:
780 config.append(['bootloader_args', '-q'])
781 config.append(['image', config_image])
783 config_devs = []
784 configure_disks(config_devs, vals)
785 configure_pci(config_devs, vals)
786 configure_ioports(config_devs, vals)
787 configure_irq(config_devs, vals)
788 configure_vifs(config_devs, vals)
789 configure_usb(config_devs, vals)
790 configure_vtpm(config_devs, vals)
791 configure_vfbs(config_devs, vals)
792 configure_security(config, vals)
793 config += config_devs
795 return config
797 def preprocess_disk(vals):
798 if not vals.disk: return
799 disk = []
800 for v in vals.disk:
801 d = v.split(',')
802 n = len(d)
803 if n == 3:
804 d.append(None)
805 elif n == 4:
806 pass
807 else:
808 err('Invalid disk specifier: ' + v)
809 disk.append(d)
810 vals.disk = disk
812 def preprocess_pci(vals):
813 if not vals.pci: return
814 pci = []
815 for pci_dev_str in vals.pci:
816 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
817 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
818 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
819 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
820 if pci_match!=None:
821 pci_dev_info = pci_match.groupdict('0')
822 try:
823 pci.append( ('0x'+pci_dev_info['domain'], \
824 '0x'+pci_dev_info['bus'], \
825 '0x'+pci_dev_info['slot'], \
826 '0x'+pci_dev_info['func']))
827 except IndexError:
828 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
829 vals.pci = pci
831 def preprocess_ioports(vals):
832 if not vals.ioports: return
833 ioports = []
834 for v in vals.ioports:
835 d = v.split('-')
836 if len(d) < 1 or len(d) > 2:
837 err('Invalid i/o port range specifier: ' + v)
838 if len(d) == 1:
839 d.append(d[0])
840 # Components are in hex: add hex specifier.
841 hexd = ['0x' + x for x in d]
842 ioports.append(hexd)
843 vals.ioports = ioports
845 def preprocess_vtpm(vals):
846 if not vals.vtpm: return
847 vtpms = []
848 for vtpm in vals.vtpm:
849 d = {}
850 a = vtpm.split(',')
851 for b in a:
852 (k, v) = b.strip().split('=', 1)
853 k = k.strip()
854 v = v.strip()
855 if k not in ['backend', 'instance']:
856 err('Invalid vtpm specifier: ' + vtpm)
857 d[k] = v
858 vtpms.append(d)
859 vals.vtpm = vtpms
861 def preprocess_access_control(vals):
862 if not vals.access_control:
863 return
864 access_controls = []
865 num = len(vals.access_control)
866 if num == 1:
867 access_control = (vals.access_control)[0]
868 d = {}
869 a = access_control.split(',')
870 if len(a) > 2:
871 err('Too many elements in access_control specifier: ' + access_control)
872 for b in a:
873 (k, v) = b.strip().split('=', 1)
874 k = k.strip()
875 v = v.strip()
876 if k not in ['policy','label']:
877 err('Invalid access_control specifier: ' + access_control)
878 d[k] = v
879 access_controls.append(d)
880 vals.access_control = access_controls
881 elif num > 1:
882 err('Multiple access_control definitions.')
884 def preprocess_ip(vals):
885 if vals.ip or vals.dhcp != 'off':
886 dummy_nfs_server = '1.2.3.4'
887 ip = (vals.ip
888 + ':' + (vals.nfs_server or dummy_nfs_server)
889 + ':' + vals.gateway
890 + ':' + vals.netmask
891 + ':' + vals.hostname
892 + ':' + vals.interface
893 + ':' + vals.dhcp)
894 else:
895 ip = ''
896 vals.cmdline_ip = ip
898 def preprocess_nfs(vals):
899 if not vals.nfs_root: return
900 if not vals.nfs_server:
901 err('Must set nfs root and nfs server')
902 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
903 vals.extra = nfs + ' ' + vals.extra
906 def get_host_addr():
907 host = socket.gethostname()
908 addr = socket.gethostbyname(host)
909 return addr
911 VNC_BASE_PORT = 5500
913 def choose_vnc_display():
914 """Try to choose a free vnc display.
915 """
916 def netstat_local_ports():
917 """Run netstat to get a list of the local ports in use.
918 """
919 l = os.popen("netstat -nat").readlines()
920 r = []
921 # Skip 2 lines of header.
922 for x in l[2:]:
923 # Local port is field 3.
924 y = x.split()[3]
925 # Field is addr:port, split off the port.
926 y = y.split(':')[-1]
927 r.append(int(y))
928 return r
930 ports = netstat_local_ports()
931 for d in range(1, 100):
932 port = VNC_BASE_PORT + d
933 if port in ports: continue
934 return d
935 return None
936 vncpid = None
938 def daemonize(prog, args):
939 """Runs a program as a daemon with the list of arguments. Returns the PID
940 of the daemonized program, or returns 0 on error.
941 """
942 r, w = os.pipe()
943 pid = os.fork()
945 if pid == 0:
946 os.close(r)
947 w = os.fdopen(w, 'w')
948 os.setsid()
949 try:
950 pid2 = os.fork()
951 except:
952 pid2 = None
953 if pid2 == 0:
954 os.chdir("/")
955 for fd in range(0, 256):
956 try:
957 os.close(fd)
958 except:
959 pass
960 os.open("/dev/null", os.O_RDWR)
961 os.dup2(0, 1)
962 os.dup2(0, 2)
963 os.execvp(prog, args)
964 os._exit(1)
965 else:
966 w.write(str(pid2 or 0))
967 w.close()
968 os._exit(0)
969 os.close(w)
970 r = os.fdopen(r)
971 daemon_pid = int(r.read())
972 r.close()
973 os.waitpid(pid, 0)
974 return daemon_pid
976 def spawn_vnc(display):
977 """Spawns a vncviewer that listens on the specified display. On success,
978 returns the port that the vncviewer is listening on and sets the global
979 vncpid. On failure, returns 0. Note that vncviewer is daemonized.
980 """
981 vncargs = (["vncviewer", "-log", "*:stdout:0",
982 "-listen", "%d" % (VNC_BASE_PORT + display) ])
983 global vncpid
984 vncpid = daemonize("vncviewer", vncargs)
985 if vncpid == 0:
986 return 0
988 return VNC_BASE_PORT + display
990 def preprocess_vnc(vals):
991 """If vnc was specified, spawn a vncviewer in listen mode
992 and pass its address to the domain on the kernel command line.
993 """
994 if vals.dryrun: return
995 if vals.vncviewer:
996 vnc_display = choose_vnc_display()
997 if not vnc_display:
998 warn("No free vnc display")
999 return
1000 print 'VNC=', vnc_display
1001 vnc_port = spawn_vnc(vnc_display)
1002 if vnc_port > 0:
1003 vnc_host = get_host_addr()
1004 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
1005 vals.extra = vnc + ' ' + vals.extra
1007 def preprocess(vals):
1008 preprocess_disk(vals)
1009 preprocess_pci(vals)
1010 preprocess_ioports(vals)
1011 preprocess_ip(vals)
1012 preprocess_nfs(vals)
1013 preprocess_vnc(vals)
1014 preprocess_vtpm(vals)
1015 preprocess_access_control(vals)
1018 def comma_sep_kv_to_dict(c):
1019 """Convert comma-separated, equals-separated key-value pairs into a
1020 dictionary.
1021 """
1022 d = {}
1023 c = c.strip()
1024 if len(c) > 0:
1025 a = c.split(',')
1026 for b in a:
1027 if b.find('=') == -1:
1028 err("%s should be a pair, separated by an equals sign." % b)
1029 (k, v) = b.split('=', 1)
1030 k = k.strip()
1031 v = v.strip()
1032 d[k] = v
1033 return d
1036 def make_domain(opts, config):
1037 """Create, build and start a domain.
1039 @param opts: options
1040 @param config: configuration
1041 @return: domain id
1042 @rtype: int
1043 """
1045 try:
1046 dominfo = server.xend.domain.create(config)
1047 except xmlrpclib.Fault, ex:
1048 import signal
1049 if vncpid:
1050 os.kill(vncpid, signal.SIGKILL)
1051 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
1052 err("the domain '%s' does not exist." % ex.faultString)
1053 else:
1054 err("%s" % ex.faultString)
1055 except Exception, ex:
1056 # main.py has good error messages that let the user know what failed.
1057 # unless the error is a create.py specific thing, it should be handled
1058 # at main. The purpose of this general-case 'Exception' handler is to
1059 # clean up create.py specific processes/data but since create.py does
1060 # not know what to do with the error, it should pass it up.
1061 import signal
1062 if vncpid:
1063 os.kill(vncpid, signal.SIGKILL)
1064 raise
1066 dom = sxp.child_value(dominfo, 'name')
1068 try:
1069 server.xend.domain.waitForDevices(dom)
1070 except xmlrpclib.Fault, ex:
1071 server.xend.domain.destroy(dom)
1072 err("%s" % ex.faultString)
1073 except:
1074 server.xend.domain.destroy(dom)
1075 err("Device creation failed for domain %s" % dom)
1077 if not opts.vals.paused:
1078 try:
1079 server.xend.domain.unpause(dom)
1080 except:
1081 server.xend.domain.destroy(dom)
1082 err("Failed to unpause domain %s" % dom)
1083 opts.info("Started domain %s" % (dom))
1084 return int(sxp.child_value(dominfo, 'domid'))
1087 def get_xauthority():
1088 xauth = os.getenv("XAUTHORITY")
1089 if not xauth:
1090 home = os.getenv("HOME")
1091 if not home:
1092 import posix, pwd
1093 home = pwd.getpwuid(posix.getuid())[5]
1094 xauth = home + "/.Xauthority"
1095 return xauth
1098 def parseCommandLine(argv):
1099 gopts.reset()
1100 args = gopts.parse(argv)
1102 if gopts.vals.help or gopts.vals.help_config:
1103 if gopts.vals.help_config:
1104 print gopts.val_usage()
1105 return (None, None)
1107 if not gopts.vals.display:
1108 gopts.vals.display = os.getenv("DISPLAY")
1110 if not gopts.vals.xauthority:
1111 gopts.vals.xauthority = get_xauthority()
1113 gopts.is_xml = False
1115 # Process remaining args as config variables.
1116 for arg in args:
1117 if '=' in arg:
1118 (var, val) = arg.strip().split('=', 1)
1119 gopts.setvar(var.strip(), val.strip())
1120 if gopts.vals.config:
1121 config = gopts.vals.config
1122 else:
1123 try:
1124 gopts.load_defconfig()
1125 preprocess(gopts.vals)
1126 if not gopts.getopt('name') and gopts.getopt('defconfig'):
1127 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
1128 config = make_config(gopts.vals)
1129 except XMLFileError, ex:
1130 XMLFile = ex.getFile()
1131 gopts.is_xml = True
1132 config = ex.getFile()
1134 return (gopts, config)
1137 def check_domain_label(config, verbose):
1138 """All that we need to check here is that the domain label exists and
1139 is not null when security is on. Other error conditions are
1140 handled when the config file is parsed.
1141 """
1142 answer = 0
1143 default_label = None
1144 secon = 0
1145 if security.on():
1146 default_label = security.ssidref2label(security.NULL_SSIDREF)
1147 secon = 1
1149 # get the domain acm_label
1150 dom_label = None
1151 dom_name = None
1152 for x in sxp.children(config):
1153 if sxp.name(x) == 'security':
1154 dom_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1155 if sxp.name(x) == 'name':
1156 dom_name = sxp.child0(x)
1158 # sanity check on domain label
1159 if verbose:
1160 print "Checking domain:"
1161 if (not secon) and (not dom_label):
1162 answer = 1
1163 if verbose:
1164 print " %s: PERMITTED" % (dom_name)
1165 elif (secon) and (dom_label) and (dom_label != default_label):
1166 answer = 1
1167 if verbose:
1168 print " %s: PERMITTED" % (dom_name)
1169 else:
1170 print " %s: DENIED" % (dom_name)
1171 if not secon:
1172 print " --> Security off, but domain labeled"
1173 else:
1174 print " --> Domain not labeled"
1175 answer = 0
1177 return answer
1179 def config_security_check(config, verbose):
1180 """Checks each resource listed in the config to see if the active
1181 policy will permit creation of a new domain using the config.
1182 Returns 1 if the config passes all tests, otherwise 0.
1183 """
1184 answer = 1
1186 # get the domain acm_label
1187 domain_label = None
1188 domain_policy = None
1189 for x in sxp.children(config):
1190 if sxp.name(x) == 'security':
1191 domain_label = sxp.child_value(sxp.name(sxp.child0(x)), 'label')
1192 domain_policy = sxp.child_value(sxp.name(sxp.child0(x)), 'policy')
1194 # if no domain label, use default
1195 if not domain_label and security.on():
1196 try:
1197 domain_label = security.ssidref2label(security.NULL_SSIDREF)
1198 except:
1199 import traceback
1200 traceback.print_exc(limit=1)
1201 return 0
1202 domain_policy = 'NULL'
1203 elif not domain_label:
1204 domain_label = ""
1205 domain_policy = 'NULL'
1207 if verbose:
1208 print "Checking resources:"
1210 # build a list of all resources in the config file
1211 resources = []
1212 for x in sxp.children(config):
1213 if sxp.name(x) == 'device':
1214 if sxp.name(sxp.child0(x)) == 'vbd':
1215 resources.append(sxp.child_value(sxp.child0(x), 'uname'))
1217 # perform a security check on each resource
1218 for resource in resources:
1219 try:
1220 security.res_security_check(resource, domain_label)
1221 if verbose:
1222 print " %s: PERMITTED" % (resource)
1224 except security.ACMError:
1225 print " %s: DENIED" % (resource)
1226 (poltype, res_label, res_policy) = security.get_res_label(resource)
1227 if not res_label:
1228 res_label = ""
1229 print " --> res: %s (%s:%s)" % (str(res_label),
1230 str(poltype), str(res_policy))
1231 print " --> dom: %s (%s:%s)" % (str(domain_label),
1232 str(poltype), str(domain_policy))
1234 answer = 0
1236 return answer
1238 def create_security_check(config):
1239 passed = 0
1240 try:
1241 if check_domain_label(config, verbose=0):
1242 if config_security_check(config, verbose=0):
1243 passed = 1
1244 else:
1245 print "Checking resources: (skipped)"
1246 except security.ACMError:
1247 sys.exit(-1)
1249 return passed
1251 def help():
1252 return str(gopts)
1254 def main(argv):
1255 is_xml = False
1257 try:
1258 (opts, config) = parseCommandLine(argv)
1259 except StandardError, ex:
1260 err(str(ex))
1262 if not opts:
1263 return
1265 if not opts.is_xml:
1266 if type(config) == str:
1267 try:
1268 config = sxp.parse(file(config))[0]
1269 except IOError, exn:
1270 raise OptionError("Cannot read file %s: %s" % (config, exn[1]))
1272 if serverType == SERVER_XEN_API:
1273 from xen.xm.xenapi_create import sxp2xml
1274 sxp2xml_inst = sxp2xml()
1275 doc = sxp2xml_inst.convert_sxp_to_xml(config, transient=True)
1277 if opts.vals.dryrun and not opts.is_xml:
1278 SXPPrettyPrint.prettyprint(config)
1280 if opts.vals.xmldryrun and serverType == SERVER_XEN_API:
1281 from xml.dom.ext import PrettyPrint as XMLPrettyPrint
1282 XMLPrettyPrint(doc)
1284 if opts.vals.dryrun or opts.vals.xmldryrun:
1285 return
1287 if opts.vals.console_autoconnect:
1288 do_console(sxp.child_value(config, 'name', -1))
1290 if serverType == SERVER_XEN_API:
1291 from xen.xm.xenapi_create import xenapi_create
1292 xenapi_create_inst = xenapi_create()
1293 if opts.is_xml:
1294 vm_refs = xenapi_create_inst.create(filename = config,
1295 skipdtd = opts.vals.skipdtd)
1296 else:
1297 vm_refs = xenapi_create_inst.create(document = doc,
1298 skipdtd = opts.vals.skipdtd)
1300 map(lambda vm_ref: server.xenapi.VM.start(vm_ref, 0), vm_refs)
1301 elif not opts.is_xml:
1302 if not create_security_check(config):
1303 raise security.ACMError(
1304 'Security Configuration prevents domain from starting')
1305 dom = make_domain(opts, config)
1307 def do_console(domain_name):
1308 cpid = os.fork()
1309 if cpid != 0:
1310 for i in range(10):
1311 # Catch failure of the create process
1312 time.sleep(1)
1313 (p, rv) = os.waitpid(cpid, os.WNOHANG)
1314 if os.WIFEXITED(rv):
1315 if os.WEXITSTATUS(rv) != 0:
1316 sys.exit(os.WEXITSTATUS(rv))
1317 try:
1318 # Acquire the console of the created dom
1319 if serverType == SERVER_XEN_API:
1320 domid = server.xenapi.VM.get_domid(
1321 get_single_vm(domain_name))
1322 else:
1323 dom = server.xend.domain(domain_name)
1324 domid = int(sxp.child_value(dom, 'domid', '-1'))
1325 console.execConsole(domid)
1326 except:
1327 pass
1328 print("Could not start console\n");
1329 sys.exit(0)
1331 if __name__ == '__main__':
1332 main(sys.argv)