ia64/xen-unstable

view tools/python/xen/xm/create.py @ 7912:04a7decd0645

Fix support for sxp domain configuration files.
Signed-off-by: Christian Limpach <Christian.Limpach@cl.cam.ac.uk>
author cl349@firebug.cl.cam.ac.uk
date Sat Nov 19 01:59:43 2005 +0100 (2005-11-19)
parents bffbe58801d0
children 317e5a7092e2
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 os
23 import os.path
24 import string
25 import sys
26 import socket
27 import commands
28 import time
30 import xen.lowlevel.xc
32 from xen.xend import sxp
33 from xen.xend import PrettyPrint
34 from xen.xend.XendClient import server, XendError
35 from xen.xend.XendBootloader import bootloader
36 from xen.util import blkif
38 from xen.xm.opts import *
40 import console
43 gopts = Opts(use="""[options] [vars]
45 Create a domain.
47 Domain creation parameters can be set by command-line switches, from
48 a python configuration script or an SXP config file. See documentation
49 for --defconfig, --config. Configuration variables can be set using
50 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
52 """)
54 gopts.opt('help', short='h',
55 fn=set_true, default=0,
56 use="Print this help.")
58 gopts.opt('help_config',
59 fn=set_true, default=0,
60 use="Print help for the configuration script.")
62 gopts.opt('quiet', short='q',
63 fn=set_true, default=0,
64 use="Quiet.")
66 gopts.opt('path', val='PATH',
67 fn=set_value, default='.:/etc/xen',
68 use="""Search path for configuration scripts.
69 The value of PATH is a colon-separated directory list.""")
71 gopts.opt('defconfig', short='f', val='FILE',
72 fn=set_value, default='xmdefconfig',
73 use="""Use the given Python configuration script.
74 The configuration script is loaded after arguments have been processed.
75 Each command-line option sets a configuration variable named after
76 its long option name, and these variables are placed in the
77 environment of the script before it is loaded.
78 Variables for options that may be repeated have list values.
79 Other variables can be set using VAR=VAL on the command line.
81 After the script is loaded, option values that were not set on the
82 command line are replaced by the values set in the script.""")
84 gopts.default('defconfig')
86 gopts.opt('config', short='F', val='FILE',
87 fn=set_value, default=None,
88 use="""Domain configuration to use (SXP).
89 SXP is the underlying configuration format used by Xen.
90 SXP configurations can be hand-written or generated from Python configuration
91 scripts, using the -n (dryrun) option to print the configuration.""")
93 gopts.opt('load', short='L', val='FILE',
94 fn=set_value, default=None,
95 use='Domain saved state to load.')
97 gopts.opt('dryrun', short='n',
98 fn=set_true, default=0,
99 use="""Dry run - print the configuration but don't create the domain.
100 Loads the configuration script, creates the SXP configuration and prints it.""")
102 gopts.opt('paused', short='p',
103 fn=set_true, default=0,
104 use='Leave the domain paused after it is created.')
106 gopts.opt('console_autoconnect', short='c',
107 fn=set_true, default=0,
108 use="Connect to the console after the domain is created.")
110 gopts.var('vncviewer', val='no|yes',
111 fn=set_bool, default=None,
112 use="""Spawn a vncviewer listening for a vnc server in the domain.
113 The address of the vncviewer is passed to the domain on the kernel command
114 line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
115 A display value with a free port is chosen if possible.
116 Only valid when vnc=1.
117 """)
119 gopts.var('name', val='NAME',
120 fn=set_value, default=None,
121 use="Domain name. Must be unique.")
123 gopts.var('bootloader', val='FILE',
124 fn=set_value, default=None,
125 use="Path to bootloader.")
127 gopts.var('bootentry', val='NAME',
128 fn=set_value, default=None,
129 use="Entry to boot via boot loader")
131 gopts.var('kernel', val='FILE',
132 fn=set_value, default=None,
133 use="Path to kernel image.")
135 gopts.var('ramdisk', val='FILE',
136 fn=set_value, default='',
137 use="Path to ramdisk.")
139 gopts.var('builder', val='FUNCTION',
140 fn=set_value, default='linux',
141 use="Function to use to build the domain.")
143 gopts.var('memory', val='MEMORY',
144 fn=set_int, default=128,
145 use="Domain memory in MB.")
147 gopts.var('ssidref', val='SSIDREF',
148 fn=set_u32, default=0,
149 use="Security Identifier.")
151 gopts.var('maxmem', val='MEMORY',
152 fn=set_int, default=None,
153 use="Maximum domain memory in MB.")
155 gopts.var('cpu', val='CPU',
156 fn=set_int, default=None,
157 use="CPU to run the domain on.")
159 gopts.var('lapic', val='LAPIC',
160 fn=set_int, default=0,
161 use="Disable or enable local APIC of VMX domain.")
163 gopts.var('vcpus', val='VCPUS',
164 fn=set_int, default=1,
165 use="# of Virtual CPUS in domain.")
167 gopts.var('cpu_weight', val='WEIGHT',
168 fn=set_float, default=None,
169 use="""Set the new domain's cpu weight.
170 WEIGHT is a float that controls the domain's share of the cpu.""")
172 gopts.var('restart', val='onreboot|always|never',
173 fn=set_value, default=None,
174 use="""Deprecated. Use on_poweroff, on_reboot, and on_crash
175 instead.
177 Whether the domain should be restarted on exit.
178 - onreboot: restart on exit with shutdown code reboot
179 - always: always restart on exit, ignore exit code
180 - never: never restart on exit, ignore exit code""")
182 gopts.var('on_poweroff', val='destroy|restart|preserve|rename-restart',
183 fn=set_value, default=None,
184 use="""Behaviour when a domain exits with reason 'poweroff'.
185 - destroy: the domain is cleaned up as normal;
186 - restart: a new domain is started in place of the old one;
187 - preserve: no clean-up is done until the domain is manually
188 destroyed (using xm destroy, for example);
189 - rename-restart: the old domain is not cleaned up, but is
190 renamed and a new domain started in its place.
191 """)
193 gopts.var('on_reboot', val='destroy|restart|preserve|rename-restart',
194 fn=set_value, default=None,
195 use="""Behaviour when a domain exits with reason 'reboot'.
196 - destroy: the domain is cleaned up as normal;
197 - restart: a new domain is started in place of the old one;
198 - preserve: no clean-up is done until the domain is manually
199 destroyed (using xm destroy, for example);
200 - rename-restart: the old domain is not cleaned up, but is
201 renamed and a new domain started in its place.
202 """)
204 gopts.var('on_crash', val='destroy|restart|preserve|rename-restart',
205 fn=set_value, default=None,
206 use="""Behaviour when a domain exits with reason 'crash'.
207 - destroy: the domain is cleaned up as normal;
208 - restart: a new domain is started in place of the old one;
209 - preserve: no clean-up is done until the domain is manually
210 destroyed (using xm destroy, for example);
211 - rename-restart: the old domain is not cleaned up, but is
212 renamed and a new domain started in its place.
213 """)
215 gopts.var('blkif', val='no|yes',
216 fn=set_bool, default=0,
217 use="Make the domain a block device backend.")
219 gopts.var('netif', val='no|yes',
220 fn=set_bool, default=0,
221 use="Make the domain a network interface backend.")
223 gopts.var('tpmif', val='frontend=DOM',
224 fn=append_value, default=[],
225 use="""Make the domain a TPM interface backend. If frontend is given,
226 the frontend in that domain is connected to this backend (not
227 completely implemented, yet)""")
229 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
230 fn=append_value, default=[],
231 use="""Add a disk device to a domain. The physical device is DEV,
232 which is exported to the domain as VDEV. The disk is read-only if MODE
233 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
234 backend driver domain to use for the disk.
235 The option may be repeated to add more than one disk.""")
237 gopts.var('pci', val='BUS,DEV,FUNC',
238 fn=append_value, default=[],
239 use="""Add a PCI device to a domain, using given params (in hex).
240 For example '-pci c0,02,1a'.
241 The option may be repeated to add more than one pci device.""")
243 gopts.var('ioports', val='FROM[-TO]',
244 fn=append_value, default=[],
245 use="""Add a legacy I/O range to a domain, using given params (in hex).
246 For example '-ioports 02f8-02ff'.
247 The option may be repeated to add more than one i/o range.""")
249 gopts.var('usb', val='PATH',
250 fn=append_value, default=[],
251 use="""Add a physical USB port to a domain, as specified by the path
252 to that port. This option may be repeated to add more than one port.""")
254 gopts.var('ipaddr', val="IPADDR",
255 fn=append_value, default=[],
256 use="Add an IP address to the domain.")
258 gopts.var('vif', val="type=TYPE,mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
259 fn=append_value, default=[],
260 use="""Add a network interface with the given MAC address and bridge.
261 The vif is configured by calling the given configuration script.
262 If type is not specified, default is netfront not ioemu device.
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('boot', val="a|b|c|d",
361 fn=set_value, default='c',
362 use="Default boot device")
364 gopts.var('nographic', val='no|yes',
365 fn=set_bool, default=0,
366 use="Should device models use graphics?")
368 gopts.var('ne2000', val='no|yes',
369 fn=set_bool, default=0,
370 use="Should device models use ne2000?")
372 gopts.var('vnc', val='',
373 fn=set_value, default=None,
374 use="""Should the device model use VNC?""")
376 gopts.var('sdl', val='',
377 fn=set_value, default=None,
378 use="""Should the device model use SDL?""")
380 gopts.var('display', val='DISPLAY',
381 fn=set_value, default=None,
382 use="X11 display to use")
385 def err(msg):
386 """Print an error to stderr and exit.
387 """
388 print >>sys.stderr, "Error:", msg
389 sys.exit(1)
392 def warn(msg):
393 """Print a warning to stdout.
394 """
395 print >>sys.stderr, "Warning:", msg
398 def strip(pre, s):
399 """Strip prefix 'pre' if present.
400 """
401 if s.startswith(pre):
402 return s[len(pre):]
403 else:
404 return s
406 def configure_image(vals):
407 """Create the image config.
408 """
409 config_image = [ vals.builder ]
410 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
411 if vals.ramdisk:
412 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
413 if vals.cmdline_ip:
414 cmdline_ip = strip('ip=', vals.cmdline_ip)
415 config_image.append(['ip', cmdline_ip])
416 if vals.root:
417 cmdline_root = strip('root=', vals.root)
418 config_image.append(['root', cmdline_root])
419 if vals.extra:
420 config_image.append(['args', vals.extra])
421 if vals.vcpus:
422 config_image.append(['vcpus', vals.vcpus])
423 return config_image
425 def configure_disks(config_devs, vals):
426 """Create the config for disks (virtual block devices).
427 """
428 for (uname, dev, mode, backend) in vals.disk:
429 config_vbd = ['vbd',
430 ['uname', uname],
431 ['dev', dev ],
432 ['mode', mode ] ]
433 if backend:
434 config_vbd.append(['backend', backend])
435 config_devs.append(['device', config_vbd])
437 def configure_pci(config_devs, vals):
438 """Create the config for pci devices.
439 """
440 for (bus, dev, func) in vals.pci:
441 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
442 config_devs.append(['device', config_pci])
444 def configure_ioports(config_devs, vals):
445 """Create the config for legacy i/o ranges.
446 """
447 for (io_from, io_to) in vals.ioports:
448 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
449 config_devs.append(['device', config_ioports])
451 def configure_usb(config_devs, vals):
452 for path in vals.usb:
453 config_usb = ['usb', ['path', path]]
454 config_devs.append(['device', config_usb])
456 def configure_vtpm(config_devs, vals):
457 """Create the config for virtual TPM interfaces.
458 """
459 vtpm = vals.vtpm
460 vtpm_n = 1
461 for idx in range(0, vtpm_n):
462 if idx < len(vtpm):
463 d = vtpm[idx]
464 instance = d.get('instance')
465 if instance == "VTPMD":
466 instance = "0"
467 else:
468 try:
469 if int(instance) == 0:
470 err('VM config error: vTPM instance must not be 0.')
471 except ValueError:
472 err('Vm config error: could not parse instance number.')
473 backend = d.get('backend')
474 config_vtpm = ['vtpm']
475 if instance:
476 config_vtpm.append(['instance', instance])
477 if backend:
478 config_vtpm.append(['backend', backend])
479 config_devs.append(['device', config_vtpm])
481 def configure_tpmif(config_devs, vals):
482 """Create the config for virtual TPM interfaces.
483 """
484 tpmif = vals.tpmif
485 tpmif_n = 1
486 for idx in range(0, tpmif_n):
487 if idx < len(tpmif):
488 d = tpmif[idx]
489 frontend = d.get('frontend')
490 config_tpmif = ['tpmif']
491 if frontend:
492 config_tpmif.append(['frontend', frontend])
493 config_devs.append(['device', config_tpmif])
496 def configure_vifs(config_devs, vals):
497 """Create the config for virtual network interfaces.
498 """
499 vifs = vals.vif
500 vifs_n = max(vals.nics, len(vifs))
502 for idx in range(0, vifs_n):
503 if idx < len(vifs):
504 d = vifs[idx]
505 mac = d.get('mac')
506 be_mac = d.get('be_mac')
507 bridge = d.get('bridge')
508 script = d.get('script')
509 backend = d.get('backend')
510 ip = d.get('ip')
511 vifname = d.get('vifname')
512 type = d.get('type')
513 else:
514 mac = None
515 be_mac = None
516 bridge = None
517 script = None
518 backend = None
519 ip = None
520 vifname = None
521 type = None
522 config_vif = ['vif']
523 if mac:
524 config_vif.append(['mac', mac])
525 if vifname:
526 config_vif.append(['vifname', vifname])
527 if be_mac:
528 config_vif.append(['be_mac', be_mac])
529 if bridge:
530 config_vif.append(['bridge', bridge])
531 if script:
532 config_vif.append(['script', script])
533 if backend:
534 config_vif.append(['backend', backend])
535 if ip:
536 config_vif.append(['ip', ip])
537 if type:
538 config_vif.append(['type', type])
539 config_devs.append(['device', config_vif])
541 def configure_vfr(config, vals):
542 if not vals.ipaddr: return
543 config_vfr = ['vfr']
544 idx = 0 # No way of saying which IP is for which vif?
545 for ip in vals.ipaddr:
546 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
547 config.append(config_vfr)
549 def configure_vmx(config_image, vals):
550 """Create the config for VMX devices.
551 """
552 args = [ 'device_model', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
553 'localtime', 'serial', 'stdvga', 'isa', 'nographic',
554 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'lapic']
555 for a in args:
556 if (vals.__dict__[a]):
557 config_image.append([a, vals.__dict__[a]])
559 def run_bootloader(vals):
560 if not os.access(vals.bootloader, os.X_OK):
561 err("Bootloader isn't executable")
562 if len(vals.disk) < 1:
563 err("No disks configured and boot loader requested")
564 (uname, dev, mode, backend) = vals.disk[0]
565 file = blkif.blkdev_uname_to_file(uname)
567 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
568 vals.vcpus, vals.bootentry)
570 def make_config(vals):
571 """Create the domain configuration.
572 """
574 config = ['vm']
576 def add_conf(n):
577 if hasattr(vals, n):
578 v = getattr(vals, n)
579 if v:
580 config.append([n, v])
582 map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
583 'on_poweroff', 'on_reboot', 'on_crash'])
585 if vals.cpu is not None:
586 config.append(['cpu', vals.cpu])
587 if vals.cpu_weight is not None:
588 config.append(['cpu_weight', vals.cpu_weight])
589 if vals.blkif:
590 config.append(['backend', ['blkif']])
591 if vals.netif:
592 config.append(['backend', ['netif']])
593 if vals.tpmif:
594 config.append(['backend', ['tpmif']])
596 if vals.bootloader:
597 config.append(['bootloader', vals.bootloader])
598 config_image = run_bootloader(vals)
599 else:
600 config_image = configure_image(vals)
601 configure_vmx(config_image, vals)
602 config.append(['image', config_image])
604 config_devs = []
605 configure_disks(config_devs, vals)
606 configure_pci(config_devs, vals)
607 configure_ioports(config_devs, vals)
608 configure_vifs(config_devs, vals)
609 configure_usb(config_devs, vals)
610 configure_vtpm(config_devs, vals)
611 config += config_devs
613 return config
615 def preprocess_disk(vals):
616 if not vals.disk: return
617 disk = []
618 for v in vals.disk:
619 d = v.split(',')
620 n = len(d)
621 if n == 3:
622 d.append(None)
623 elif n == 4:
624 pass
625 else:
626 err('Invalid disk specifier: ' + v)
627 disk.append(d)
628 vals.disk = disk
630 def preprocess_pci(vals):
631 if not vals.pci: return
632 pci = []
633 for v in vals.pci:
634 d = v.split(',')
635 if len(d) != 3:
636 err('Invalid pci specifier: ' + v)
637 # Components are in hex: add hex specifier.
638 hexd = map(lambda v: '0x'+v, d)
639 pci.append(hexd)
640 vals.pci = pci
642 def preprocess_ioports(vals):
643 if not vals.ioports: return
644 ioports = []
645 for v in vals.ioports:
646 d = v.split('-')
647 if len(d) < 1 or len(d) > 2:
648 err('Invalid i/o port range specifier: ' + v)
649 if len(d) == 1:
650 d.append(d[0])
651 # Components are in hex: add hex specifier.
652 hexd = map(lambda v: '0x'+v, d)
653 ioports.append(hexd)
654 vals.ioports = ioports
656 def preprocess_vifs(vals):
657 if not vals.vif: return
658 vifs = []
659 for vif in vals.vif:
660 d = {}
661 a = vif.split(',')
662 for b in a:
663 (k, v) = b.strip().split('=', 1)
664 k = k.strip()
665 v = v.strip()
666 if k not in ['type', 'mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
667 err('Invalid vif specifier: ' + vif)
668 d[k] = v
669 vifs.append(d)
670 vals.vif = vifs
672 def preprocess_vtpm(vals):
673 if not vals.vtpm: return
674 vtpms = []
675 for vtpm in vals.vtpm:
676 d = {}
677 a = vtpm.split(',')
678 for b in a:
679 (k, v) = b.strip().split('=', 1)
680 k = k.strip()
681 v = v.strip()
682 if k not in ['backend', 'instance']:
683 err('Invalid vtpm specifier: ' + vtpm)
684 d[k] = v
685 vtpms.append(d)
686 vals.vtpm = vtpms
688 def preprocess_tpmif(vals):
689 if not vals.tpmif: return
690 tpmifs = []
691 for tpmif in vals.tpmif:
692 d = {}
693 a = tpmif.split(',')
694 for b in a:
695 (k, v) = b.strip().split('=', 1)
696 k = k.strip()
697 v = v.strip()
698 if k not in ['frontend']:
699 err('Invalid tpmif specifier: ' + vtpm)
700 d[k] = v
701 tpmifs.append(d)
702 vals.tpmif = tpmifs
704 def preprocess_ip(vals):
705 if vals.ip or vals.dhcp != 'off':
706 dummy_nfs_server = '1.2.3.4'
707 ip = (vals.ip
708 + ':' + (vals.nfs_server or dummy_nfs_server)
709 + ':' + vals.gateway
710 + ':' + vals.netmask
711 + ':' + vals.hostname
712 + ':' + vals.interface
713 + ':' + vals.dhcp)
714 else:
715 ip = ''
716 vals.cmdline_ip = ip
718 def preprocess_nfs(vals):
719 if not vals.nfs_root: return
720 if not vals.nfs_server:
721 err('Must set nfs root and nfs server')
722 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
723 vals.extra = nfs + ' ' + vals.extra
726 def get_host_addr():
727 host = socket.gethostname()
728 addr = socket.gethostbyname(host)
729 return addr
731 VNC_BASE_PORT = 5500
733 def choose_vnc_display():
734 """Try to choose a free vnc display.
735 """
736 def netstat_local_ports():
737 """Run netstat to get a list of the local ports in use.
738 """
739 l = os.popen("netstat -nat").readlines()
740 r = []
741 # Skip 2 lines of header.
742 for x in l[2:]:
743 # Local port is field 3.
744 y = x.split()[3]
745 # Field is addr:port, split off the port.
746 y = y.split(':')[-1]
747 r.append(int(y))
748 return r
750 ports = netstat_local_ports()
751 for d in range(1, 100):
752 port = VNC_BASE_PORT + d
753 if port in ports: continue
754 return d
755 return None
757 vncpid = None
759 def spawn_vnc(display):
760 vncargs = (["vncviewer", "-log", "*:stdout:0",
761 "-listen", "%d" % (VNC_BASE_PORT + display) ])
762 global vncpid
763 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
765 return VNC_BASE_PORT + display
767 def preprocess_vnc(vals):
768 """If vnc was specified, spawn a vncviewer in listen mode
769 and pass its address to the domain on the kernel command line.
770 """
771 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
772 vnc_display = choose_vnc_display()
773 if not vnc_display:
774 warn("No free vnc display")
775 return
776 print 'VNC=', vnc_display
777 vnc_port = spawn_vnc(vnc_display)
778 if vnc_port > 0:
779 vnc_host = get_host_addr()
780 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
781 vals.extra = vnc + ' ' + vals.extra
783 def preprocess(vals):
784 if not vals.kernel and not vals.bootloader:
785 err("No kernel specified")
786 preprocess_disk(vals)
787 preprocess_pci(vals)
788 preprocess_ioports(vals)
789 preprocess_vifs(vals)
790 preprocess_ip(vals)
791 preprocess_nfs(vals)
792 preprocess_vnc(vals)
793 preprocess_vtpm(vals)
794 preprocess_tpmif(vals)
796 def make_domain(opts, config):
797 """Create, build and start a domain.
799 @param opts: options
800 @param config: configuration
801 @return: domain id
802 @rtype: int
803 """
805 try:
806 if opts.vals.load:
807 filename = os.path.abspath(opts.vals.load)
808 dominfo = server.xend_domain_restore(filename, config)
809 else:
810 dominfo = server.xend_domain_create(config)
811 except XendError, ex:
812 import signal
813 if vncpid:
814 os.kill(vncpid, signal.SIGKILL)
815 err(str(ex))
817 dom = sxp.child_value(dominfo, 'name')
819 if server.xend_domain_wait_for_devices(dom) < 0:
820 server.xend_domain_destroy(dom)
821 err("Device creation failed for domain %s" % dom)
823 if not opts.vals.paused:
824 if server.xend_domain_unpause(dom) < 0:
825 server.xend_domain_destroy(dom)
826 err("Failed to unpause domain %s" % dom)
827 opts.info("Started domain %s" % (dom))
828 return int(sxp.child_value(dominfo, 'domid'))
830 def get_dom0_alloc():
831 """Return current allocation memory of dom0 (in MB). Return 0 on error"""
832 PROC_XEN_BALLOON = "/proc/xen/balloon"
834 f = open(PROC_XEN_BALLOON, "r")
835 line = f.readline()
836 for x in line.split():
837 for n in x:
838 if not n.isdigit():
839 break
840 else:
841 f.close()
842 return int(x)/1024
843 f.close()
844 return 0
846 def balloon_out(dom0_min_mem, opts):
847 """Balloon out memory from dom0 if necessary"""
848 SLACK = 4
849 timeout = 20 # 2s
850 ret = 1
852 xc = xen.lowlevel.xc.new()
853 free_mem = xc.physinfo()['free_pages'] / 256
854 domU_need_mem = opts.vals.memory + SLACK
856 # we already have enough free memory, return success
857 if free_mem >= domU_need_mem:
858 del xc
859 return 0
861 dom0_cur_alloc = get_dom0_alloc()
862 dom0_new_alloc = dom0_cur_alloc - (domU_need_mem - free_mem)
863 if dom0_new_alloc < dom0_min_mem:
864 dom0_new_alloc = dom0_min_mem
866 server.xend_domain_mem_target_set(0, dom0_new_alloc)
868 while timeout > 0:
869 time.sleep(0.1) # sleep 100ms
871 free_mem = xc.physinfo()['free_pages'] / 256
872 if free_mem >= domU_need_mem:
873 ret = 0
874 break
875 timeout -= 1
877 del xc
878 return ret
881 def parseCommandLine(argv):
882 opts = gopts
883 args = opts.parse(argv)
884 if opts.vals.help:
885 opts.usage()
886 if opts.vals.help or opts.vals.help_config:
887 opts.load_defconfig(help=1)
888 if opts.vals.help or opts.vals.help_config:
889 return (None, None)
891 if not opts.vals.display:
892 opts.vals.display = os.getenv("DISPLAY")
894 # Process remaining args as config variables.
895 for arg in args:
896 if '=' in arg:
897 (var, val) = arg.strip().split('=', 1)
898 gopts.setvar(var.strip(), val.strip())
899 if opts.vals.config:
900 config = opts.vals.config
901 else:
902 opts.load_defconfig()
903 preprocess(opts.vals)
904 if not opts.getopt('name') and opts.getopt('defconfig'):
905 opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
906 config = make_config(opts.vals)
908 if type(config) == str:
909 config = sxp.parse(file(config))[0]
911 return (opts, config)
914 def main(argv):
915 (opts, config) = parseCommandLine(argv)
917 if not opts:
918 return
920 if opts.vals.dryrun:
921 PrettyPrint.prettyprint(config)
922 else:
923 from xen.xend import XendRoot
925 xroot = XendRoot.instance()
927 dom0_min_mem = xroot.get_dom0_min_mem()
928 if dom0_min_mem != 0:
929 if balloon_out(dom0_min_mem, opts):
930 print >>sys.stderr, "error: cannot allocate enough memory for domain"
931 sys.exit(1)
933 dom = make_domain(opts, config)
934 if opts.vals.console_autoconnect:
935 console.execConsole(dom)
937 if __name__ == '__main__':
938 main(sys.argv)