ia64/xen-unstable

view tools/python/xen/xm/create.py @ 9726:4b168245977a

Allow cpus parameter to xm create to accept a range string by removing the
restriction of cpus accepting only integers.

Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
author Ryan Harper <ryanh@us.ibm.com>
date Fri Apr 14 21:15:38 2006 +0100 (2006-04-14)
parents 9fcfdab04aa9
children 681a18bf049e
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 string
25 import sys
26 import socket
27 import commands
28 import time
29 import re
30 import xmlrpclib
32 from xen.xend import sxp
33 from xen.xend import PrettyPrint
34 import xen.xend.XendClient
35 from xen.xend.XendClient import server
36 from xen.xend.XendBootloader import bootloader
37 from xen.util import blkif
39 from xen.xm.opts import *
41 import console
44 gopts = Opts(use="""[options] [vars]
46 Create a domain.
48 Domain creation parameters can be set by command-line switches, from
49 a python configuration script or an SXP config file. See documentation
50 for --defconfig, --config. Configuration variables can be set using
51 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
53 """)
55 gopts.opt('help', short='h',
56 fn=set_true, default=0,
57 use="Print this help.")
59 gopts.opt('help_config',
60 fn=set_true, default=0,
61 use="Print help for the configuration script.")
63 gopts.opt('quiet', short='q',
64 fn=set_true, default=0,
65 use="Quiet.")
67 gopts.opt('path', val='PATH',
68 fn=set_value, default='.:/etc/xen',
69 use="""Search path for configuration scripts.
70 The value of PATH is a colon-separated directory list.""")
72 gopts.opt('defconfig', short='f', val='FILE',
73 fn=set_value, default='xmdefconfig',
74 use="""Use the given Python configuration script.
75 The configuration script is loaded after arguments have been processed.
76 Each command-line option sets a configuration variable named after
77 its long option name, and these variables are placed in the
78 environment of the script before it is loaded.
79 Variables for options that may be repeated have list values.
80 Other variables can be set using VAR=VAL on the command line.
82 After the script is loaded, option values that were not set on the
83 command line are replaced by the values set in the script.""")
85 gopts.default('defconfig')
87 gopts.opt('config', short='F', val='FILE',
88 fn=set_value, default=None,
89 use="""Domain configuration to use (SXP).
90 SXP is the underlying configuration format used by Xen.
91 SXP configurations can be hand-written or generated from Python configuration
92 scripts, using the -n (dryrun) option to print the configuration.""")
94 gopts.opt('dryrun', short='n',
95 fn=set_true, default=0,
96 use="""Dry run - print the configuration but don't create the domain.
97 Loads the configuration script, creates the SXP configuration and prints it.""")
99 gopts.opt('paused', short='p',
100 fn=set_true, default=0,
101 use='Leave the domain paused after it is created.')
103 gopts.opt('console_autoconnect', short='c',
104 fn=set_true, default=0,
105 use="Connect to the console after the domain is created.")
107 gopts.var('vncviewer', val='no|yes',
108 fn=set_bool, default=None,
109 use="""Spawn a vncviewer listening for a vnc server in the domain.
110 The address of the vncviewer is passed to the domain on the kernel command
111 line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
112 A display value with a free port is chosen if possible.
113 Only valid when vnc=1.
114 """)
116 gopts.var('name', val='NAME',
117 fn=set_value, default=None,
118 use="Domain name. Must be unique.")
120 gopts.var('bootloader', val='FILE',
121 fn=set_value, default=None,
122 use="Path to bootloader.")
124 gopts.var('bootentry', val='NAME',
125 fn=set_value, default=None,
126 use="Entry to boot via boot loader")
128 gopts.var('kernel', val='FILE',
129 fn=set_value, default=None,
130 use="Path to kernel image.")
132 gopts.var('ramdisk', val='FILE',
133 fn=set_value, default='',
134 use="Path to ramdisk.")
136 gopts.var('features', val='FEATURES',
137 fn=set_value, default='',
138 use="Features to enable in guest kernel")
140 gopts.var('builder', val='FUNCTION',
141 fn=set_value, default='linux',
142 use="Function to use to build the domain.")
144 gopts.var('memory', val='MEMORY',
145 fn=set_int, default=128,
146 use="Domain memory in MB.")
148 gopts.var('ssidref', val='SSIDREF',
149 fn=set_u32, default=0,
150 use="Security Identifier.")
152 gopts.var('maxmem', val='MEMORY',
153 fn=set_int, default=None,
154 use="Maximum domain memory in MB.")
156 gopts.var('cpu', val='CPU',
157 fn=set_int, default=None,
158 use="CPU to run the VCPU0 on.")
160 gopts.var('cpus', val='CPUS',
161 fn=set_value, default=None,
162 use="CPUS to run the domain on.")
164 gopts.var('pae', val='PAE',
165 fn=set_int, default=0,
166 use="Disable or enable PAE of HVM domain.")
168 gopts.var('acpi', val='ACPI',
169 fn=set_int, default=0,
170 use="Disable or enable ACPI of HVM domain.")
172 gopts.var('apic', val='APIC',
173 fn=set_int, default=0,
174 use="Disable or enable APIC of HVM domain.")
176 gopts.var('vcpus', val='VCPUS',
177 fn=set_int, default=1,
178 use="# of Virtual CPUS in domain.")
180 gopts.var('cpu_weight', val='WEIGHT',
181 fn=set_float, default=None,
182 use="""Set the new domain's cpu weight.
183 WEIGHT is a float that controls the domain's share of the cpu.""")
185 gopts.var('restart', val='onreboot|always|never',
186 fn=set_value, default=None,
187 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
188 instead.
190 Whether the domain should be restarted on exit.
191 - onreboot: restart on exit with shutdown code reboot
192 - always: always restart on exit, ignore exit code
193 - never: never restart on exit, ignore exit code""")
195 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
196 fn=set_value, default=None,
197 use="""Behaviour when a domain exits with reason 'poweroff'.
198 - destroy: the domain is cleaned up as normal;
199 - restart: a new domain is started in place of the old one;
200 - preserve: no clean-up is done until the domain is manually
201 destroyed (using xm destroy, for example);
202 - rename-restart: the old domain is not cleaned up, but is
203 renamed and a new domain started in its place.
204 """)
206 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
207 fn=set_value, default=None,
208 use="""Behaviour when a domain exits with reason 'reboot'.
209 - destroy: the domain is cleaned up as normal;
210 - restart: a new domain is started in place of the old one;
211 - preserve: no clean-up is done until the domain is manually
212 destroyed (using xm destroy, for example);
213 - rename-restart: the old domain is not cleaned up, but is
214 renamed and a new domain started in its place.
215 """)
217 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
218 fn=set_value, default=None,
219 use="""Behaviour when a domain exits with reason 'crash'.
220 - destroy: the domain is cleaned up as normal;
221 - restart: a new domain is started in place of the old one;
222 - preserve: no clean-up is done until the domain is manually
223 destroyed (using xm destroy, for example);
224 - rename-restart: the old domain is not cleaned up, but is
225 renamed and a new domain started in its place.
226 """)
228 gopts.var('blkif', val='no|yes',
229 fn=set_bool, default=0,
230 use="Make the domain a block device backend.")
232 gopts.var('netif', val='no|yes',
233 fn=set_bool, default=0,
234 use="Make the domain a network interface backend.")
236 gopts.var('tpmif', val='no|yes',
237 fn=append_value, default=0,
238 use="Make the domain a TPM interface backend.")
240 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
241 fn=append_value, default=[],
242 use="""Add a disk device to a domain. The physical device is DEV,
243 which is exported to the domain as VDEV. The disk is read-only if MODE
244 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
245 backend driver domain to use for the disk.
246 The option may be repeated to add more than one disk.""")
248 gopts.var('pci', val='BUS:DEV.FUNC',
249 fn=append_value, default=[],
250 use="""Add a PCI device to a domain, using given params (in hex).
251 For example 'pci=c0:02.1a'.
252 The option may be repeated to add more than one pci device.""")
254 gopts.var('ioports', val='FROM[-TO]',
255 fn=append_value, default=[],
256 use="""Add a legacy I/O range to a domain, using given params (in hex).
257 For example 'ioports=02f8-02ff'.
258 The option may be repeated to add more than one i/o range.""")
260 gopts.var('irq', val='IRQ',
261 fn=append_value, default=[],
262 use="""Add an IRQ (interrupt line) to a domain.
263 For example 'irq=7'.
264 This option may be repeated to add more than one IRQ.""")
266 gopts.var('usb', val='PATH',
267 fn=append_value, default=[],
268 use="""Add a physical USB port to a domain, as specified by the path
269 to that port. This option may be repeated to add more than one port.""")
271 gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,backend=DOM,vifname=NAME",
272 fn=append_value, default=[],
273 use="""Add a network interface with the given MAC address and bridge.
274 The vif is configured by calling the given configuration script.
275 If type is not specified, default is netfront not ioemu device.
276 If mac is not specified a random MAC address is used.
277 If not specified then the network backend chooses it's own MAC address.
278 If bridge is not specified the first bridge found is used.
279 If script is not specified the default script is used.
280 If backend is not specified the default backend driver domain is used.
281 If vifname is not specified the backend virtual interface will have name vifD.N
282 where D is the domain id and N is the interface id.
283 This option may be repeated to add more than one vif.
284 Specifying vifs will increase the number of interfaces as needed.""")
286 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
287 fn=append_value, default=[],
288 use="""Add a TPM interface. On the backend side use the given
289 instance as virtual TPM instance. The given number is merely the
290 preferred instance number. The hotplug script will determine
291 which instance number will actually be assigned to the domain.
292 The associtation between virtual machine and the TPM instance
293 number can be found in /etc/xen/vtpm.db. Use the backend in the
294 given domain.""")
296 gopts.var('nics', val="NUM",
297 fn=set_int, default=-1,
298 use="""DEPRECATED. Use empty vif entries instead.
300 Set the number of network interfaces.
301 Use the vif option to define interface parameters, otherwise
302 defaults are used. Specifying vifs will increase the
303 number of interfaces as needed.""")
305 gopts.var('root', val='DEVICE',
306 fn=set_value, default='',
307 use="""Set the root= parameter on the kernel command line.
308 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
310 gopts.var('extra', val="ARGS",
311 fn=set_value, default='',
312 use="Set extra arguments to append to the kernel command line.")
314 gopts.var('ip', val='IPADDR',
315 fn=set_value, default='',
316 use="Set the kernel IP interface address.")
318 gopts.var('gateway', val="IPADDR",
319 fn=set_value, default='',
320 use="Set the kernel IP gateway.")
322 gopts.var('netmask', val="MASK",
323 fn=set_value, default = '',
324 use="Set the kernel IP netmask.")
326 gopts.var('hostname', val="NAME",
327 fn=set_value, default='',
328 use="Set the kernel IP hostname.")
330 gopts.var('interface', val="INTF",
331 fn=set_value, default="eth0",
332 use="Set the kernel IP interface name.")
334 gopts.var('dhcp', val="off|dhcp",
335 fn=set_value, default='off',
336 use="Set the kernel dhcp option.")
338 gopts.var('nfs_server', val="IPADDR",
339 fn=set_value, default=None,
340 use="Set the address of the NFS server for NFS root.")
342 gopts.var('nfs_root', val="PATH",
343 fn=set_value, default=None,
344 use="Set the path of the root NFS directory.")
346 gopts.var('device_model', val='FILE',
347 fn=set_value, default='',
348 use="Path to device model program.")
350 gopts.var('fda', val='FILE',
351 fn=set_value, default='',
352 use="Path to fda")
354 gopts.var('fdb', val='FILE',
355 fn=set_value, default='',
356 use="Path to fdb")
358 gopts.var('serial', val='FILE',
359 fn=set_value, default='',
360 use="Path to serial or pty or vc")
362 gopts.var('localtime', val='no|yes',
363 fn=set_bool, default=0,
364 use="Is RTC set to localtime?")
366 gopts.var('stdvga', val='no|yes',
367 fn=set_bool, default=0,
368 use="Use std vga or cirrhus logic graphics")
370 gopts.var('isa', val='no|yes',
371 fn=set_bool, default=0,
372 use="Simulate an ISA only system?")
374 gopts.var('cdrom', val='FILE',
375 fn=set_value, default='',
376 use="Path to cdrom")
378 gopts.var('boot', val="a|b|c|d",
379 fn=set_value, default='c',
380 use="Default boot device")
382 gopts.var('nographic', val='no|yes',
383 fn=set_bool, default=0,
384 use="Should device models use graphics?")
386 gopts.var('ne2000', val='no|yes',
387 fn=set_bool, default=0,
388 use="Should device models use ne2000?")
390 gopts.var('audio', val='no|yes',
391 fn=set_bool, default=0,
392 use="Should device models enable audio?")
394 gopts.var('vnc', val='',
395 fn=set_value, default=None,
396 use="""Should the device model use VNC?""")
398 gopts.var('sdl', val='',
399 fn=set_value, default=None,
400 use="""Should the device model use SDL?""")
402 gopts.var('display', val='DISPLAY',
403 fn=set_value, default=None,
404 use="X11 display to use")
406 gopts.var('xauthority', val='XAUTHORITY',
407 fn=set_value, default=None,
408 use="X11 Authority to use")
410 gopts.var('uuid', val='',
411 fn=set_value, default=None,
412 use="""xenstore UUID (universally unique identifier) to use. One
413 will be randomly generated if this option is not set, just like MAC
414 addresses for virtual network interfaces. This must be a unique
415 value across the entire cluster.""")
418 def err(msg):
419 """Print an error to stderr and exit.
420 """
421 print >>sys.stderr, "Error:", msg
422 sys.exit(1)
425 def warn(msg):
426 """Print a warning to stdout.
427 """
428 print >>sys.stderr, "Warning:", msg
431 def strip(pre, s):
432 """Strip prefix 'pre' if present.
433 """
434 if s.startswith(pre):
435 return s[len(pre):]
436 else:
437 return s
439 def configure_image(vals):
440 """Create the image config.
441 """
442 config_image = [ vals.builder ]
443 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
444 if vals.ramdisk:
445 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
446 if vals.cmdline_ip:
447 cmdline_ip = strip('ip=', vals.cmdline_ip)
448 config_image.append(['ip', cmdline_ip])
449 if vals.root:
450 cmdline_root = strip('root=', vals.root)
451 config_image.append(['root', cmdline_root])
452 if vals.extra:
453 config_image.append(['args', vals.extra])
454 if vals.features:
455 config_image.append(['features', vals.features])
457 if vals.builder == 'hvm':
458 configure_hvm(config_image, vals)
460 return config_image
462 def configure_disks(config_devs, vals):
463 """Create the config for disks (virtual block devices).
464 """
465 for (uname, dev, mode, backend) in vals.disk:
466 config_vbd = ['vbd',
467 ['uname', uname],
468 ['dev', dev ],
469 ['mode', mode ] ]
470 if backend:
471 config_vbd.append(['backend', backend])
472 config_devs.append(['device', config_vbd])
474 def configure_pci(config_devs, vals):
475 """Create the config for pci devices.
476 """
477 config_pci = []
478 for (domain, bus, slot, func) in vals.pci:
479 config_pci.append(['dev', ['domain', domain], ['bus', bus], \
480 ['slot', slot], ['func', func]])
482 if len(config_pci)>0:
483 config_pci.insert(0, 'pci')
484 config_devs.append(['device', config_pci])
486 def configure_ioports(config_devs, vals):
487 """Create the config for legacy i/o ranges.
488 """
489 for (io_from, io_to) in vals.ioports:
490 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
491 config_devs.append(['device', config_ioports])
493 def configure_irq(config_devs, vals):
494 """Create the config for irqs.
495 """
496 for irq in vals.irq:
497 config_irq = ['irq', ['irq', irq]]
498 config_devs.append(['device', config_irq])
500 def configure_usb(config_devs, vals):
501 for path in vals.usb:
502 config_usb = ['usb', ['path', path]]
503 config_devs.append(['device', config_usb])
505 def configure_vtpm(config_devs, vals):
506 """Create the config for virtual TPM interfaces.
507 """
508 vtpm = vals.vtpm
509 vtpm_n = 1
510 for idx in range(0, vtpm_n):
511 if idx < len(vtpm):
512 d = vtpm[idx]
513 instance = d.get('instance')
514 if instance == "VTPMD":
515 instance = "0"
516 else:
517 if instance != None:
518 try:
519 if int(instance) == 0:
520 err('VM config error: vTPM instance must not be 0.')
521 except ValueError:
522 err('Vm config error: could not parse instance number.')
523 backend = d.get('backend')
524 config_vtpm = ['vtpm']
525 if instance:
526 config_vtpm.append(['pref_instance', instance])
527 if backend:
528 config_vtpm.append(['backend', backend])
529 config_devs.append(['device', config_vtpm])
532 def configure_vifs(config_devs, vals):
533 """Create the config for virtual network interfaces.
534 """
536 vifs = vals.vif
537 vifs_n = len(vifs)
539 if hasattr(vals, 'nics'):
540 if vals.nics > 0:
541 warn("The nics option is deprecated. Please use an empty vif "
542 "entry instead:\n\n vif = [ '' ]\n")
543 for _ in range(vifs_n, vals.nics):
544 vifs.append('')
545 vifs_n = len(vifs)
546 elif vals.nics == 0:
547 warn("The nics option is deprecated. Please remove it.")
549 for c in vifs:
550 d = comma_sep_kv_to_dict(c)
551 config_vif = ['vif']
553 def f(k):
554 if k not in ['backend', 'bridge', 'ip', 'mac', 'script', 'type',
555 'vifname', 'rate']:
556 err('Invalid vif option: ' + k)
558 config_vif.append([k, d[k]])
560 map(f, d.keys())
561 config_devs.append(['device', config_vif])
564 def configure_hvm(config_image, vals):
565 """Create the config for HVM devices.
566 """
567 args = [ 'device_model', 'pae', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
568 'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'audio',
569 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'acpi', 'apic',
570 'xauthority' ]
571 for a in args:
572 if (vals.__dict__[a]):
573 config_image.append([a, vals.__dict__[a]])
575 def run_bootloader(vals):
576 if not os.access(vals.bootloader, os.X_OK):
577 err("Bootloader isn't executable")
578 if len(vals.disk) < 1:
579 err("No disks configured and boot loader requested")
580 (uname, dev, mode, backend) = vals.disk[0]
581 file = blkif.blkdev_uname_to_file(uname)
583 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
584 vals.vcpus, vals.bootentry)
586 def make_config(vals):
587 """Create the domain configuration.
588 """
590 config = ['vm']
592 def add_conf(n):
593 if hasattr(vals, n):
594 v = getattr(vals, n)
595 if v:
596 config.append([n, v])
598 map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
599 'on_poweroff', 'on_reboot', 'on_crash', 'vcpus'])
601 if vals.uuid is not None:
602 config.append(['uuid', vals.uuid])
603 if vals.cpu is not None:
604 config.append(['cpu', vals.cpu])
605 if vals.cpus is not None:
606 config.append(['cpus', vals.cpus])
607 if vals.cpu_weight is not None:
608 config.append(['cpu_weight', vals.cpu_weight])
609 if vals.blkif:
610 config.append(['backend', ['blkif']])
611 if vals.netif:
612 config.append(['backend', ['netif']])
613 if vals.tpmif:
614 config.append(['backend', ['tpmif']])
616 if vals.bootloader:
617 config.append(['bootloader', vals.bootloader])
618 config_image = run_bootloader(vals)
619 else:
620 config_image = configure_image(vals)
621 config.append(['image', config_image])
623 config_devs = []
624 configure_disks(config_devs, vals)
625 configure_pci(config_devs, vals)
626 configure_ioports(config_devs, vals)
627 configure_irq(config_devs, vals)
628 configure_vifs(config_devs, vals)
629 configure_usb(config_devs, vals)
630 configure_vtpm(config_devs, vals)
631 config += config_devs
633 return config
635 def preprocess_disk(vals):
636 if not vals.disk: return
637 disk = []
638 for v in vals.disk:
639 d = v.split(',')
640 n = len(d)
641 if n == 3:
642 d.append(None)
643 elif n == 4:
644 pass
645 else:
646 err('Invalid disk specifier: ' + v)
647 disk.append(d)
648 vals.disk = disk
650 def preprocess_pci(vals):
651 if not vals.pci: return
652 pci = []
653 for pci_dev_str in vals.pci:
654 pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
655 r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
656 r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
657 r"(?P<func>[0-9a-fA-F])", pci_dev_str)
658 if pci_match!=None:
659 pci_dev_info = pci_match.groupdict('0')
660 try:
661 pci.append( ('0x'+pci_dev_info['domain'], \
662 '0x'+pci_dev_info['bus'], \
663 '0x'+pci_dev_info['slot'], \
664 '0x'+pci_dev_info['func']))
665 except IndexError:
666 err('Error in PCI slot syntax "%s"'%(pci_dev_str))
667 vals.pci = pci
669 def preprocess_ioports(vals):
670 if not vals.ioports: return
671 ioports = []
672 for v in vals.ioports:
673 d = v.split('-')
674 if len(d) < 1 or len(d) > 2:
675 err('Invalid i/o port range specifier: ' + v)
676 if len(d) == 1:
677 d.append(d[0])
678 # Components are in hex: add hex specifier.
679 hexd = map(lambda v: '0x'+v, d)
680 ioports.append(hexd)
681 vals.ioports = ioports
683 def preprocess_vtpm(vals):
684 if not vals.vtpm: return
685 vtpms = []
686 for vtpm in vals.vtpm:
687 d = {}
688 a = vtpm.split(',')
689 for b in a:
690 (k, v) = b.strip().split('=', 1)
691 k = k.strip()
692 v = v.strip()
693 if k not in ['backend', 'instance']:
694 err('Invalid vtpm specifier: ' + vtpm)
695 d[k] = v
696 vtpms.append(d)
697 vals.vtpm = vtpms
699 def preprocess_ip(vals):
700 if vals.ip or vals.dhcp != 'off':
701 dummy_nfs_server = '1.2.3.4'
702 ip = (vals.ip
703 + ':' + (vals.nfs_server or dummy_nfs_server)
704 + ':' + vals.gateway
705 + ':' + vals.netmask
706 + ':' + vals.hostname
707 + ':' + vals.interface
708 + ':' + vals.dhcp)
709 else:
710 ip = ''
711 vals.cmdline_ip = ip
713 def preprocess_nfs(vals):
714 if not vals.nfs_root: return
715 if not vals.nfs_server:
716 err('Must set nfs root and nfs server')
717 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
718 vals.extra = nfs + ' ' + vals.extra
721 def get_host_addr():
722 host = socket.gethostname()
723 addr = socket.gethostbyname(host)
724 return addr
726 VNC_BASE_PORT = 5500
728 def choose_vnc_display():
729 """Try to choose a free vnc display.
730 """
731 def netstat_local_ports():
732 """Run netstat to get a list of the local ports in use.
733 """
734 l = os.popen("netstat -nat").readlines()
735 r = []
736 # Skip 2 lines of header.
737 for x in l[2:]:
738 # Local port is field 3.
739 y = x.split()[3]
740 # Field is addr:port, split off the port.
741 y = y.split(':')[-1]
742 r.append(int(y))
743 return r
745 ports = netstat_local_ports()
746 for d in range(1, 100):
747 port = VNC_BASE_PORT + d
748 if port in ports: continue
749 return d
750 return None
752 vncpid = None
754 def spawn_vnc(display):
755 vncargs = (["vncviewer", "-log", "*:stdout:0",
756 "-listen", "%d" % (VNC_BASE_PORT + display) ])
757 global vncpid
758 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
760 return VNC_BASE_PORT + display
762 def preprocess_vnc(vals):
763 """If vnc was specified, spawn a vncviewer in listen mode
764 and pass its address to the domain on the kernel command line.
765 """
766 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
767 vnc_display = choose_vnc_display()
768 if not vnc_display:
769 warn("No free vnc display")
770 return
771 print 'VNC=', vnc_display
772 vnc_port = spawn_vnc(vnc_display)
773 if vnc_port > 0:
774 vnc_host = get_host_addr()
775 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
776 vals.extra = vnc + ' ' + vals.extra
778 def preprocess(vals):
779 if not vals.kernel and not vals.bootloader:
780 err("No kernel specified")
781 preprocess_disk(vals)
782 preprocess_pci(vals)
783 preprocess_ioports(vals)
784 preprocess_ip(vals)
785 preprocess_nfs(vals)
786 preprocess_vnc(vals)
787 preprocess_vtpm(vals)
790 def comma_sep_kv_to_dict(c):
791 """Convert comma-separated, equals-separated key-value pairs into a
792 dictionary.
793 """
794 d = {}
795 c = c.strip()
796 if len(c) > 0:
797 a = c.split(',')
798 for b in a:
799 if b.find('=') == -1:
800 err("%s should be a pair, separated by an equals sign." % b)
801 (k, v) = b.split('=', 1)
802 k = k.strip()
803 v = v.strip()
804 d[k] = v
805 return d
808 def make_domain(opts, config):
809 """Create, build and start a domain.
811 @param opts: options
812 @param config: configuration
813 @return: domain id
814 @rtype: int
815 """
817 try:
818 dominfo = server.xend.domain.create(config)
819 except xmlrpclib.Fault, ex:
820 import signal
821 if vncpid:
822 os.kill(vncpid, signal.SIGKILL)
823 if ex.faultCode == xen.xend.XendClient.ERROR_INVALID_DOMAIN:
824 err("the domain '%s' does not exist." % ex.faultString)
825 else:
826 err("%s" % ex.faultString)
827 except Exception, ex:
828 import signal
829 if vncpid:
830 os.kill(vncpid, signal.SIGKILL)
831 err(str(ex))
833 dom = sxp.child_value(dominfo, 'name')
835 try:
836 server.xend.domain.waitForDevices(dom)
837 except xmlrpclib.Fault, ex:
838 server.xend.domain.destroy(dom)
839 err("%s" % ex.faultString)
840 except:
841 server.xend.domain.destroy(dom)
842 err("Device creation failed for domain %s" % dom)
844 if not opts.vals.paused:
845 try:
846 server.xend.domain.unpause(dom)
847 except:
848 server.xend.domain.destroy(dom)
849 err("Failed to unpause domain %s" % dom)
850 opts.info("Started domain %s" % (dom))
851 return int(sxp.child_value(dominfo, 'domid'))
854 def get_xauthority():
855 xauth = os.getenv("XAUTHORITY")
856 if not xauth:
857 home = os.getenv("HOME")
858 if not home:
859 import posix, pwd
860 home = pwd.getpwuid(posix.getuid())[5]
861 xauth = home + "/.Xauthority"
862 return xauth
865 def parseCommandLine(argv):
866 gopts.reset()
867 args = gopts.parse(argv)
868 if gopts.vals.help:
869 gopts.usage()
870 if gopts.vals.help or gopts.vals.help_config:
871 gopts.load_defconfig(help=1)
872 if gopts.vals.help or gopts.vals.help_config:
873 return (None, None)
875 if not gopts.vals.display:
876 gopts.vals.display = os.getenv("DISPLAY")
878 if not gopts.vals.xauthority:
879 gopts.vals.xauthority = get_xauthority()
881 # Process remaining args as config variables.
882 for arg in args:
883 if '=' in arg:
884 (var, val) = arg.strip().split('=', 1)
885 gopts.setvar(var.strip(), val.strip())
886 if gopts.vals.config:
887 config = gopts.vals.config
888 else:
889 gopts.load_defconfig()
890 preprocess(gopts.vals)
891 if not gopts.getopt('name') and gopts.getopt('defconfig'):
892 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
893 config = make_config(gopts.vals)
895 return (gopts, config)
898 def main(argv):
899 try:
900 (opts, config) = parseCommandLine(argv)
901 except StandardError, ex:
902 err(str(ex))
904 if not opts:
905 return
907 if opts.vals.dryrun:
908 PrettyPrint.prettyprint(config)
909 else:
910 dom = make_domain(opts, config)
911 if opts.vals.console_autoconnect:
912 console.execConsole(dom)
914 if __name__ == '__main__':
915 main(sys.argv)