ia64/xen-unstable

view tools/python/xen/xm/create.py @ 7598:a48ac90f99f1

Fix the DISPLAY configration that breaks vmx domain creation with SDL
on vncserver or remote X. Changeset 7547 added a test of
opts.vals.display when getenv("DISPLAY"); because there is a default
value:localhost:0, that causes the test always to be true.

Signed-off-by: Xiaofeng Ling <xiaofeng.ling@intel.com>
author kaf24@firebug.cl.cam.ac.uk
date Tue Nov 01 10:31:25 2005 +0100 (2005-11-01)
parents bcccadcc56e5
children c352591ebfdf
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('usb', val='PATH',
245 fn=append_value, default=[],
246 use="""Add a physical USB port to a domain, as specified by the path
247 to that port. This option may be repeated to add more than one port.""")
249 gopts.var('ipaddr', val="IPADDR",
250 fn=append_value, default=[],
251 use="Add an IP address to the domain.")
253 gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
254 fn=append_value, default=[],
255 use="""Add a network interface with the given MAC address and bridge.
256 The vif is configured by calling the given configuration script.
257 If mac is not specified a random MAC address is used.
258 The MAC address of the backend interface can be selected with be_mac.
259 If not specified then the network backend chooses it's own MAC address.
260 If bridge is not specified the first bridge found is used.
261 If script is not specified the default script is used.
262 If backend is not specified the default backend driver domain is used.
263 If vifname is not specified the backend virtual interface will have name vifD.N
264 where D is the domain id and N is the interface id.
265 This option may be repeated to add more than one vif.
266 Specifying vifs will increase the number of interfaces as needed.""")
268 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
269 fn=append_value, default=[],
270 use="""Add a tpm interface. On the backend side us the the given
271 instance as virtual TPM instance. Use the backend in the given
272 domain.""")
274 gopts.var('nics', val="NUM",
275 fn=set_int, default=1,
276 use="""Set the number of network interfaces.
277 Use the vif option to define interface parameters, otherwise
278 defaults are used. Specifying vifs will increase the
279 number of interfaces as needed.""")
281 gopts.var('root', val='DEVICE',
282 fn=set_value, default='',
283 use="""Set the root= parameter on the kernel command line.
284 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
286 gopts.var('extra', val="ARGS",
287 fn=set_value, default='',
288 use="Set extra arguments to append to the kernel command line.")
290 gopts.var('ip', val='IPADDR',
291 fn=set_value, default='',
292 use="Set the kernel IP interface address.")
294 gopts.var('gateway', val="IPADDR",
295 fn=set_value, default='',
296 use="Set the kernel IP gateway.")
298 gopts.var('netmask', val="MASK",
299 fn=set_value, default = '',
300 use="Set the kernel IP netmask.")
302 gopts.var('hostname', val="NAME",
303 fn=set_value, default='',
304 use="Set the kernel IP hostname.")
306 gopts.var('interface', val="INTF",
307 fn=set_value, default="eth0",
308 use="Set the kernel IP interface name.")
310 gopts.var('dhcp', val="off|dhcp",
311 fn=set_value, default='off',
312 use="Set the kernel dhcp option.")
314 gopts.var('nfs_server', val="IPADDR",
315 fn=set_value, default=None,
316 use="Set the address of the NFS server for NFS root.")
318 gopts.var('nfs_root', val="PATH",
319 fn=set_value, default=None,
320 use="Set the path of the root NFS directory.")
322 gopts.var('device_model', val='FILE',
323 fn=set_value, default='',
324 use="Path to device model program.")
326 gopts.var('fda', val='FILE',
327 fn=set_value, default='',
328 use="Path to fda")
330 gopts.var('fdb', val='FILE',
331 fn=set_value, default='',
332 use="Path to fdb")
334 gopts.var('serial', val='FILE',
335 fn=set_value, default='',
336 use="Path to serial or pty or vc")
338 gopts.var('localtime', val='no|yes',
339 fn=set_bool, default=0,
340 use="Is RTC set to localtime?")
342 gopts.var('stdvga', val='no|yes',
343 fn=set_bool, default=0,
344 use="Use std vga or cirrhus logic graphics")
346 gopts.var('isa', val='no|yes',
347 fn=set_bool, default=0,
348 use="Simulate an ISA only system?")
350 gopts.var('cdrom', val='FILE',
351 fn=set_value, default='',
352 use="Path to cdrom")
354 gopts.var('macaddr', val='MACADDR',
355 fn=set_value, default='',
356 use="Macaddress of the first network interface")
358 gopts.var('boot', val="a|b|c|d",
359 fn=set_value, default='c',
360 use="Default boot device")
362 gopts.var('nographic', val='no|yes',
363 fn=set_bool, default=0,
364 use="Should device models use graphics?")
366 gopts.var('ne2000', val='no|yes',
367 fn=set_bool, default=0,
368 use="Should device models use ne2000?")
370 gopts.var('vnc', val='',
371 fn=set_value, default=None,
372 use="""Should the device model use VNC?""")
374 gopts.var('sdl', val='',
375 fn=set_value, default=None,
376 use="""Should the device model use SDL?""")
378 gopts.var('display', val='DISPLAY',
379 fn=set_value, default=None,
380 use="X11 display to use")
383 def err(msg):
384 """Print an error to stderr and exit.
385 """
386 print >>sys.stderr, "Error:", msg
387 sys.exit(1)
390 def warn(msg):
391 """Print a warning to stdout.
392 """
393 print >>sys.stderr, "Warning:", msg
396 def strip(pre, s):
397 """Strip prefix 'pre' if present.
398 """
399 if s.startswith(pre):
400 return s[len(pre):]
401 else:
402 return s
404 def configure_image(vals):
405 """Create the image config.
406 """
407 config_image = [ vals.builder ]
408 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
409 if vals.ramdisk:
410 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
411 if vals.cmdline_ip:
412 cmdline_ip = strip('ip=', vals.cmdline_ip)
413 config_image.append(['ip', cmdline_ip])
414 if vals.root:
415 cmdline_root = strip('root=', vals.root)
416 config_image.append(['root', cmdline_root])
417 if vals.extra:
418 config_image.append(['args', vals.extra])
419 if vals.vcpus:
420 config_image.append(['vcpus', vals.vcpus])
421 return config_image
423 def configure_disks(config_devs, vals):
424 """Create the config for disks (virtual block devices).
425 """
426 for (uname, dev, mode, backend) in vals.disk:
427 config_vbd = ['vbd',
428 ['uname', uname],
429 ['dev', dev ],
430 ['mode', mode ] ]
431 if backend:
432 config_vbd.append(['backend', backend])
433 config_devs.append(['device', config_vbd])
435 def configure_pci(config_devs, vals):
436 """Create the config for pci devices.
437 """
438 for (bus, dev, func) in vals.pci:
439 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
440 config_devs.append(['device', config_pci])
442 def configure_usb(config_devs, vals):
443 for path in vals.usb:
444 config_usb = ['usb', ['path', path]]
445 config_devs.append(['device', config_usb])
447 def configure_vtpm(config_devs, vals):
448 """Create the config for virtual TPM interfaces.
449 """
450 vtpm = vals.vtpm
451 vtpm_n = 1
452 for idx in range(0, vtpm_n):
453 if idx < len(vtpm):
454 d = vtpm[idx]
455 instance = d.get('instance')
456 if instance == "VTPMD":
457 instance = "0"
458 else:
459 try:
460 if int(instance) == 0:
461 err('VM config error: vTPM instance must not be 0.')
462 except ValueError:
463 err('Vm config error: could not parse instance number.')
464 backend = d.get('backend')
465 config_vtpm = ['vtpm']
466 if instance:
467 config_vtpm.append(['instance', instance])
468 if backend:
469 config_vtpm.append(['backend', backend])
470 config_devs.append(['device', config_vtpm])
472 def configure_tpmif(config_devs, vals):
473 """Create the config for virtual TPM interfaces.
474 """
475 tpmif = vals.tpmif
476 tpmif_n = 1
477 for idx in range(0, tpmif_n):
478 if idx < len(tpmif):
479 d = tpmif[idx]
480 frontend = d.get('frontend')
481 config_tpmif = ['tpmif']
482 if frontend:
483 config_tpmif.append(['frontend', frontend])
484 config_devs.append(['device', config_tpmif])
487 def randomMAC():
488 """Generate a random MAC address.
490 Uses OUI (Organizationally Unique Identifier) AA:00:00, an
491 unassigned one that used to belong to DEC. The OUI list is
492 available at 'standards.ieee.org'.
494 The remaining 3 fields are random, with the first bit of the first
495 random field set 0.
497 @return: MAC address string
498 """
499 mac = [ 0xaa, 0x00, 0x00,
500 random.randint(0x00, 0x7f),
501 random.randint(0x00, 0xff),
502 random.randint(0x00, 0xff) ]
503 return ':'.join(map(lambda x: "%02x" % x, mac))
505 def configure_vifs(config_devs, vals):
506 """Create the config for virtual network interfaces.
507 """
508 vifs = vals.vif
509 vifs_n = max(vals.nics, len(vifs))
511 for idx in range(0, vifs_n):
512 if idx < len(vifs):
513 d = vifs[idx]
514 mac = d.get('mac')
515 if not mac:
516 mac = randomMAC()
517 be_mac = d.get('be_mac')
518 bridge = d.get('bridge')
519 script = d.get('script')
520 backend = d.get('backend')
521 ip = d.get('ip')
522 vifname = d.get('vifname')
523 else:
525 mac = randomMAC()
526 be_mac = None
527 bridge = None
528 script = None
529 backend = None
530 ip = None
531 vifname = None
532 config_vif = ['vif']
533 config_vif.append(['mac', mac])
534 if vifname:
535 config_vif.append(['vifname', vifname])
536 if be_mac:
537 config_vif.append(['be_mac', be_mac])
538 if bridge:
539 config_vif.append(['bridge', bridge])
540 if script:
541 config_vif.append(['script', script])
542 if backend:
543 config_vif.append(['backend', backend])
544 if ip:
545 config_vif.append(['ip', ip])
546 config_devs.append(['device', config_vif])
548 def configure_vfr(config, vals):
549 if not vals.ipaddr: return
550 config_vfr = ['vfr']
551 idx = 0 # No way of saying which IP is for which vif?
552 for ip in vals.ipaddr:
553 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
554 config.append(config_vfr)
556 def configure_vmx(config_image, vals):
557 """Create the config for VMX devices.
558 """
559 args = [ 'device_model', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
560 'localtime', 'serial', 'macaddr', 'stdvga', 'isa', 'nographic',
561 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'lapic']
562 for a in args:
563 if (vals.__dict__[a]):
564 config_image.append([a, vals.__dict__[a]])
566 def run_bootloader(vals):
567 if not os.access(vals.bootloader, os.X_OK):
568 err("Bootloader isn't executable")
569 if len(vals.disk) < 1:
570 err("No disks configured and boot loader requested")
571 (uname, dev, mode, backend) = vals.disk[0]
572 file = blkif.blkdev_uname_to_file(uname)
574 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
575 vals.vcpus, vals.blentry)
577 def make_config(vals):
578 """Create the domain configuration.
579 """
581 config = ['vm']
583 def add_conf(n):
584 if hasattr(vals, n):
585 v = getattr(vals, n)
586 if v:
587 config.append([n, v])
589 map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
590 'on_poweroff', 'on_reboot', 'on_crash'])
592 if vals.cpu is not None:
593 config.append(['cpu', vals.cpu])
594 if vals.cpu_weight is not None:
595 config.append(['cpu_weight', vals.cpu_weight])
596 if vals.blkif:
597 config.append(['backend', ['blkif']])
598 if vals.netif:
599 config.append(['backend', ['netif']])
600 if vals.tpmif:
601 config.append(['backend', ['tpmif']])
603 if vals.bootloader:
604 config.append(['bootloader', vals.bootloader])
605 config_image = run_bootloader(vals)
606 else:
607 config_image = configure_image(vals)
608 configure_vmx(config_image, vals)
609 config.append(['image', config_image])
611 config_devs = []
612 configure_disks(config_devs, vals)
613 configure_pci(config_devs, vals)
614 configure_vifs(config_devs, vals)
615 configure_usb(config_devs, vals)
616 configure_vtpm(config_devs, vals)
617 config += config_devs
619 return config
621 def preprocess_disk(vals):
622 if not vals.disk: return
623 disk = []
624 for v in vals.disk:
625 d = v.split(',')
626 n = len(d)
627 if n == 3:
628 d.append(None)
629 elif n == 4:
630 pass
631 else:
632 err('Invalid disk specifier: ' + v)
633 disk.append(d)
634 vals.disk = disk
636 def preprocess_pci(vals):
637 if not vals.pci: return
638 pci = []
639 for v in vals.pci:
640 d = v.split(',')
641 if len(d) != 3:
642 err('Invalid pci specifier: ' + v)
643 # Components are in hex: add hex specifier.
644 hexd = map(lambda v: '0x'+v, d)
645 pci.append(hexd)
646 vals.pci = pci
648 def preprocess_vifs(vals):
649 if not vals.vif: return
650 vifs = []
651 for vif in vals.vif:
652 d = {}
653 a = vif.split(',')
654 for b in a:
655 (k, v) = b.strip().split('=', 1)
656 k = k.strip()
657 v = v.strip()
658 if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
659 err('Invalid vif specifier: ' + vif)
660 d[k] = v
661 vifs.append(d)
662 vals.vif = vifs
664 def preprocess_vtpm(vals):
665 if not vals.vtpm: return
666 vtpms = []
667 for vtpm in vals.vtpm:
668 d = {}
669 a = vtpm.split(',')
670 for b in a:
671 (k, v) = b.strip().split('=', 1)
672 k = k.strip()
673 v = v.strip()
674 if k not in ['backend', 'instance']:
675 err('Invalid vtpm specifier: ' + vtpm)
676 d[k] = v
677 vtpms.append(d)
678 vals.vtpm = vtpms
680 def preprocess_tpmif(vals):
681 if not vals.tpmif: return
682 tpmifs = []
683 for tpmif in vals.tpmif:
684 d = {}
685 a = tpmif.split(',')
686 for b in a:
687 (k, v) = b.strip().split('=', 1)
688 k = k.strip()
689 v = v.strip()
690 if k not in ['frontend']:
691 err('Invalid tpmif specifier: ' + vtpm)
692 d[k] = v
693 tpmifs.append(d)
694 vals.tpmif = tpmifs
696 def preprocess_ip(vals):
697 if vals.ip or vals.dhcp != 'off':
698 dummy_nfs_server = '1.2.3.4'
699 ip = (vals.ip
700 + ':' + (vals.nfs_server or dummy_nfs_server)
701 + ':' + vals.gateway
702 + ':' + vals.netmask
703 + ':' + vals.hostname
704 + ':' + vals.interface
705 + ':' + vals.dhcp)
706 else:
707 ip = ''
708 vals.cmdline_ip = ip
710 def preprocess_nfs(vals):
711 if not vals.nfs_root: return
712 if not vals.nfs_server:
713 err('Must set nfs root and nfs server')
714 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
715 vals.extra = nfs + ' ' + vals.extra
718 def get_host_addr():
719 host = socket.gethostname()
720 addr = socket.gethostbyname(host)
721 return addr
723 VNC_BASE_PORT = 5500
725 def choose_vnc_display():
726 """Try to choose a free vnc display.
727 """
728 def netstat_local_ports():
729 """Run netstat to get a list of the local ports in use.
730 """
731 l = os.popen("netstat -nat").readlines()
732 r = []
733 # Skip 2 lines of header.
734 for x in l[2:]:
735 # Local port is field 3.
736 y = x.split()[3]
737 # Field is addr:port, split off the port.
738 y = y.split(':')[-1]
739 r.append(int(y))
740 return r
742 ports = netstat_local_ports()
743 for d in range(1, 100):
744 port = VNC_BASE_PORT + d
745 if port in ports: continue
746 return d
747 return None
749 vncpid = None
751 def spawn_vnc(display):
752 vncargs = (["vncviewer" + "-log", "*:stdout:0",
753 "-listen", "%d" % (VNC_BASE_PORT + display) ])
754 global vncpid
755 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
757 return VNC_BASE_PORT + display
759 def preprocess_vnc(vals):
760 """If vnc was specified, spawn a vncviewer in listen mode
761 and pass its address to the domain on the kernel command line.
762 """
763 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
764 vnc_display = choose_vnc_display()
765 if not vnc_display:
766 warn("No free vnc display")
767 return
768 print 'VNC=', vnc_display
769 vnc_port = spawn_vnc(vnc_display)
770 if vnc_port > 0:
771 vnc_host = get_host_addr()
772 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
773 vals.extra = vnc + ' ' + vals.extra
775 def preprocess(vals):
776 if not vals.kernel:
777 err("No kernel specified")
778 preprocess_disk(vals)
779 preprocess_pci(vals)
780 preprocess_vifs(vals)
781 preprocess_ip(vals)
782 preprocess_nfs(vals)
783 preprocess_vnc(vals)
784 preprocess_vtpm(vals)
785 preprocess_tpmif(vals)
787 def make_domain(opts, config):
788 """Create, build and start a domain.
790 @param opts: options
791 @param config: configuration
792 @return: domain id
793 @rtype: int
794 """
796 try:
797 if opts.vals.load:
798 filename = os.path.abspath(opts.vals.load)
799 dominfo = server.xend_domain_restore(filename, config)
800 else:
801 dominfo = server.xend_domain_create(config)
802 except XendError, ex:
803 import signal
804 if vncpid:
805 os.kill(vncpid, signal.SIGKILL)
806 err(str(ex))
808 dom = sxp.child_value(dominfo, 'name')
810 if not opts.vals.paused:
811 if server.xend_domain_unpause(dom) < 0:
812 server.xend_domain_destroy(dom)
813 err("Failed to unpause domain %s" % dom)
814 opts.info("Started domain %s" % (dom))
815 return int(sxp.child_value(dominfo, 'domid'))
817 def get_dom0_alloc():
818 """Return current allocation memory of dom0 (in MB). Return 0 on error"""
819 PROC_XEN_BALLOON = "/proc/xen/balloon"
821 f = open(PROC_XEN_BALLOON, "r")
822 line = f.readline()
823 for x in line.split():
824 for n in x:
825 if not n.isdigit():
826 break
827 else:
828 f.close()
829 return int(x)/1024
830 f.close()
831 return 0
833 def balloon_out(dom0_min_mem, opts):
834 """Balloon out memory from dom0 if necessary"""
835 SLACK = 4
836 timeout = 20 # 2s
837 ret = 1
839 xc = xen.lowlevel.xc.new()
840 free_mem = xc.physinfo()['free_pages'] / 256
841 domU_need_mem = opts.vals.memory + SLACK
843 # we already have enough free memory, return success
844 if free_mem >= domU_need_mem:
845 del xc
846 return 0
848 dom0_cur_alloc = get_dom0_alloc()
849 dom0_new_alloc = dom0_cur_alloc - (domU_need_mem - free_mem)
850 if dom0_new_alloc < dom0_min_mem:
851 dom0_new_alloc = dom0_min_mem
853 server.xend_domain_mem_target_set(0, dom0_new_alloc)
855 while timeout > 0:
856 time.sleep(0.1) # sleep 100ms
858 free_mem = xc.physinfo()['free_pages'] / 256
859 if free_mem >= domU_need_mem:
860 ret = 0
861 break
862 timeout -= 1
864 del xc
865 return ret
868 def parseCommandLine(argv):
869 opts = gopts
870 args = opts.parse(argv)
871 if opts.vals.help:
872 opts.usage()
873 if opts.vals.help or opts.vals.help_config:
874 opts.load_defconfig(help=1)
875 if opts.vals.help or opts.vals.help_config:
876 return (None, None)
878 if not opts.vals.display:
879 opts.vals.display = os.getenv("DISPLAY")
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 opts.vals.config:
887 config = opts.vals.config
888 else:
889 opts.load_defconfig()
890 preprocess(opts.vals)
891 if not opts.getopt('name') and opts.getopt('defconfig'):
892 opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
893 config = make_config(opts.vals)
895 return (opts, config)
898 def main(argv):
899 random.seed()
901 (opts, config) = parseCommandLine(argv)
903 if not opts:
904 return
906 if opts.vals.dryrun:
907 PrettyPrint.prettyprint(config)
908 else:
909 from xen.xend import XendRoot
911 xroot = XendRoot.instance()
913 dom0_min_mem = xroot.get_dom0_min_mem()
914 if dom0_min_mem != 0:
915 if balloon_out(dom0_min_mem, opts):
916 print >>sys.stderr, "error: cannot allocate enough memory for domain"
917 sys.exit(1)
919 dom = make_domain(opts, config)
920 if opts.vals.console_autoconnect:
921 console.execConsole(dom)
923 if __name__ == '__main__':
924 main(sys.argv)