direct-io.hg

view tools/python/xen/xm/create.py @ 5547:84b9630129d7

bitkeeper revision 1.1729 (42ba7d48EFeRBiv6YMxK_aR9binchA)

[PATCH] novnc-dryrun.patch

Don't start VNC on a dry run.

Signed-off-by: Arun Sharma <arun.sharma@intel.com>
author arun.sharma@intel.com[kaf24]
date Thu Jun 23 09:13:44 2005 +0000 (2005-06-23)
parents 76346519b28e
children 6d68b0c27199
line source
1 # Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
3 """Domain creation.
4 """
5 import random
6 import string
7 import sys
8 import socket
10 from xen.xend import sxp
11 from xen.xend import PrettyPrint
12 from xen.xend.XendClient import server, XendError
13 from xen.xend.XendBootloader import bootloader
14 from xen.util import blkif
16 from xen.util import console_client
18 from xen.xm.opts import *
20 gopts = Opts(use="""[options] [vars]
22 Create a domain.
24 Domain creation parameters can be set by command-line switches, from
25 a python configuration script or an SXP config file. See documentation
26 for --defconfig, --config. Configuration variables can be set using
27 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
29 """)
31 gopts.opt('help', short='h',
32 fn=set_true, default=0,
33 use="Print this help.")
35 gopts.opt('help_config',
36 fn=set_true, default=0,
37 use="Print help for the configuration script.")
39 gopts.opt('quiet', short='q',
40 fn=set_true, default=0,
41 use="Quiet.")
43 gopts.opt('path', val='PATH',
44 fn=set_value, default='.:/etc/xen',
45 use="""Search path for configuration scripts.
46 The value of PATH is a colon-separated directory list.""")
48 gopts.opt('defconfig', short='f', val='FILE',
49 fn=set_value, default='xmdefconfig',
50 use="""Use the given Python configuration script.
51 The configuration script is loaded after arguments have been processed.
52 Each command-line option sets a configuration variable named after
53 its long option name, and these variables are placed in the
54 environment of the script before it is loaded.
55 Variables for options that may be repeated have list values.
56 Other variables can be set using VAR=VAL on the command line.
58 After the script is loaded, option values that were not set on the
59 command line are replaced by the values set in the script.""")
61 gopts.default('defconfig')
63 gopts.opt('config', short='F', val='FILE',
64 fn=set_value, default=None,
65 use="""Domain configuration to use (SXP).
66 SXP is the underlying configuration format used by Xen.
67 SXP configurations can be hand-written or generated from Python configuration
68 scripts, using the -n (dryrun) option to print the configuration.""")
70 gopts.opt('load', short='L', val='FILE',
71 fn=set_value, default=None,
72 use='Domain saved state to load.')
74 gopts.opt('dryrun', short='n',
75 fn=set_true, default=0,
76 use="""Dry run - print the configuration but don't create the domain.
77 Loads the configuration script, creates the SXP configuration and prints it.""")
79 gopts.opt('paused', short='p',
80 fn=set_true, default=0,
81 use='Leave the domain paused after it is created.')
83 gopts.opt('console_autoconnect', short='c',
84 fn=set_true, default=0,
85 use="Connect to the console after the domain is created.")
87 gopts.var('vnc', val='no|yes',
88 fn=set_bool, default=None,
89 use="""Spawn a vncviewer listening for a vnc server in the domain.
90 The address of the vncviewer is passed to the domain on the kernel command
91 line using 'VNC_SERVER=<host>:<port>'. The port used by vnc is 5500 + DISPLAY.
92 A display value with a free port is chosen if possible.
93 """)
95 gopts.var('name', val='NAME',
96 fn=set_value, default=None,
97 use="Domain name. Must be unique.")
99 gopts.var('bootloader', val='FILE',
100 fn=set_value, default=None,
101 use="Path to bootloader.")
103 gopts.var('bootentry', val='NAME',
104 fn=set_value, default=None,
105 use="Entry to boot via boot loader")
107 gopts.var('kernel', val='FILE',
108 fn=set_value, default=None,
109 use="Path to kernel image.")
111 gopts.var('ramdisk', val='FILE',
112 fn=set_value, default='',
113 use="Path to ramdisk.")
115 gopts.var('builder', val='FUNCTION',
116 fn=set_value, default='linux',
117 use="Function to use to build the domain.")
119 gopts.var('memory', val='MEMORY',
120 fn=set_int, default=128,
121 use="Domain memory in MB.")
123 gopts.var('ssidref', val='SSIDREF',
124 fn=set_u32, default=-1,
125 use="Security Identifier.")
127 gopts.var('maxmem', val='MEMORY',
128 fn=set_int, default=None,
129 use="Maximum domain memory in MB.")
131 gopts.var('cpu', val='CPU',
132 fn=set_int, default=None,
133 use="CPU to run the domain on.")
135 gopts.var('vcpus', val='VCPUS',
136 fn=set_int, default=1,
137 use="# of Virtual CPUS in domain.")
139 gopts.var('cpu_weight', val='WEIGHT',
140 fn=set_float, default=None,
141 use="""Set the new domain's cpu weight.
142 WEIGHT is a float that controls the domain's share of the cpu.""")
144 gopts.var('console', val='PORT',
145 fn=set_int, default=None,
146 use="Console port to use. Default is 9600 + domain id.")
148 gopts.var('restart', val='onreboot|always|never',
149 fn=set_value, default=None,
150 use="""Whether the domain should be restarted on exit.
151 - onreboot: restart on exit with shutdown code reboot
152 - always: always restart on exit, ignore exit code
153 - never: never restart on exit, ignore exit code""")
155 gopts.var('blkif', val='no|yes',
156 fn=set_bool, default=0,
157 use="Make the domain a block device backend.")
159 gopts.var('netif', val='no|yes',
160 fn=set_bool, default=0,
161 use="Make the domain a network interface backend.")
163 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
164 fn=append_value, default=[],
165 use="""Add a disk device to a domain. The physical device is DEV,
166 which is exported to the domain as VDEV. The disk is read-only if MODE
167 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
168 backend driver domain to use for the disk.
169 The option may be repeated to add more than one disk.""")
171 gopts.var('pci', val='BUS,DEV,FUNC',
172 fn=append_value, default=[],
173 use="""Add a PCI device to a domain, using given params (in hex).
174 For example '-pci c0,02,1a'.
175 The option may be repeated to add more than one pci device.""")
177 gopts.var('usb', val='PATH',
178 fn=append_value, default=[],
179 use="""Add a physical USB port to a domain, as specified by the path
180 to that port. This option may be repeated to add more than one port.""")
182 gopts.var('ipaddr', val="IPADDR",
183 fn=append_value, default=[],
184 use="Add an IP address to the domain.")
186 gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM,vifname=NAME",
187 fn=append_value, default=[],
188 use="""Add a network interface with the given MAC address and bridge.
189 The vif is configured by calling the given configuration script.
190 If mac is not specified a random MAC address is used.
191 The MAC address of the backend interface can be selected with be_mac.
192 If not specified then the network backend chooses it's own MAC address.
193 If bridge is not specified the default bridge is used.
194 If script is not specified the default script is used.
195 If backend is not specified the default backend driver domain is used.
196 If vifname is not specified the backend virtual interface will have name vifD.N
197 where D is the domain id and N is the interface id.
198 This option may be repeated to add more than one vif.
199 Specifying vifs will increase the number of interfaces as needed.""")
201 gopts.var('nics', val="NUM",
202 fn=set_int, default=1,
203 use="""Set the number of network interfaces.
204 Use the vif option to define interface parameters, otherwise
205 defaults are used. Specifying vifs will increase the
206 number of interfaces as needed.""")
208 gopts.var('root', val='DEVICE',
209 fn=set_value, default='',
210 use="""Set the root= parameter on the kernel command line.
211 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
213 gopts.var('extra', val="ARGS",
214 fn=set_value, default='',
215 use="Set extra arguments to append to the kernel command line.")
217 gopts.var('ip', val='IPADDR',
218 fn=set_value, default='',
219 use="Set the kernel IP interface address.")
221 gopts.var('gateway', val="IPADDR",
222 fn=set_value, default='',
223 use="Set the kernel IP gateway.")
225 gopts.var('netmask', val="MASK",
226 fn=set_value, default = '',
227 use="Set the kernel IP netmask.")
229 gopts.var('hostname', val="NAME",
230 fn=set_value, default='',
231 use="Set the kernel IP hostname.")
233 gopts.var('interface', val="INTF",
234 fn=set_value, default="eth0",
235 use="Set the kernel IP interface name.")
237 gopts.var('dhcp', val="off|dhcp",
238 fn=set_value, default='off',
239 use="Set the kernel dhcp option.")
241 gopts.var('nfs_server', val="IPADDR",
242 fn=set_value, default=None,
243 use="Set the address of the NFS server for NFS root.")
245 gopts.var('nfs_root', val="PATH",
246 fn=set_value, default=None,
247 use="Set the path of the root NFS directory.")
249 gopts.var('memmap', val='FILE',
250 fn=set_value, default='',
251 use="Path to memap SXP file.")
253 gopts.var('device_model', val='FILE',
254 fn=set_value, default='',
255 use="Path to device model program.")
257 gopts.var('device_config', val='FILE',
258 fn=set_value, default='',
259 use="Path to device model configuration.")
261 def strip(pre, s):
262 """Strip prefix 'pre' if present.
263 """
264 if s.startswith(pre):
265 return s[len(pre):]
266 else:
267 return s
269 def configure_image(opts, config, vals):
270 """Create the image config.
271 """
272 config_image = [ vals.builder ]
273 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
274 if vals.ramdisk:
275 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
276 if vals.cmdline_ip:
277 cmdline_ip = strip('ip=', vals.cmdline_ip)
278 config_image.append(['ip', cmdline_ip])
279 if vals.root:
280 cmdline_root = strip('root=', vals.root)
281 config_image.append(['root', cmdline_root])
282 if vals.extra:
283 config_image.append(['args', vals.extra])
284 if vals.vcpus:
285 config_image.append(['vcpus', vals.vcpus])
286 config.append(['image', config_image ])
289 def configure_disks(opts, config_devs, vals):
290 """Create the config for disks (virtual block devices).
291 """
292 for (uname, dev, mode, backend) in vals.disk:
293 config_vbd = ['vbd',
294 ['uname', uname],
295 ['dev', dev ],
296 ['mode', mode ] ]
297 if backend:
298 config_vbd.append(['backend', backend])
299 config_devs.append(['device', config_vbd])
301 def configure_pci(opts, config_devs, vals):
302 """Create the config for pci devices.
303 """
304 for (bus, dev, func) in vals.pci:
305 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
306 config_devs.append(['device', config_pci])
308 def configure_usb(opts, config_devs, vals):
309 for path in vals.usb:
310 config_usb = ['usb', ['path', path]]
311 config_devs.append(['device', config_usb])
313 def randomMAC():
314 """Generate a random MAC address.
316 Uses OUI (Organizationally Unique Identifier) AA:00:00, an
317 unassigned one that used to belong to DEC. The OUI list is
318 available at 'standards.ieee.org'.
320 The remaining 3 fields are random, with the first bit of the first
321 random field set 0.
323 @return: MAC address string
324 """
325 random.seed()
326 mac = [ 0xaa, 0x00, 0x00,
327 random.randint(0x00, 0x7f),
328 random.randint(0x00, 0xff),
329 random.randint(0x00, 0xff) ]
330 return ':'.join(map(lambda x: "%02x" % x, mac))
332 def configure_vifs(opts, config_devs, vals):
333 """Create the config for virtual network interfaces.
334 """
335 vifs = vals.vif
336 vifs_n = max(vals.nics, len(vifs))
338 for idx in range(0, vifs_n):
339 if idx < len(vifs):
340 d = vifs[idx]
341 mac = d.get('mac')
342 if not mac:
343 mac = randomMAC()
344 be_mac = d.get('be_mac')
345 bridge = d.get('bridge')
346 script = d.get('script')
347 backend = d.get('backend')
348 ip = d.get('ip')
349 vifname = d.get('vifname')
350 else:
351 mac = randomMAC()
352 be_mac = None
353 bridge = None
354 script = None
355 backend = None
356 ip = None
357 vifname = None
358 config_vif = ['vif']
359 config_vif.append(['mac', mac])
360 if vifname:
361 config_vif.append(['vifname', vifname])
362 if be_mac:
363 config_vif.append(['be_mac', be_mac])
364 if bridge:
365 config_vif.append(['bridge', bridge])
366 if script:
367 config_vif.append(['script', script])
368 if backend:
369 config_vif.append(['backend', backend])
370 if ip:
371 config_vif.append(['ip', ip])
372 config_devs.append(['device', config_vif])
374 def configure_vfr(opts, config, vals):
375 if not vals.ipaddr: return
376 config_vfr = ['vfr']
377 idx = 0 # No way of saying which IP is for which vif?
378 for ip in vals.ipaddr:
379 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
380 config.append(config_vfr)
382 def configure_vmx(opts, config_devs, vals):
383 """Create the config for VMX devices.
384 """
385 memmap = vals.memmap
386 device_model = vals.device_model
387 device_config = vals.device_config
388 config_devs.append(['memmap', memmap])
389 config_devs.append(['device_model', device_model])
390 config_devs.append(['device_config', device_config])
392 def run_bootloader(opts, config, vals):
393 if not os.access(vals.bootloader, os.X_OK):
394 opts.err("Bootloader isn't executable")
395 if len(vals.disk) < 1:
396 opts.err("No disks configured and boot loader requested")
397 (uname, dev, mode, backend) = vals.disk[0]
398 file = blkif.blkdev_uname_to_file(uname)
400 blcfg = bootloader(vals.bootloader, file, not vals.console_autoconnect,
401 vals.vcpus, vals.blentry)
403 config.append(['bootloader', vals.bootloader])
404 config.append(blcfg)
406 def make_config(opts, vals):
407 """Create the domain configuration.
408 """
410 config = ['vm',
411 ['name', vals.name ],
412 ['memory', vals.memory ],
413 ['ssidref', vals.ssidref ]]
414 if vals.maxmem:
415 config.append(['maxmem', vals.maxmem])
416 if vals.cpu is not None:
417 config.append(['cpu', vals.cpu])
418 if vals.cpu_weight is not None:
419 config.append(['cpu_weight', vals.cpu_weight])
420 if vals.blkif:
421 config.append(['backend', ['blkif']])
422 if vals.netif:
423 config.append(['backend', ['netif']])
424 if vals.restart:
425 config.append(['restart', vals.restart])
426 if vals.console:
427 config.append(['console', vals.console])
429 if vals.bootloader:
430 run_bootloader(opts, config, vals)
431 else:
432 configure_image(opts, config, vals)
433 config_devs = []
434 configure_disks(opts, config_devs, vals)
435 configure_pci(opts, config_devs, vals)
436 configure_vifs(opts, config_devs, vals)
437 configure_usb(opts, config_devs, vals)
438 configure_vmx(opts, config_devs, vals)
439 config += config_devs
441 return config
443 def preprocess_disk(opts, vals):
444 if not vals.disk: return
445 disk = []
446 for v in vals.disk:
447 d = v.split(',')
448 n = len(d)
449 if n == 3:
450 d.append(None)
451 elif n == 4:
452 pass
453 else:
454 opts.err('Invalid disk specifier: ' + v)
455 disk.append(d)
456 vals.disk = disk
458 def preprocess_pci(opts, vals):
459 if not vals.pci: return
460 pci = []
461 for v in vals.pci:
462 d = v.split(',')
463 if len(d) != 3:
464 opts.err('Invalid pci specifier: ' + v)
465 # Components are in hex: add hex specifier.
466 hexd = map(lambda v: '0x'+v, d)
467 pci.append(hexd)
468 vals.pci = pci
470 def preprocess_vifs(opts, vals):
471 if not vals.vif: return
472 vifs = []
473 for vif in vals.vif:
474 d = {}
475 a = vif.split(',')
476 for b in a:
477 (k, v) = b.strip().split('=', 1)
478 k = k.strip()
479 v = v.strip()
480 if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip', 'vifname']:
481 opts.err('Invalid vif specifier: ' + vif)
482 d[k] = v
483 vifs.append(d)
484 vals.vif = vifs
486 def preprocess_ip(opts, vals):
487 if vals.ip or vals.dhcp != 'off':
488 dummy_nfs_server = '1.2.3.4'
489 ip = (vals.ip
490 + ':' + (vals.nfs_server or dummy_nfs_server)
491 + ':' + vals.gateway
492 + ':' + vals.netmask
493 + ':' + vals.hostname
494 + ':' + vals.interface
495 + ':' + vals.dhcp)
496 else:
497 ip = ''
498 vals.cmdline_ip = ip
500 def preprocess_nfs(opts, vals):
501 if not vals.nfs_root: return
502 if not vals.nfs_server:
503 opts.err('Must set nfs root and nfs server')
504 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
505 vals.extra = nfs + ' ' + vals.extra
508 def get_host_addr():
509 host = socket.gethostname()
510 addr = socket.gethostbyname(host)
511 return addr
513 VNC_BASE_PORT = 5500
515 def choose_vnc_display():
516 """Try to choose a free vnc display.
517 """
518 def netstat_local_ports():
519 """Run netstat to get a list of the local ports in use.
520 """
521 l = os.popen("netstat -nat").readlines()
522 r = []
523 # Skip 2 lines of header.
524 for x in l[2:]:
525 # Local port is field 3.
526 y = x.split()[3]
527 # Field is addr:port, split off the port.
528 y = y.split(':')[1]
529 r.append(int(y))
530 return r
532 ports = netstat_local_ports()
533 for d in range(1, 100):
534 port = VNC_BASE_PORT + d
535 if port in ports: continue
536 return d
537 return None
539 def spawn_vnc(display):
540 os.system("vncviewer -log *:stdout:0 -listen %d &" %
541 (VNC_BASE_PORT + display))
542 return VNC_BASE_PORT + display
544 def preprocess_vnc(opts, vals):
545 """If vnc was specified, spawn a vncviewer in listen mode
546 and pass its address to the domain on the kernel command line.
547 """
548 if not vals.vnc: return
549 vnc_display = choose_vnc_display()
550 if not vnc_display:
551 opts.warn("No free vnc display")
552 return
553 print 'VNC=', vnc_display
554 vnc_port = spawn_vnc(vnc_display)
555 if vnc_port > 0:
556 vnc_host = get_host_addr()
557 vnc = 'VNC_VIEWER=%s:%d' % (vnc_host, vnc_port)
558 vals.extra = vnc + ' ' + vals.extra
560 def preprocess(opts, vals):
561 if not vals.kernel:
562 opts.err("No kernel specified")
563 preprocess_disk(opts, vals)
564 preprocess_pci(opts, vals)
565 preprocess_vifs(opts, vals)
566 preprocess_ip(opts, vals)
567 preprocess_nfs(opts, vals)
568 preprocess_vnc(opts, vals)
570 def make_domain(opts, config):
571 """Create, build and start a domain.
573 @param opts: options
574 @param config: configuration
575 @return: domain id, console port
576 @rtype: (int, int)
577 """
579 try:
580 if opts.vals.load:
581 filename = os.path.abspath(opts.vals.load)
582 dominfo = server.xend_domain_restore(filename, config)
583 else:
584 dominfo = server.xend_domain_create(config)
585 except XendError, ex:
586 opts.err(str(ex))
588 dom = sxp.child_value(dominfo, 'name')
589 console_info = sxp.child(dominfo, 'console')
590 if console_info:
591 console_port = int(sxp.child_value(console_info, 'console_port'))
592 else:
593 console_port = None
595 if not opts.vals.paused:
596 if server.xend_domain_unpause(dom) < 0:
597 server.xend_domain_destroy(dom)
598 opts.err("Failed to unpause domain %s" % dom)
599 opts.info("Started domain %s, console on port %d"
600 % (dom, console_port))
601 return (dom, console_port)
603 def main(argv):
604 opts = gopts
605 args = opts.parse(argv)
606 if opts.vals.help:
607 opts.usage()
608 if opts.vals.help or opts.vals.help_config:
609 opts.load_defconfig(help=1)
610 if opts.vals.help or opts.vals.help_config:
611 return
612 # Process remaining args as config variables.
613 for arg in args:
614 if '=' in arg:
615 (var, val) = arg.strip().split('=', 1)
616 gopts.setvar(var.strip(), val.strip())
617 if opts.vals.config:
618 config = opts.vals.config
619 else:
620 opts.load_defconfig()
621 opts.vals.vnc = not opts.vals.dryrun
622 preprocess(opts, opts.vals)
623 if not opts.getopt('name') and opts.getopt('defconfig'):
624 opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
625 config = make_config(opts, opts.vals)
627 if opts.vals.dryrun:
628 PrettyPrint.prettyprint(config)
629 else:
630 (dom, console) = make_domain(opts, config)
631 if opts.vals.console_autoconnect:
632 path = "/var/lib/xend/console-%s" % console
633 console_client.connect('localhost', console, path=path)
635 if __name__ == '__main__':
636 main(sys.argv)