ia64/xen-unstable

view tools/python/xen/xm/create.py @ 7639:c352591ebfdf

Add support for the ioport_permission dom0 op to xend and xm

xm now accepts a parameter 'ioports' that accepts a hex ioport
or ioport range, in the form 02f8[-02ff]

Signed-off-by: Jody Belka <knew (at) pimb (dot) org>
author kaf24@firebug.cl.cam.ac.uk
date Sun Nov 06 16:40:33 2005 +0100 (2005-11-06)
parents a48ac90f99f1
children 270469d40f02
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 XenSource Ltd
18 #============================================================================
20 """Domain creation.
21 """
22 import random
23 import os
24 import os.path
25 import string
26 import sys
27 import socket
28 import commands
29 import time
31 import xen.lowlevel.xc
33 from xen.xend import sxp
34 from xen.xend import PrettyPrint
35 from xen.xend.XendClient import server, XendError
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('load', short='L', val='FILE',
95 fn=set_value, default=None,
96 use='Domain saved state to load.')
98 gopts.opt('dryrun', short='n',
99 fn=set_true, default=0,
100 use="""Dry run - print the configuration but don't create the domain.
101 Loads the configuration script, creates the SXP configuration and prints it.""")
103 gopts.opt('paused', short='p',
104 fn=set_true, default=0,
105 use='Leave the domain paused after it is created.')
107 gopts.opt('console_autoconnect', short='c',
108 fn=set_true, default=0,
109 use="Connect to the console after the domain is created.")
111 gopts.var('vncviewer', val='no|yes',
112 fn=set_bool, default=None,
113 use="""Spawn a vncviewer listening for a vnc server in the domain.
114 The address of the vncviewer is passed to the domain on the kernel command
115 line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
116 A display value with a free port is chosen if possible.
117 Only valid when vnc=1.
118 """)
120 gopts.var('name', val='NAME',
121 fn=set_value, default=None,
122 use="Domain name. Must be unique.")
124 gopts.var('bootloader', val='FILE',
125 fn=set_value, default=None,
126 use="Path to bootloader.")
128 gopts.var('bootentry', val='NAME',
129 fn=set_value, default=None,
130 use="Entry to boot via boot loader")
132 gopts.var('kernel', val='FILE',
133 fn=set_value, default=None,
134 use="Path to kernel image.")
136 gopts.var('ramdisk', val='FILE',
137 fn=set_value, default='',
138 use="Path to ramdisk.")
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 domain on.")
160 gopts.var('lapic', val='LAPIC',
161 fn=set_int, default=0,
162 use="Disable or enable local APIC of VMX domain.")
164 gopts.var('vcpus', val='VCPUS',
165 fn=set_int, default=1,
166 use="# of Virtual CPUS in domain.")
168 gopts.var('cpu_weight', val='WEIGHT',
169 fn=set_float, default=None,
170 use="""Set the new domain's cpu weight.
171 WEIGHT is a float that controls the domain's share of the cpu.""")
173 gopts.var('restart', val='onreboot|always|never',
174 fn=set_value, default=None,
175 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
176 instead.
178 Whether the domain should be restarted on exit.
179 - onreboot: restart on exit with shutdown code reboot
180 - always: always restart on exit, ignore exit code
181 - never: never restart on exit, ignore exit code""")
183 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
184 fn=set_value, default=None,
185 use="""Behaviour when a domain exits with reason 'poweroff'.
186 - destroy: the domain is cleaned up as normal;
187 - restart: a new domain is started in place of the old one;
188 - preserve: no clean-up is done until the domain is manually
189 destroyed (using xm destroy, for example);
190 - rename-restart: the old domain is not cleaned up, but is
191 renamed and a new domain started in its place.
192 """)
194 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
195 fn=set_value, default=None,
196 use="""Behaviour when a domain exits with reason 'reboot'.
197 - destroy: the domain is cleaned up as normal;
198 - restart: a new domain is started in place of the old one;
199 - preserve: no clean-up is done until the domain is manually
200 destroyed (using xm destroy, for example);
201 - rename-restart: the old domain is not cleaned up, but is
202 renamed and a new domain started in its place.
203 """)
205 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
206 fn=set_value, default=None,
207 use="""Behaviour when a domain exits with reason 'crash'.
208 - destroy: the domain is cleaned up as normal;
209 - restart: a new domain is started in place of the old one;
210 - preserve: no clean-up is done until the domain is manually
211 destroyed (using xm destroy, for example);
212 - rename-restart: the old domain is not cleaned up, but is
213 renamed and a new domain started in its place.
214 """)
216 gopts.var('blkif', val='no|yes',
217 fn=set_bool, default=0,
218 use="Make the domain a block device backend.")
220 gopts.var('netif', val='no|yes',
221 fn=set_bool, default=0,
222 use="Make the domain a network interface backend.")
224 gopts.var('tpmif', val='frontend=DOM',
225 fn=append_value, default=[],
226 use="""Make the domain a TPM interface backend. If frontend is given,
227 the frontend in that domain is connected to this backend (not
228 completely implemented, yet)""")
230 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
231 fn=append_value, default=[],
232 use="""Add a disk device to a domain. The physical device is DEV,
233 which is exported to the domain as VDEV. The disk is read-only if MODE
234 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
235 backend driver domain to use for the disk.
236 The option may be repeated to add more than one disk.""")
238 gopts.var('pci', val='BUS,DEV,FUNC',
239 fn=append_value, default=[],
240 use="""Add a PCI device to a domain, using given params (in hex).
241 For example '-pci c0,02,1a'.
242 The option may be repeated to add more than one pci device.""")
244 gopts.var('ioports', val='FROM[-TO]',
245 fn=append_value, default=[],
246 use="""Add a legacy I/O range to a domain, using given params (in hex).
247 For example '-ioports 02f8-02ff'.
248 The option may be repeated to add more than one i/o range.""")
250 gopts.var('usb', val='PATH',
251 fn=append_value, default=[],
252 use="""Add a physical USB port to a domain, as specified by the path
253 to that port. This option may be repeated to add more than one port.""")
255 gopts.var('ipaddr', val="IPADDR",
256 fn=append_value, default=[],
257 use="Add an IP address to the domain.")
259 gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
260 fn=append_value, default=[],
261 use="""Add a network interface with the given MAC address and bridge.
262 The vif is configured by calling the given configuration script.
263 If mac is not specified a random MAC address is used.
264 The MAC address of the backend interface can be selected with be_mac.
265 If not specified then the network backend chooses it's own MAC address.
266 If bridge is not specified the first bridge found is used.
267 If script is not specified the default script is used.
268 If backend is not specified the default backend driver domain is used.
269 If vifname is not specified the backend virtual interface will have name vifD.N
270 where D is the domain id and N is the interface id.
271 This option may be repeated to add more than one vif.
272 Specifying vifs will increase the number of interfaces as needed.""")
274 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
275 fn=append_value, default=[],
276 use="""Add a tpm interface. On the backend side us the the given
277 instance as virtual TPM instance. Use the backend in the given
278 domain.""")
280 gopts.var('nics', val="NUM",
281 fn=set_int, default=1,
282 use="""Set the number of network interfaces.
283 Use the vif option to define interface parameters, otherwise
284 defaults are used. Specifying vifs will increase the
285 number of interfaces as needed.""")
287 gopts.var('root', val='DEVICE',
288 fn=set_value, default='',
289 use="""Set the root= parameter on the kernel command line.
290 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
292 gopts.var('extra', val="ARGS",
293 fn=set_value, default='',
294 use="Set extra arguments to append to the kernel command line.")
296 gopts.var('ip', val='IPADDR',
297 fn=set_value, default='',
298 use="Set the kernel IP interface address.")
300 gopts.var('gateway', val="IPADDR",
301 fn=set_value, default='',
302 use="Set the kernel IP gateway.")
304 gopts.var('netmask', val="MASK",
305 fn=set_value, default = '',
306 use="Set the kernel IP netmask.")
308 gopts.var('hostname', val="NAME",
309 fn=set_value, default='',
310 use="Set the kernel IP hostname.")
312 gopts.var('interface', val="INTF",
313 fn=set_value, default="eth0",
314 use="Set the kernel IP interface name.")
316 gopts.var('dhcp', val="off|dhcp",
317 fn=set_value, default='off',
318 use="Set the kernel dhcp option.")
320 gopts.var('nfs_server', val="IPADDR",
321 fn=set_value, default=None,
322 use="Set the address of the NFS server for NFS root.")
324 gopts.var('nfs_root', val="PATH",
325 fn=set_value, default=None,
326 use="Set the path of the root NFS directory.")
328 gopts.var('device_model', val='FILE',
329 fn=set_value, default='',
330 use="Path to device model program.")
332 gopts.var('fda', val='FILE',
333 fn=set_value, default='',
334 use="Path to fda")
336 gopts.var('fdb', val='FILE',
337 fn=set_value, default='',
338 use="Path to fdb")
340 gopts.var('serial', val='FILE',
341 fn=set_value, default='',
342 use="Path to serial or pty or vc")
344 gopts.var('localtime', val='no|yes',
345 fn=set_bool, default=0,
346 use="Is RTC set to localtime?")
348 gopts.var('stdvga', val='no|yes',
349 fn=set_bool, default=0,
350 use="Use std vga or cirrhus logic graphics")
352 gopts.var('isa', val='no|yes',
353 fn=set_bool, default=0,
354 use="Simulate an ISA only system?")
356 gopts.var('cdrom', val='FILE',
357 fn=set_value, default='',
358 use="Path to cdrom")
360 gopts.var('macaddr', val='MACADDR',
361 fn=set_value, default='',
362 use="Macaddress of the first network interface")
364 gopts.var('boot', val="a|b|c|d",
365 fn=set_value, default='c',
366 use="Default boot device")
368 gopts.var('nographic', val='no|yes',
369 fn=set_bool, default=0,
370 use="Should device models use graphics?")
372 gopts.var('ne2000', val='no|yes',
373 fn=set_bool, default=0,
374 use="Should device models use ne2000?")
376 gopts.var('vnc', val='',
377 fn=set_value, default=None,
378 use="""Should the device model use VNC?""")
380 gopts.var('sdl', val='',
381 fn=set_value, default=None,
382 use="""Should the device model use SDL?""")
384 gopts.var('display', val='DISPLAY',
385 fn=set_value, default=None,
386 use="X11 display to use")
389 def err(msg):
390 """Print an error to stderr and exit.
391 """
392 print >>sys.stderr, "Error:", msg
393 sys.exit(1)
396 def warn(msg):
397 """Print a warning to stdout.
398 """
399 print >>sys.stderr, "Warning:", msg
402 def strip(pre, s):
403 """Strip prefix 'pre' if present.
404 """
405 if s.startswith(pre):
406 return s[len(pre):]
407 else:
408 return s
410 def configure_image(vals):
411 """Create the image config.
412 """
413 config_image = [ vals.builder ]
414 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
415 if vals.ramdisk:
416 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
417 if vals.cmdline_ip:
418 cmdline_ip = strip('ip=', vals.cmdline_ip)
419 config_image.append(['ip', cmdline_ip])
420 if vals.root:
421 cmdline_root = strip('root=', vals.root)
422 config_image.append(['root', cmdline_root])
423 if vals.extra:
424 config_image.append(['args', vals.extra])
425 if vals.vcpus:
426 config_image.append(['vcpus', vals.vcpus])
427 return config_image
429 def configure_disks(config_devs, vals):
430 """Create the config for disks (virtual block devices).
431 """
432 for (uname, dev, mode, backend) in vals.disk:
433 config_vbd = ['vbd',
434 ['uname', uname],
435 ['dev', dev ],
436 ['mode', mode ] ]
437 if backend:
438 config_vbd.append(['backend', backend])
439 config_devs.append(['device', config_vbd])
441 def configure_pci(config_devs, vals):
442 """Create the config for pci devices.
443 """
444 for (bus, dev, func) in vals.pci:
445 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
446 config_devs.append(['device', config_pci])
448 def configure_ioports(config_devs, vals):
449 """Create the config for legacy i/o ranges.
450 """
451 for (io_from, io_to) in vals.ioports:
452 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
453 config_devs.append(['device', config_ioports])
455 def configure_usb(config_devs, vals):
456 for path in vals.usb:
457 config_usb = ['usb', ['path', path]]
458 config_devs.append(['device', config_usb])
460 def configure_vtpm(config_devs, vals):
461 """Create the config for virtual TPM interfaces.
462 """
463 vtpm = vals.vtpm
464 vtpm_n = 1
465 for idx in range(0, vtpm_n):
466 if idx < len(vtpm):
467 d = vtpm[idx]
468 instance = d.get('instance')
469 if instance == "VTPMD":
470 instance = "0"
471 else:
472 try:
473 if int(instance) == 0:
474 err('VM config error: vTPM instance must not be 0.')
475 except ValueError:
476 err('Vm config error: could not parse instance number.')
477 backend = d.get('backend')
478 config_vtpm = ['vtpm']
479 if instance:
480 config_vtpm.append(['instance', instance])
481 if backend:
482 config_vtpm.append(['backend', backend])
483 config_devs.append(['device', config_vtpm])
485 def configure_tpmif(config_devs, vals):
486 """Create the config for virtual TPM interfaces.
487 """
488 tpmif = vals.tpmif
489 tpmif_n = 1
490 for idx in range(0, tpmif_n):
491 if idx < len(tpmif):
492 d = tpmif[idx]
493 frontend = d.get('frontend')
494 config_tpmif = ['tpmif']
495 if frontend:
496 config_tpmif.append(['frontend', frontend])
497 config_devs.append(['device', config_tpmif])
500 def randomMAC():
501 """Generate a random MAC address.
503 Uses OUI (Organizationally Unique Identifier) AA:00:00, an
504 unassigned one that used to belong to DEC. The OUI list is
505 available at 'standards.ieee.org'.
507 The remaining 3 fields are random, with the first bit of the first
508 random field set 0.
510 @return: MAC address string
511 """
512 mac = [ 0xaa, 0x00, 0x00,
513 random.randint(0x00, 0x7f),
514 random.randint(0x00, 0xff),
515 random.randint(0x00, 0xff) ]
516 return ':'.join(map(lambda x: "%02x" % x, mac))
518 def configure_vifs(config_devs, vals):
519 """Create the config for virtual network interfaces.
520 """
521 vifs = vals.vif
522 vifs_n = max(vals.nics, len(vifs))
524 for idx in range(0, vifs_n):
525 if idx < len(vifs):
526 d = vifs[idx]
527 mac = d.get('mac')
528 if not mac:
529 mac = randomMAC()
530 be_mac = d.get('be_mac')
531 bridge = d.get('bridge')
532 script = d.get('script')
533 backend = d.get('backend')
534 ip = d.get('ip')
535 vifname = d.get('vifname')
536 else:
538 mac = randomMAC()
539 be_mac = None
540 bridge = None
541 script = None
542 backend = None
543 ip = None
544 vifname = None
545 config_vif = ['vif']
546 config_vif.append(['mac', mac])
547 if vifname:
548 config_vif.append(['vifname', vifname])
549 if be_mac:
550 config_vif.append(['be_mac', be_mac])
551 if bridge:
552 config_vif.append(['bridge', bridge])
553 if script:
554 config_vif.append(['script', script])
555 if backend:
556 config_vif.append(['backend', backend])
557 if ip:
558 config_vif.append(['ip', ip])
559 config_devs.append(['device', config_vif])
561 def configure_vfr(config, vals):
562 if not vals.ipaddr: return
563 config_vfr = ['vfr']
564 idx = 0 # No way of saying which IP is for which vif?
565 for ip in vals.ipaddr:
566 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
567 config.append(config_vfr)
569 def configure_vmx(config_image, vals):
570 """Create the config for VMX devices.
571 """
572 args = [ 'device_model', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
573 'localtime', 'serial', 'macaddr', 'stdvga', 'isa', 'nographic',
574 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'lapic']
575 for a in args:
576 if (vals.__dict__[a]):
577 config_image.append([a, vals.__dict__[a]])
579 def run_bootloader(vals):
580 if not os.access(vals.bootloader, os.X_OK):
581 err("Bootloader isn't executable")
582 if len(vals.disk) < 1:
583 err("No disks configured and boot loader requested")
584 (uname, dev, mode, backend) = vals.disk[0]
585 file = blkif.blkdev_uname_to_file(uname)
587 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
588 vals.vcpus, vals.blentry)
590 def make_config(vals):
591 """Create the domain configuration.
592 """
594 config = ['vm']
596 def add_conf(n):
597 if hasattr(vals, n):
598 v = getattr(vals, n)
599 if v:
600 config.append([n, v])
602 map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
603 'on_poweroff', 'on_reboot', 'on_crash'])
605 if vals.cpu is not None:
606 config.append(['cpu', vals.cpu])
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 configure_vmx(config_image, vals)
622 config.append(['image', config_image])
624 config_devs = []
625 configure_disks(config_devs, vals)
626 configure_pci(config_devs, vals)
627 configure_ioports(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 v in vals.pci:
654 d = v.split(',')
655 if len(d) != 3:
656 err('Invalid pci specifier: ' + v)
657 # Components are in hex: add hex specifier.
658 hexd = map(lambda v: '0x'+v, d)
659 pci.append(hexd)
660 vals.pci = pci
662 def preprocess_ioports(vals):
663 if not vals.ioports: return
664 ioports = []
665 for v in vals.ioports:
666 d = v.split('-')
667 if len(d) < 1 || len(d) > 2:
668 err('Invalid i/o port range specifier: ' + v)
669 if len(d) == 1:
670 d.append(d[0])
671 # Components are in hex: add hex specifier.
672 hexd = map(lambda v: '0x'+v, d)
673 ioports.append(hexd)
674 vals.ioports = ioports
676 def preprocess_vifs(vals):
677 if not vals.vif: return
678 vifs = []
679 for vif in vals.vif:
680 d = {}
681 a = vif.split(',')
682 for b in a:
683 (k, v) = b.strip().split('=', 1)
684 k = k.strip()
685 v = v.strip()
686 if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
687 err('Invalid vif specifier: ' + vif)
688 d[k] = v
689 vifs.append(d)
690 vals.vif = vifs
692 def preprocess_vtpm(vals):
693 if not vals.vtpm: return
694 vtpms = []
695 for vtpm in vals.vtpm:
696 d = {}
697 a = vtpm.split(',')
698 for b in a:
699 (k, v) = b.strip().split('=', 1)
700 k = k.strip()
701 v = v.strip()
702 if k not in ['backend', 'instance']:
703 err('Invalid vtpm specifier: ' + vtpm)
704 d[k] = v
705 vtpms.append(d)
706 vals.vtpm = vtpms
708 def preprocess_tpmif(vals):
709 if not vals.tpmif: return
710 tpmifs = []
711 for tpmif in vals.tpmif:
712 d = {}
713 a = tpmif.split(',')
714 for b in a:
715 (k, v) = b.strip().split('=', 1)
716 k = k.strip()
717 v = v.strip()
718 if k not in ['frontend']:
719 err('Invalid tpmif specifier: ' + vtpm)
720 d[k] = v
721 tpmifs.append(d)
722 vals.tpmif = tpmifs
724 def preprocess_ip(vals):
725 if vals.ip or vals.dhcp != 'off':
726 dummy_nfs_server = '1.2.3.4'
727 ip = (vals.ip
728 + ':' + (vals.nfs_server or dummy_nfs_server)
729 + ':' + vals.gateway
730 + ':' + vals.netmask
731 + ':' + vals.hostname
732 + ':' + vals.interface
733 + ':' + vals.dhcp)
734 else:
735 ip = ''
736 vals.cmdline_ip = ip
738 def preprocess_nfs(vals):
739 if not vals.nfs_root: return
740 if not vals.nfs_server:
741 err('Must set nfs root and nfs server')
742 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
743 vals.extra = nfs + ' ' + vals.extra
746 def get_host_addr():
747 host = socket.gethostname()
748 addr = socket.gethostbyname(host)
749 return addr
751 VNC_BASE_PORT = 5500
753 def choose_vnc_display():
754 """Try to choose a free vnc display.
755 """
756 def netstat_local_ports():
757 """Run netstat to get a list of the local ports in use.
758 """
759 l = os.popen("netstat -nat").readlines()
760 r = []
761 # Skip 2 lines of header.
762 for x in l[2:]:
763 # Local port is field 3.
764 y = x.split()[3]
765 # Field is addr:port, split off the port.
766 y = y.split(':')[-1]
767 r.append(int(y))
768 return r
770 ports = netstat_local_ports()
771 for d in range(1, 100):
772 port = VNC_BASE_PORT + d
773 if port in ports: continue
774 return d
775 return None
777 vncpid = None
779 def spawn_vnc(display):
780 vncargs = (["vncviewer" + "-log", "*:stdout:0",
781 "-listen", "%d" % (VNC_BASE_PORT + display) ])
782 global vncpid
783 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
785 return VNC_BASE_PORT + display
787 def preprocess_vnc(vals):
788 """If vnc was specified, spawn a vncviewer in listen mode
789 and pass its address to the domain on the kernel command line.
790 """
791 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
792 vnc_display = choose_vnc_display()
793 if not vnc_display:
794 warn("No free vnc display")
795 return
796 print 'VNC=', vnc_display
797 vnc_port = spawn_vnc(vnc_display)
798 if vnc_port > 0:
799 vnc_host = get_host_addr()
800 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
801 vals.extra = vnc + ' ' + vals.extra
803 def preprocess(vals):
804 if not vals.kernel:
805 err("No kernel specified")
806 preprocess_disk(vals)
807 preprocess_pci(vals)
808 preprocess_ioports(vals)
809 preprocess_vifs(vals)
810 preprocess_ip(vals)
811 preprocess_nfs(vals)
812 preprocess_vnc(vals)
813 preprocess_vtpm(vals)
814 preprocess_tpmif(vals)
816 def make_domain(opts, config):
817 """Create, build and start a domain.
819 @param opts: options
820 @param config: configuration
821 @return: domain id
822 @rtype: int
823 """
825 try:
826 if opts.vals.load:
827 filename = os.path.abspath(opts.vals.load)
828 dominfo = server.xend_domain_restore(filename, config)
829 else:
830 dominfo = server.xend_domain_create(config)
831 except XendError, ex:
832 import signal
833 if vncpid:
834 os.kill(vncpid, signal.SIGKILL)
835 err(str(ex))
837 dom = sxp.child_value(dominfo, 'name')
839 if not opts.vals.paused:
840 if server.xend_domain_unpause(dom) < 0:
841 server.xend_domain_destroy(dom)
842 err("Failed to unpause domain %s" % dom)
843 opts.info("Started domain %s" % (dom))
844 return int(sxp.child_value(dominfo, 'domid'))
846 def get_dom0_alloc():
847 """Return current allocation memory of dom0 (in MB). Return 0 on error"""
848 PROC_XEN_BALLOON = "/proc/xen/balloon"
850 f = open(PROC_XEN_BALLOON, "r")
851 line = f.readline()
852 for x in line.split():
853 for n in x:
854 if not n.isdigit():
855 break
856 else:
857 f.close()
858 return int(x)/1024
859 f.close()
860 return 0
862 def balloon_out(dom0_min_mem, opts):
863 """Balloon out memory from dom0 if necessary"""
864 SLACK = 4
865 timeout = 20 # 2s
866 ret = 1
868 xc = xen.lowlevel.xc.new()
869 free_mem = xc.physinfo()['free_pages'] / 256
870 domU_need_mem = opts.vals.memory + SLACK
872 # we already have enough free memory, return success
873 if free_mem >= domU_need_mem:
874 del xc
875 return 0
877 dom0_cur_alloc = get_dom0_alloc()
878 dom0_new_alloc = dom0_cur_alloc - (domU_need_mem - free_mem)
879 if dom0_new_alloc < dom0_min_mem:
880 dom0_new_alloc = dom0_min_mem
882 server.xend_domain_mem_target_set(0, dom0_new_alloc)
884 while timeout > 0:
885 time.sleep(0.1) # sleep 100ms
887 free_mem = xc.physinfo()['free_pages'] / 256
888 if free_mem >= domU_need_mem:
889 ret = 0
890 break
891 timeout -= 1
893 del xc
894 return ret
897 def parseCommandLine(argv):
898 opts = gopts
899 args = opts.parse(argv)
900 if opts.vals.help:
901 opts.usage()
902 if opts.vals.help or opts.vals.help_config:
903 opts.load_defconfig(help=1)
904 if opts.vals.help or opts.vals.help_config:
905 return (None, None)
907 if not opts.vals.display:
908 opts.vals.display = os.getenv("DISPLAY")
910 # Process remaining args as config variables.
911 for arg in args:
912 if '=' in arg:
913 (var, val) = arg.strip().split('=', 1)
914 gopts.setvar(var.strip(), val.strip())
915 if opts.vals.config:
916 config = opts.vals.config
917 else:
918 opts.load_defconfig()
919 preprocess(opts.vals)
920 if not opts.getopt('name') and opts.getopt('defconfig'):
921 opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
922 config = make_config(opts.vals)
924 return (opts, config)
927 def main(argv):
928 random.seed()
930 (opts, config) = parseCommandLine(argv)
932 if not opts:
933 return
935 if opts.vals.dryrun:
936 PrettyPrint.prettyprint(config)
937 else:
938 from xen.xend import XendRoot
940 xroot = XendRoot.instance()
942 dom0_min_mem = xroot.get_dom0_min_mem()
943 if dom0_min_mem != 0:
944 if balloon_out(dom0_min_mem, opts):
945 print >>sys.stderr, "error: cannot allocate enough memory for domain"
946 sys.exit(1)
948 dom = make_domain(opts, config)
949 if opts.vals.console_autoconnect:
950 console.execConsole(dom)
952 if __name__ == '__main__':
953 main(sys.argv)