ia64/xen-unstable

view tools/python/xen/xm/create.py @ 8155:9ff0a75fc3f7

Match rename of free_pages field to free_memory

Signed-off-by: Robert Read <rread@xensource.com>
author rread@ubuntu.eng.hq.xensource.com
date Wed Nov 30 20:11:49 2005 -0700 (2005-11-30)
parents cb215a84d1af
children
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='no|yes',
224 fn=append_value, default=0,
225 use="Make the domain a TPM interface backend.")
227 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
228 fn=append_value, default=[],
229 use="""Add a disk device to a domain. The physical device is DEV,
230 which is exported to the domain as VDEV. The disk is read-only if MODE
231 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
232 backend driver domain to use for the disk.
233 The option may be repeated to add more than one disk.""")
235 gopts.var('pci', val='BUS,DEV,FUNC',
236 fn=append_value, default=[],
237 use="""Add a PCI device to a domain, using given params (in hex).
238 For example '-pci c0,02,1a'.
239 The option may be repeated to add more than one pci device.""")
241 gopts.var('ioports', val='FROM[-TO]',
242 fn=append_value, default=[],
243 use="""Add a legacy I/O range to a domain, using given params (in hex).
244 For example '-ioports 02f8-02ff'.
245 The option may be repeated to add more than one i/o range.""")
247 gopts.var('usb', val='PATH',
248 fn=append_value, default=[],
249 use="""Add a physical USB port to a domain, as specified by the path
250 to that port. This option may be repeated to add more than one port.""")
252 gopts.var('ipaddr', val="IPADDR",
253 fn=append_value, default=[],
254 use="Add an IP address to the domain.")
256 gopts.var('vif', val="type=TYPE,mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
257 fn=append_value, default=[],
258 use="""Add a network interface with the given MAC address and bridge.
259 The vif is configured by calling the given configuration script.
260 If type is not specified, default is netfront not ioemu device.
261 If mac is not specified a random MAC address is used.
262 The MAC address of the backend interface can be selected with be_mac.
263 If not specified then the network backend chooses it's own MAC address.
264 If bridge is not specified the first bridge found is used.
265 If script is not specified the default script is used.
266 If backend is not specified the default backend driver domain is used.
267 If vifname is not specified the backend virtual interface will have name vifD.N
268 where D is the domain id and N is the interface id.
269 This option may be repeated to add more than one vif.
270 Specifying vifs will increase the number of interfaces as needed.""")
272 gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
273 fn=append_value, default=[],
274 use="""Add a TPM interface. On the backend side use the given
275 instance as virtual TPM instance. The given number is merely the
276 preferred instance number. The hotplug script will determine
277 which instance number will actually be assigned to the domain.
278 The associtation between virtual machine and the TPM instance
279 number can be found in /etc/xen/vtpm.db. Use the backend in the
280 given domain.""")
282 gopts.var('nics', val="NUM",
283 fn=set_int, default=1,
284 use="""Set the number of network interfaces.
285 Use the vif option to define interface parameters, otherwise
286 defaults are used. Specifying vifs will increase the
287 number of interfaces as needed.""")
289 gopts.var('root', val='DEVICE',
290 fn=set_value, default='',
291 use="""Set the root= parameter on the kernel command line.
292 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
294 gopts.var('extra', val="ARGS",
295 fn=set_value, default='',
296 use="Set extra arguments to append to the kernel command line.")
298 gopts.var('ip', val='IPADDR',
299 fn=set_value, default='',
300 use="Set the kernel IP interface address.")
302 gopts.var('gateway', val="IPADDR",
303 fn=set_value, default='',
304 use="Set the kernel IP gateway.")
306 gopts.var('netmask', val="MASK",
307 fn=set_value, default = '',
308 use="Set the kernel IP netmask.")
310 gopts.var('hostname', val="NAME",
311 fn=set_value, default='',
312 use="Set the kernel IP hostname.")
314 gopts.var('interface', val="INTF",
315 fn=set_value, default="eth0",
316 use="Set the kernel IP interface name.")
318 gopts.var('dhcp', val="off|dhcp",
319 fn=set_value, default='off',
320 use="Set the kernel dhcp option.")
322 gopts.var('nfs_server', val="IPADDR",
323 fn=set_value, default=None,
324 use="Set the address of the NFS server for NFS root.")
326 gopts.var('nfs_root', val="PATH",
327 fn=set_value, default=None,
328 use="Set the path of the root NFS directory.")
330 gopts.var('device_model', val='FILE',
331 fn=set_value, default='',
332 use="Path to device model program.")
334 gopts.var('fda', val='FILE',
335 fn=set_value, default='',
336 use="Path to fda")
338 gopts.var('fdb', val='FILE',
339 fn=set_value, default='',
340 use="Path to fdb")
342 gopts.var('serial', val='FILE',
343 fn=set_value, default='',
344 use="Path to serial or pty or vc")
346 gopts.var('localtime', val='no|yes',
347 fn=set_bool, default=0,
348 use="Is RTC set to localtime?")
350 gopts.var('stdvga', val='no|yes',
351 fn=set_bool, default=0,
352 use="Use std vga or cirrhus logic graphics")
354 gopts.var('isa', val='no|yes',
355 fn=set_bool, default=0,
356 use="Simulate an ISA only system?")
358 gopts.var('cdrom', val='FILE',
359 fn=set_value, default='',
360 use="Path to cdrom")
362 gopts.var('boot', val="a|b|c|d",
363 fn=set_value, default='c',
364 use="Default boot device")
366 gopts.var('nographic', val='no|yes',
367 fn=set_bool, default=0,
368 use="Should device models use graphics?")
370 gopts.var('ne2000', val='no|yes',
371 fn=set_bool, default=0,
372 use="Should device models use ne2000?")
374 gopts.var('vnc', val='',
375 fn=set_value, default=None,
376 use="""Should the device model use VNC?""")
378 gopts.var('sdl', val='',
379 fn=set_value, default=None,
380 use="""Should the device model use SDL?""")
382 gopts.var('display', val='DISPLAY',
383 fn=set_value, default=None,
384 use="X11 display to use")
387 def err(msg):
388 """Print an error to stderr and exit.
389 """
390 print >>sys.stderr, "Error:", msg
391 sys.exit(1)
394 def warn(msg):
395 """Print a warning to stdout.
396 """
397 print >>sys.stderr, "Warning:", msg
400 def strip(pre, s):
401 """Strip prefix 'pre' if present.
402 """
403 if s.startswith(pre):
404 return s[len(pre):]
405 else:
406 return s
408 def configure_image(vals):
409 """Create the image config.
410 """
411 config_image = [ vals.builder ]
412 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
413 if vals.ramdisk:
414 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
415 if vals.cmdline_ip:
416 cmdline_ip = strip('ip=', vals.cmdline_ip)
417 config_image.append(['ip', cmdline_ip])
418 if vals.root:
419 cmdline_root = strip('root=', vals.root)
420 config_image.append(['root', cmdline_root])
421 if vals.extra:
422 config_image.append(['args', vals.extra])
423 if vals.vcpus:
424 config_image.append(['vcpus', vals.vcpus])
425 return config_image
427 def configure_disks(config_devs, vals):
428 """Create the config for disks (virtual block devices).
429 """
430 for (uname, dev, mode, backend) in vals.disk:
431 config_vbd = ['vbd',
432 ['uname', uname],
433 ['dev', dev ],
434 ['mode', mode ] ]
435 if backend:
436 config_vbd.append(['backend', backend])
437 config_devs.append(['device', config_vbd])
439 def configure_pci(config_devs, vals):
440 """Create the config for pci devices.
441 """
442 for (bus, dev, func) in vals.pci:
443 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
444 config_devs.append(['device', config_pci])
446 def configure_ioports(config_devs, vals):
447 """Create the config for legacy i/o ranges.
448 """
449 for (io_from, io_to) in vals.ioports:
450 config_ioports = ['ioports', ['from', io_from], ['to', io_to]]
451 config_devs.append(['device', config_ioports])
453 def configure_usb(config_devs, vals):
454 for path in vals.usb:
455 config_usb = ['usb', ['path', path]]
456 config_devs.append(['device', config_usb])
458 def configure_vtpm(config_devs, vals):
459 """Create the config for virtual TPM interfaces.
460 """
461 vtpm = vals.vtpm
462 vtpm_n = 1
463 for idx in range(0, vtpm_n):
464 if idx < len(vtpm):
465 d = vtpm[idx]
466 instance = d.get('instance')
467 if instance == "VTPMD":
468 instance = "0"
469 else:
470 if instance != None:
471 try:
472 if int(instance) == 0:
473 err('VM config error: vTPM instance must not be 0.')
474 except ValueError:
475 err('Vm config error: could not parse instance number.')
476 backend = d.get('backend')
477 config_vtpm = ['vtpm']
478 if instance:
479 config_vtpm.append(['pref_instance', instance])
480 if backend:
481 config_vtpm.append(['backend', backend])
482 config_devs.append(['device', config_vtpm])
484 def configure_vifs(config_devs, vals):
485 """Create the config for virtual network interfaces.
486 """
487 vifs = vals.vif
488 vifs_n = max(vals.nics, len(vifs))
490 for idx in range(0, vifs_n):
491 if idx < len(vifs):
492 d = vifs[idx]
493 mac = d.get('mac')
494 be_mac = d.get('be_mac')
495 bridge = d.get('bridge')
496 script = d.get('script')
497 backend = d.get('backend')
498 ip = d.get('ip')
499 vifname = d.get('vifname')
500 type = d.get('type')
501 else:
502 mac = None
503 be_mac = None
504 bridge = None
505 script = None
506 backend = None
507 ip = None
508 vifname = None
509 type = None
510 config_vif = ['vif']
511 if mac:
512 config_vif.append(['mac', mac])
513 if vifname:
514 config_vif.append(['vifname', vifname])
515 if be_mac:
516 config_vif.append(['be_mac', be_mac])
517 if bridge:
518 config_vif.append(['bridge', bridge])
519 if script:
520 config_vif.append(['script', script])
521 if backend:
522 config_vif.append(['backend', backend])
523 if ip:
524 config_vif.append(['ip', ip])
525 if type:
526 config_vif.append(['type', type])
527 config_devs.append(['device', config_vif])
529 def configure_vfr(config, vals):
530 if not vals.ipaddr: return
531 config_vfr = ['vfr']
532 idx = 0 # No way of saying which IP is for which vif?
533 for ip in vals.ipaddr:
534 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
535 config.append(config_vfr)
537 def configure_vmx(config_image, vals):
538 """Create the config for VMX devices.
539 """
540 args = [ 'device_model', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
541 'localtime', 'serial', 'stdvga', 'isa', 'nographic',
542 'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'lapic']
543 for a in args:
544 if (vals.__dict__[a]):
545 config_image.append([a, vals.__dict__[a]])
547 def run_bootloader(vals):
548 if not os.access(vals.bootloader, os.X_OK):
549 err("Bootloader isn't executable")
550 if len(vals.disk) < 1:
551 err("No disks configured and boot loader requested")
552 (uname, dev, mode, backend) = vals.disk[0]
553 file = blkif.blkdev_uname_to_file(uname)
555 return bootloader(vals.bootloader, file, not vals.console_autoconnect,
556 vals.vcpus, vals.bootentry)
558 def make_config(vals):
559 """Create the domain configuration.
560 """
562 config = ['vm']
564 def add_conf(n):
565 if hasattr(vals, n):
566 v = getattr(vals, n)
567 if v:
568 config.append([n, v])
570 map(add_conf, ['name', 'memory', 'ssidref', 'maxmem', 'restart',
571 'on_poweroff', 'on_reboot', 'on_crash'])
573 if vals.cpu is not None:
574 config.append(['cpu', vals.cpu])
575 if vals.cpu_weight is not None:
576 config.append(['cpu_weight', vals.cpu_weight])
577 if vals.blkif:
578 config.append(['backend', ['blkif']])
579 if vals.netif:
580 config.append(['backend', ['netif']])
581 if vals.tpmif:
582 config.append(['backend', ['tpmif']])
584 if vals.bootloader:
585 config.append(['bootloader', vals.bootloader])
586 config_image = run_bootloader(vals)
587 else:
588 config_image = configure_image(vals)
589 configure_vmx(config_image, vals)
590 config.append(['image', config_image])
592 config_devs = []
593 configure_disks(config_devs, vals)
594 configure_pci(config_devs, vals)
595 configure_ioports(config_devs, vals)
596 configure_vifs(config_devs, vals)
597 configure_usb(config_devs, vals)
598 configure_vtpm(config_devs, vals)
599 config += config_devs
601 return config
603 def preprocess_disk(vals):
604 if not vals.disk: return
605 disk = []
606 for v in vals.disk:
607 d = v.split(',')
608 n = len(d)
609 if n == 3:
610 d.append(None)
611 elif n == 4:
612 pass
613 else:
614 err('Invalid disk specifier: ' + v)
615 disk.append(d)
616 vals.disk = disk
618 def preprocess_pci(vals):
619 if not vals.pci: return
620 pci = []
621 for v in vals.pci:
622 d = v.split(',')
623 if len(d) != 3:
624 err('Invalid pci specifier: ' + v)
625 # Components are in hex: add hex specifier.
626 hexd = map(lambda v: '0x'+v, d)
627 pci.append(hexd)
628 vals.pci = pci
630 def preprocess_ioports(vals):
631 if not vals.ioports: return
632 ioports = []
633 for v in vals.ioports:
634 d = v.split('-')
635 if len(d) < 1 or len(d) > 2:
636 err('Invalid i/o port range specifier: ' + v)
637 if len(d) == 1:
638 d.append(d[0])
639 # Components are in hex: add hex specifier.
640 hexd = map(lambda v: '0x'+v, d)
641 ioports.append(hexd)
642 vals.ioports = ioports
644 def preprocess_vifs(vals):
645 if not vals.vif: return
646 vifs = []
647 for vif in vals.vif:
648 d = {}
649 a = vif.split(',')
650 for b in a:
651 (k, v) = b.strip().split('=', 1)
652 k = k.strip()
653 v = v.strip()
654 if k not in ['type', 'mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
655 err('Invalid vif specifier: ' + vif)
656 d[k] = v
657 vifs.append(d)
658 vals.vif = vifs
660 def preprocess_vtpm(vals):
661 if not vals.vtpm: return
662 vtpms = []
663 for vtpm in vals.vtpm:
664 d = {}
665 a = vtpm.split(',')
666 for b in a:
667 (k, v) = b.strip().split('=', 1)
668 k = k.strip()
669 v = v.strip()
670 if k not in ['backend', 'instance']:
671 err('Invalid vtpm specifier: ' + vtpm)
672 d[k] = v
673 vtpms.append(d)
674 vals.vtpm = vtpms
676 def preprocess_ip(vals):
677 if vals.ip or vals.dhcp != 'off':
678 dummy_nfs_server = '1.2.3.4'
679 ip = (vals.ip
680 + ':' + (vals.nfs_server or dummy_nfs_server)
681 + ':' + vals.gateway
682 + ':' + vals.netmask
683 + ':' + vals.hostname
684 + ':' + vals.interface
685 + ':' + vals.dhcp)
686 else:
687 ip = ''
688 vals.cmdline_ip = ip
690 def preprocess_nfs(vals):
691 if not vals.nfs_root: return
692 if not vals.nfs_server:
693 err('Must set nfs root and nfs server')
694 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
695 vals.extra = nfs + ' ' + vals.extra
698 def get_host_addr():
699 host = socket.gethostname()
700 addr = socket.gethostbyname(host)
701 return addr
703 VNC_BASE_PORT = 5500
705 def choose_vnc_display():
706 """Try to choose a free vnc display.
707 """
708 def netstat_local_ports():
709 """Run netstat to get a list of the local ports in use.
710 """
711 l = os.popen("netstat -nat").readlines()
712 r = []
713 # Skip 2 lines of header.
714 for x in l[2:]:
715 # Local port is field 3.
716 y = x.split()[3]
717 # Field is addr:port, split off the port.
718 y = y.split(':')[-1]
719 r.append(int(y))
720 return r
722 ports = netstat_local_ports()
723 for d in range(1, 100):
724 port = VNC_BASE_PORT + d
725 if port in ports: continue
726 return d
727 return None
729 vncpid = None
731 def spawn_vnc(display):
732 vncargs = (["vncviewer", "-log", "*:stdout:0",
733 "-listen", "%d" % (VNC_BASE_PORT + display) ])
734 global vncpid
735 vncpid = os.spawnvp(os.P_NOWAIT, "vncviewer", vncargs)
737 return VNC_BASE_PORT + display
739 def preprocess_vnc(vals):
740 """If vnc was specified, spawn a vncviewer in listen mode
741 and pass its address to the domain on the kernel command line.
742 """
743 if not (vals.vnc and vals.vncviewer) or vals.dryrun: return
744 vnc_display = choose_vnc_display()
745 if not vnc_display:
746 warn("No free vnc display")
747 return
748 print 'VNC=', vnc_display
749 vnc_port = spawn_vnc(vnc_display)
750 if vnc_port > 0:
751 vnc_host = get_host_addr()
752 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
753 vals.extra = vnc + ' ' + vals.extra
755 def preprocess(vals):
756 if not vals.kernel and not vals.bootloader:
757 err("No kernel specified")
758 preprocess_disk(vals)
759 preprocess_pci(vals)
760 preprocess_ioports(vals)
761 preprocess_vifs(vals)
762 preprocess_ip(vals)
763 preprocess_nfs(vals)
764 preprocess_vnc(vals)
765 preprocess_vtpm(vals)
767 def make_domain(opts, config):
768 """Create, build and start a domain.
770 @param opts: options
771 @param config: configuration
772 @return: domain id
773 @rtype: int
774 """
776 try:
777 if opts.vals.load:
778 filename = os.path.abspath(opts.vals.load)
779 dominfo = server.xend_domain_restore(filename, config)
780 else:
781 dominfo = server.xend_domain_create(config)
782 except XendError, ex:
783 import signal
784 if vncpid:
785 os.kill(vncpid, signal.SIGKILL)
786 err(str(ex))
788 dom = sxp.child_value(dominfo, 'name')
790 if server.xend_domain_wait_for_devices(dom) < 0:
791 server.xend_domain_destroy(dom)
792 err("Device creation failed for domain %s" % dom)
794 if not opts.vals.paused:
795 if server.xend_domain_unpause(dom) < 0:
796 server.xend_domain_destroy(dom)
797 err("Failed to unpause domain %s" % dom)
798 opts.info("Started domain %s" % (dom))
799 return int(sxp.child_value(dominfo, 'domid'))
801 def get_dom0_alloc():
802 """Return current allocation memory of dom0 (in MB). Return 0 on error"""
803 PROC_XEN_BALLOON = "/proc/xen/balloon"
805 f = open(PROC_XEN_BALLOON, "r")
806 line = f.readline()
807 for x in line.split():
808 for n in x:
809 if not n.isdigit():
810 break
811 else:
812 f.close()
813 return int(x)/1024
814 f.close()
815 return 0
817 def balloon_out(dom0_min_mem, opts):
818 """Balloon out memory from dom0 if necessary"""
819 SLACK = 4
820 timeout = 20 # 2s
821 ret = 1
823 xc = xen.lowlevel.xc.xc()
824 free_mem = xc.physinfo()['free_memory']
825 domU_need_mem = opts.vals.memory + SLACK
827 # we already have enough free memory, return success
828 if free_mem >= domU_need_mem:
829 del xc
830 return 0
832 dom0_cur_alloc = get_dom0_alloc()
833 dom0_new_alloc = dom0_cur_alloc - (domU_need_mem - free_mem)
834 if dom0_new_alloc < dom0_min_mem:
835 dom0_new_alloc = dom0_min_mem
837 server.xend_domain_mem_target_set(0, dom0_new_alloc)
839 while timeout > 0:
840 time.sleep(0.1) # sleep 100ms
842 free_mem = xc.physinfo()['free_memory']
843 if free_mem >= domU_need_mem:
844 ret = 0
845 break
846 timeout -= 1
848 del xc
849 return ret
852 def parseCommandLine(argv):
853 gopts.reset()
854 args = gopts.parse(argv)
855 if gopts.vals.help:
856 gopts.usage()
857 if gopts.vals.help or gopts.vals.help_config:
858 gopts.load_defconfig(help=1)
859 if gopts.vals.help or gopts.vals.help_config:
860 return (None, None)
862 if not gopts.vals.display:
863 gopts.vals.display = os.getenv("DISPLAY")
865 # Process remaining args as config variables.
866 for arg in args:
867 if '=' in arg:
868 (var, val) = arg.strip().split('=', 1)
869 gopts.setvar(var.strip(), val.strip())
870 if gopts.vals.config:
871 config = gopts.vals.config
872 else:
873 gopts.load_defconfig()
874 preprocess(gopts.vals)
875 if not gopts.getopt('name') and gopts.getopt('defconfig'):
876 gopts.setopt('name', os.path.basename(gopts.getopt('defconfig')))
877 config = make_config(gopts.vals)
879 return (gopts, config)
882 def main(argv):
883 try:
884 (opts, config) = parseCommandLine(argv)
885 except StandardError, ex:
886 err(str(ex))
888 if not opts:
889 return
891 if opts.vals.dryrun:
892 PrettyPrint.prettyprint(config)
893 else:
894 from xen.xend import XendRoot
896 xroot = XendRoot.instance()
898 dom0_min_mem = xroot.get_dom0_min_mem()
899 if dom0_min_mem != 0:
900 if balloon_out(dom0_min_mem, opts):
901 err("cannot allocate enough memory for domain")
903 dom = make_domain(opts, config)
904 if opts.vals.console_autoconnect:
905 console.execConsole(dom)
907 if __name__ == '__main__':
908 main(sys.argv)