debuggers.hg

view tools/python/xen/xm/create.py @ 3685:bbe8541361dd

bitkeeper revision 1.1159.1.542 (42038a42_52IAalMZRKdTn0UbVN5fw)

Merge tempest.cl.cam.ac.uk:/auto/groups/xeno-xenod/BK/xeno.bk
into tempest.cl.cam.ac.uk:/local/scratch/smh22/xen-unstable.bk
author smh22@tempest.cl.cam.ac.uk
date Fri Feb 04 14:44:18 2005 +0000 (2005-02-04)
parents bb56e77896e7 ef59b38283a5
children 0a4b76b6b5a0
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
9 from xen.xend import sxp
10 from xen.xend import PrettyPrint
11 from xen.xend.XendClient import server, XendError
13 from xen.util import console_client
15 from xen.xm.opts import *
17 gopts = Opts(use="""[options] [vars]
19 Create a domain.
21 Domain creation parameters can be set by command-line switches, from
22 a python configuration script or an SXP config file. See documentation
23 for --defconfig, --config. Configuration variables can be set using
24 VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
26 """)
28 gopts.opt('help', short='h',
29 fn=set_true, default=0,
30 use="Print this help.")
32 gopts.opt('help_config',
33 fn=set_true, default=0,
34 use="Print help for the configuration script.")
36 gopts.opt('quiet', short='q',
37 fn=set_true, default=0,
38 use="Quiet.")
40 gopts.opt('path', val='PATH',
41 fn=set_value, default='.:/etc/xen',
42 use="""Search path for configuration scripts.
43 The value of PATH is a colon-separated directory list.""")
45 gopts.opt('defconfig', short='f', val='FILE',
46 fn=set_value, default='xmdefconfig',
47 use="""Use the given Python configuration script.
48 The configuration script is loaded after arguments have been processed.
49 Each command-line option sets a configuration variable named after
50 its long option name, and these variables are placed in the
51 environment of the script before it is loaded.
52 Variables for options that may be repeated have list values.
53 Other variables can be set using VAR=VAL on the command line.
55 After the script is loaded, option values that were not set on the
56 command line are replaced by the values set in the script.""")
58 gopts.default('defconfig')
60 gopts.opt('config', short='F', val='FILE',
61 fn=set_value, default=None,
62 use="""Domain configuration to use (SXP).
63 SXP is the underlying configuration format used by Xen.
64 SXP configurations can be hand-written or generated from Python configuration
65 scripts, using the -n (dryrun) option to print the configuration.""")
67 gopts.opt('load', short='L', val='FILE',
68 fn=set_value, default=None,
69 use='Domain saved state to load.')
71 gopts.opt('dryrun', short='n',
72 fn=set_true, default=0,
73 use="""Dry run - print the configuration but don't create the domain.
74 Loads the configuration script, creates the SXP configuration and prints it.""")
76 gopts.opt('paused', short='p',
77 fn=set_true, default=0,
78 use='Leave the domain paused after it is created.')
80 gopts.opt('console_autoconnect', short='c',
81 fn=set_true, default=0,
82 use="Connect to the console after the domain is created.")
84 gopts.var('name', val='NAME',
85 fn=set_value, default=None,
86 use="Domain name. Must be unique.")
88 gopts.var('kernel', val='FILE',
89 fn=set_value, default=None,
90 use="Path to kernel image.")
92 gopts.var('ramdisk', val='FILE',
93 fn=set_value, default='',
94 use="Path to ramdisk.")
96 gopts.var('builder', val='FUNCTION',
97 fn=set_value, default='linux',
98 use="Function to use to build the domain.")
100 gopts.var('memory', val='MEMORY',
101 fn=set_int, default=128,
102 use="Domain memory in MB.")
104 gopts.var('maxmem', val='MEMORY',
105 fn=set_int, default=None,
106 use="Maximum domain memory in MB.")
108 gopts.var('cpu', val='CPU',
109 fn=set_int, default=None,
110 use="CPU to run the domain on.")
112 gopts.var('vcpus', val='VCPUS',
113 fn=set_int, default=1,
114 use="# of Virtual CPUS in domain.")
116 gopts.var('cpu_weight', val='WEIGHT',
117 fn=set_float, default=None,
118 use="""Set the new domain's cpu weight.
119 WEIGHT is a float that controls the domain's share of the cpu.""")
121 gopts.var('console', val='PORT',
122 fn=set_int, default=None,
123 use="Console port to use. Default is 9600 + domain id.")
125 gopts.var('restart', val='onreboot|always|never',
126 fn=set_value, default=None,
127 use="""Whether the domain should be restarted on exit.
128 - onreboot: restart on exit with shutdown code reboot
129 - always: always restart on exit, ignore exit code
130 - never: never restart on exit, ignore exit code""")
132 gopts.var('blkif', val='no|yes',
133 fn=set_bool, default=0,
134 use="Make the domain a block device backend.")
136 gopts.var('netif', val='no|yes',
137 fn=set_bool, default=0,
138 use="Make the domain a network interface backend.")
140 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
141 fn=append_value, default=[],
142 use="""Add a disk device to a domain. The physical device is DEV,
143 which is exported to the domain as VDEV. The disk is read-only if MODE
144 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
145 backend driver domain to use for the disk.
146 The option may be repeated to add more than one disk.""")
148 gopts.var('pci', val='BUS,DEV,FUNC',
149 fn=append_value, default=[],
150 use="""Add a PCI device to a domain, using given params (in hex).
151 For example '-pci c0,02,1a'.
152 The option may be repeated to add more than one pci device.""")
154 gopts.var('usb', val='PATH',
155 fn=append_value, default=[],
156 use="""Add a physical USB port to a domain, as specified by the path
157 to that port. This option may be repeated to add more than one port.""")
159 gopts.var('ipaddr', val="IPADDR",
160 fn=append_value, default=[],
161 use="Add an IP address to the domain.")
163 gopts.var('vif', val="mac=MAC,be_mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM",
164 fn=append_value, default=[],
165 use="""Add a network interface with the given MAC address and bridge.
166 The vif is configured by calling the given configuration script.
167 If mac is not specified a random MAC address is used.
168 The MAC address of the backend interface can be selected with be_mac.
169 If not specified then the network backend chooses it's own MAC address.
170 If bridge is not specified the default bridge is used.
171 If script is not specified the default script is used.
172 If backend is not specified the default backend driver domain is used.
173 This option may be repeated to add more than one vif.
174 Specifying vifs will increase the number of interfaces as needed.""")
176 gopts.var('nics', val="NUM",
177 fn=set_int, default=1,
178 use="""Set the number of network interfaces.
179 Use the vif option to define interface parameters, otherwise
180 defaults are used. Specifying vifs will increase the
181 number of interfaces as needed.""")
183 gopts.var('root', val='DEVICE',
184 fn=set_value, default='',
185 use="""Set the root= parameter on the kernel command line.
186 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
188 gopts.var('extra', val="ARGS",
189 fn=set_value, default='',
190 use="Set extra arguments to append to the kernel command line.")
192 gopts.var('ip', val='IPADDR',
193 fn=set_value, default='',
194 use="Set the kernel IP interface address.")
196 gopts.var('gateway', val="IPADDR",
197 fn=set_value, default='',
198 use="Set the kernel IP gateway.")
200 gopts.var('netmask', val="MASK",
201 fn=set_value, default = '',
202 use="Set the kernel IP netmask.")
204 gopts.var('hostname', val="NAME",
205 fn=set_value, default='',
206 use="Set the kernel IP hostname.")
208 gopts.var('interface', val="INTF",
209 fn=set_value, default="eth0",
210 use="Set the kernel IP interface name.")
212 gopts.var('dhcp', val="off|dhcp",
213 fn=set_value, default='off',
214 use="Set the kernel dhcp option.")
216 gopts.var('nfs_server', val="IPADDR",
217 fn=set_value, default=None,
218 use="Set the address of the NFS server for NFS root.")
220 gopts.var('nfs_root', val="PATH",
221 fn=set_value, default=None,
222 use="Set the path of the root NFS directory.")
224 gopts.var('memmap', val='FILE',
225 fn=set_value, default='',
226 use="Path to memap SXP file.")
228 gopts.var('device_model', val='FILE',
229 fn=set_value, default='',
230 use="Path to device model program.")
232 gopts.var('device_config', val='FILE',
233 fn=set_value, default='',
234 use="Path to device model configuration.")
236 def strip(pre, s):
237 """Strip prefix 'pre' if present.
238 """
239 if s.startswith(pre):
240 return s[len(pre):]
241 else:
242 return s
244 def configure_image(config, vals):
245 """Create the image config.
246 """
247 config_image = [ vals.builder ]
248 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
249 if vals.ramdisk:
250 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
251 if vals.cmdline_ip:
252 cmdline_ip = strip('ip=', vals.cmdline_ip)
253 config_image.append(['ip', cmdline_ip])
254 if vals.root:
255 cmdline_root = strip('root=', vals.root)
256 config_image.append(['root', cmdline_root])
257 if vals.extra:
258 config_image.append(['args', vals.extra])
259 if vals.vcpus:
260 config_image.append(['vcpus', vals.vcpus])
261 config.append(['image', config_image ])
264 def configure_disks(config_devs, vals):
265 """Create the config for disks (virtual block devices).
266 """
267 for (uname, dev, mode, backend) in vals.disk:
268 config_vbd = ['vbd',
269 ['uname', uname],
270 ['dev', dev ],
271 ['mode', mode ] ]
272 if backend:
273 config_vbd.append(['backend', backend])
274 config_devs.append(['device', config_vbd])
276 def configure_pci(config_devs, vals):
277 """Create the config for pci devices.
278 """
279 for (bus, dev, func) in vals.pci:
280 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
281 config_devs.append(['device', config_pci])
283 def configure_usb(config_devs, vals):
284 for path in vals.usb:
285 config_usb = ['usb', ['path', path]]
286 config_devs.append(['device', config_usb])
288 def randomMAC():
289 """Generate a random MAC address.
291 Uses OUI (Organizationally Unique Identifier) AA:00:00, an
292 unassigned one that used to belong to DEC. The OUI list is
293 available at 'standards.ieee.org'.
295 The remaining 3 fields are random, with the first bit of the first
296 random field set 0.
298 @return: MAC address string
299 """
300 random.seed()
301 mac = [ 0xaa, 0x00, 0x00,
302 random.randint(0x00, 0x7f),
303 random.randint(0x00, 0xff),
304 random.randint(0x00, 0xff) ]
305 return ':'.join(map(lambda x: "%02x" % x, mac))
307 def configure_vifs(config_devs, vals):
308 """Create the config for virtual network interfaces.
309 """
310 vifs = vals.vif
311 vifs_n = max(vals.nics, len(vifs))
313 for idx in range(0, vifs_n):
314 if idx < len(vifs):
315 d = vifs[idx]
316 mac = d.get('mac')
317 if not mac:
318 mac = randomMAC()
319 be_mac = d.get('be_mac')
320 bridge = d.get('bridge')
321 script = d.get('script')
322 backend = d.get('backend')
323 ip = d.get('ip')
324 else:
325 mac = randomMAC()
326 be_mac = None
327 bridge = None
328 script = None
329 backend = None
330 ip = None
331 config_vif = ['vif']
332 config_vif.append(['mac', mac])
333 if be_mac:
334 config_vif.append(['be_mac', be_mac])
335 if bridge:
336 config_vif.append(['bridge', bridge])
337 if script:
338 config_vif.append(['script', script])
339 if backend:
340 config_vif.append(['backend', backend])
341 if ip:
342 config_vif.append(['ip', ip])
343 config_devs.append(['device', config_vif])
345 def configure_vfr(config, vals):
346 if not vals.ipaddr: return
347 config_vfr = ['vfr']
348 idx = 0 # No way of saying which IP is for which vif?
349 for ip in vals.ipaddr:
350 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
351 config.append(config_vfr)
353 def configure_vmx(config_devs, vals):
354 """Create the config for VMX devices.
355 """
356 memmap = vals.memmap
357 device_model = vals.device_model
358 device_config = vals.device_config
359 config_devs.append(['memmap', memmap])
360 config_devs.append(['device_model', device_model])
361 config_devs.append(['device_config', device_config])
363 def make_config(vals):
364 """Create the domain configuration.
365 """
367 config = ['vm',
368 ['name', vals.name ],
369 ['memory', vals.memory ]]
370 if vals.maxmem:
371 config.append(['maxmem', vals.maxmem])
372 if vals.cpu is not None:
373 config.append(['cpu', vals.cpu])
374 if vals.cpu_weight is not None:
375 config.append(['cpu_weight', vals.cpu_weight])
376 if vals.blkif:
377 config.append(['backend', ['blkif']])
378 if vals.netif:
379 config.append(['backend', ['netif']])
380 if vals.restart:
381 config.append(['restart', vals.restart])
382 if vals.console:
383 config.append(['console', vals.console])
385 configure_image(config, vals)
386 config_devs = []
387 configure_disks(config_devs, vals)
388 configure_pci(config_devs, vals)
389 configure_vifs(config_devs, vals)
390 configure_usb(config_devs, vals)
391 configure_vmx(config_devs, vals)
392 config += config_devs
393 return config
395 def preprocess_disk(opts, vals):
396 if not vals.disk: return
397 disk = []
398 for v in vals.disk:
399 d = v.split(',')
400 n = len(d)
401 if n == 3:
402 d.append(None)
403 elif n == 4:
404 pass
405 else:
406 opts.err('Invalid disk specifier: ' + v)
407 disk.append(d)
408 vals.disk = disk
410 def preprocess_pci(opts, vals):
411 if not vals.pci: return
412 pci = []
413 for v in vals.pci:
414 d = v.split(',')
415 if len(d) != 3:
416 opts.err('Invalid pci specifier: ' + v)
417 # Components are in hex: add hex specifier.
418 hexd = map(lambda v: '0x'+v, d)
419 pci.append(hexd)
420 vals.pci = pci
422 def preprocess_vifs(opts, vals):
423 if not vals.vif: return
424 vifs = []
425 for vif in vals.vif:
426 d = {}
427 a = vif.split(',')
428 for b in a:
429 (k, v) = b.strip().split('=', 1)
430 k = k.strip()
431 v = v.strip()
432 if k not in ['mac', 'be_mac', 'bridge', 'script', 'backend', 'ip']:
433 opts.err('Invalid vif specifier: ' + vif)
434 d[k] = v
435 vifs.append(d)
436 vals.vif = vifs
438 def preprocess_ip(opts, vals):
439 if vals.ip or vals.dhcp != 'off':
440 dummy_nfs_server = '1.2.3.4'
441 ip = (vals.ip
442 + ':' + (vals.nfs_server or dummy_nfs_server)
443 + ':' + vals.gateway
444 + ':' + vals.netmask
445 + ':' + vals.hostname
446 + ':' + vals.interface
447 + ':' + vals.dhcp)
448 else:
449 ip = ''
450 vals.cmdline_ip = ip
452 def preprocess_nfs(opts, vals):
453 if not vals.nfs_root: return
454 if not vals.nfs_server:
455 opts.err('Must set nfs root and nfs server')
456 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
457 vals.extra = nfs + ' ' + vals.extra
459 def preprocess(opts, vals):
460 if not vals.kernel:
461 opts.err("No kernel specified")
462 preprocess_disk(opts, vals)
463 preprocess_pci(opts, vals)
464 preprocess_vifs(opts, vals)
465 preprocess_ip(opts, vals)
466 preprocess_nfs(opts, vals)
468 def make_domain(opts, config):
469 """Create, build and start a domain.
471 @param opts: options
472 @param config: configuration
473 @return: domain id, console port
474 @rtype: (int, int)
475 """
477 try:
478 if opts.vals.load:
479 filename = os.path.abspath(opts.vals.load)
480 dominfo = server.xend_domain_restore(filename, config)
481 else:
482 dominfo = server.xend_domain_create(config)
483 except XendError, ex:
484 opts.err(str(ex))
486 dom = sxp.child_value(dominfo, 'name')
487 console_info = sxp.child(dominfo, 'console')
488 if console_info:
489 console_port = int(sxp.child_value(console_info, 'console_port'))
490 else:
491 console_port = None
493 if not opts.vals.paused:
494 if server.xend_domain_unpause(dom) < 0:
495 server.xend_domain_destroy(dom)
496 opts.err("Failed to unpause domain %s" % dom)
497 opts.info("Started domain %s, console on port %d"
498 % (dom, console_port))
499 return (dom, console_port)
501 def main(argv):
502 opts = gopts
503 args = opts.parse(argv)
504 if opts.vals.help:
505 opts.usage()
506 if opts.vals.help or opts.vals.help_config:
507 opts.load_defconfig(help=1)
508 if opts.vals.help or opts.vals.help_config:
509 return
510 # Process remaining args as config variables.
511 for arg in args:
512 if '=' in arg:
513 (var, val) = arg.strip().split('=', 1)
514 gopts.setvar(var.strip(), val.strip())
515 if opts.vals.config:
516 config = opts.vals.config
517 else:
518 opts.load_defconfig()
519 preprocess(opts, opts.vals)
520 if not opts.getopt('name') and opts.getopt('defconfig'):
521 opts.setopt('name', os.path.basename(opts.getopt('defconfig')))
522 config = make_config(opts.vals)
523 if opts.vals.dryrun:
524 PrettyPrint.prettyprint(config)
525 else:
526 (dom, console) = make_domain(opts, config)
527 if opts.vals.console_autoconnect:
528 console_client.connect('localhost', console)
530 if __name__ == '__main__':
531 main(sys.argv)