ia64/xen-unstable

view tools/python/xen/xm/create.py @ 2636:611881eac889

bitkeeper revision 1.1159.1.230 (416d965arlQEDDF33KaiMFJy_o-s4g)

Better random MAC addresses.
author kaf24@freefall.cl.cam.ac.uk
date Wed Oct 13 20:55:54 2004 +0000 (2004-10-13)
parents b079795700ab
children b112cdeb83fa
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.opt('config', short='F', val='FILE',
59 fn=set_value, default=None,
60 use="""Domain configuration to use (SXP).
61 SXP is the underlying configuration format used by Xen.
62 SXP configurations can be hand-written or generated from Python configuration
63 scripts, using the -n (dryrun) option to print the configuration.""")
65 gopts.opt('load', short='L', val='FILE',
66 fn=set_value, default=None,
67 use='Domain saved state to load.')
69 gopts.opt('dryrun', short='n',
70 fn=set_true, default=0,
71 use="""Dry run - print the configuration but don't create the domain.
72 Loads the configuration script, creates the SXP configuration and prints it.""")
74 gopts.opt('paused', short='p',
75 fn=set_true, default=0,
76 use='Leave the domain paused after it is created.')
78 gopts.opt('console_autoconnect', short='c',
79 fn=set_true, default=0,
80 use="Connect to the console after the domain is created.")
82 gopts.var('name', val='NAME',
83 fn=set_value, default=None,
84 use="Domain name. Must be unique.")
86 gopts.var('kernel', val='FILE',
87 fn=set_value, default=None,
88 use="Path to kernel image.")
90 gopts.var('ramdisk', val='FILE',
91 fn=set_value, default='',
92 use="Path to ramdisk.")
94 gopts.var('builder', val='FUNCTION',
95 fn=set_value, default='linux',
96 use="Function to use to build the domain.")
98 gopts.var('memory', val='MEMORY',
99 fn=set_int, default=128,
100 use="Domain memory in MB.")
102 gopts.var('maxmem', val='MEMORY',
103 fn=set_int, default=None,
104 use="Maximum domain memory in MB.")
106 gopts.var('cpu', val='CPU',
107 fn=set_int, default=None,
108 use="CPU to run the domain on.")
110 gopts.var('cpu_weight', val='WEIGHT',
111 fn=set_float, default=None,
112 use="""Set the new domain's cpu weight.
113 WEIGHT is a float that controls the domain's share of the cpu.""")
115 gopts.var('console', val='PORT',
116 fn=set_int, default=None,
117 use="Console port to use. Default is 9600 + domain id.")
119 gopts.var('restart', val='onreboot|always|never',
120 fn=set_value, default=None,
121 use="""Whether the domain should be restarted on exit.
122 - onreboot: restart on exit with shutdown code reboot
123 - always: always restart on exit, ignore exit code
124 - never: never restart on exit, ignore exit code""")
126 gopts.var('blkif', val='no|yes',
127 fn=set_bool, default=0,
128 use="Make the domain a block device backend.")
130 gopts.var('netif', val='no|yes',
131 fn=set_bool, default=0,
132 use="Make the domain a network interface backend.")
134 gopts.var('disk', val='phy:DEV,VDEV,MODE[,DOM]',
135 fn=append_value, default=[],
136 use="""Add a disk device to a domain. The physical device is DEV,
137 which is exported to the domain as VDEV. The disk is read-only if MODE
138 is 'r', read-write if MODE is 'w'. If DOM is specified it defines the
139 backend driver domain to use for the disk.
140 The option may be repeated to add more than one disk.""")
142 gopts.var('pci', val='BUS,DEV,FUNC',
143 fn=append_value, default=[],
144 use="""Add a PCI device to a domain, using given params (in hex).
145 For example '-pci c0,02,1a'.
146 The option may be repeated to add more than one pci device.""")
148 gopts.var('ipaddr', val="IPADDR",
149 fn=append_value, default=[],
150 use="Add an IP address to the domain.")
152 gopts.var('vif', val="mac=MAC,bridge=BRIDGE,script=SCRIPT,backend=DOM",
153 fn=append_value, default=[],
154 use="""Add a network interface with the given MAC address and bridge.
155 The vif is configured by calling the given configuration script.
156 If mac is not specified a random MAC address is used.
157 If bridge is not specified the default bridge is used.
158 If script is not specified the default script is used.
159 If backend is not specified the default backend driver domain is used.
160 This option may be repeated to add more than one vif.
161 Specifying vifs will increase the number of interfaces as needed.""")
163 gopts.var('nics', val="NUM",
164 fn=set_int, default=1,
165 use="""Set the number of network interfaces.
166 Use the vif option to define interface parameters, otherwise
167 defaults are used. Specifying vifs will increase the
168 number of interfaces as needed.""")
170 gopts.var('root', val='DEVICE',
171 fn=set_value, default='',
172 use="""Set the root= parameter on the kernel command line.
173 Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
175 gopts.var('extra', val="ARGS",
176 fn=set_value, default='',
177 use="Set extra arguments to append to the kernel command line.")
179 gopts.var('ip', val='IPADDR',
180 fn=set_value, default='',
181 use="Set the kernel IP interface address.")
183 gopts.var('gateway', val="IPADDR",
184 fn=set_value, default='',
185 use="Set the kernel IP gateway.")
187 gopts.var('netmask', val="MASK",
188 fn=set_value, default = '',
189 use="Set the kernel IP netmask.")
191 gopts.var('hostname', val="NAME",
192 fn=set_value, default='',
193 use="Set the kernel IP hostname.")
195 gopts.var('interface', val="INTF",
196 fn=set_value, default="eth0",
197 use="Set the kernel IP interface name.")
199 gopts.var('dhcp', val="off|dhcp",
200 fn=set_value, default='off',
201 use="Set the kernel dhcp option.")
203 gopts.var('nfs_server', val="IPADDR",
204 fn=set_value, default=None,
205 use="Set the address of the NFS server for NFS root.")
207 gopts.var('nfs_root', val="PATH",
208 fn=set_value, default=None,
209 use="Set the path of the root NFS directory.")
211 def strip(pre, s):
212 """Strip prefix 'pre' if present.
213 """
214 if s.startswith(pre):
215 return s[len(pre):]
216 else:
217 return s
219 def configure_image(config, vals):
220 """Create the image config.
221 """
222 config_image = [ vals.builder ]
223 config_image.append([ 'kernel', os.path.abspath(vals.kernel) ])
224 if vals.ramdisk:
225 config_image.append([ 'ramdisk', os.path.abspath(vals.ramdisk) ])
226 if vals.cmdline_ip:
227 cmdline_ip = strip('ip=', vals.cmdline_ip)
228 config_image.append(['ip', cmdline_ip])
229 if vals.root:
230 cmdline_root = strip('root=', vals.root)
231 config_image.append(['root', cmdline_root])
232 if vals.extra:
233 config_image.append(['args', vals.extra])
234 config.append(['image', config_image ])
236 def configure_disks(config_devs, vals):
237 """Create the config for disks (virtual block devices).
238 """
239 for (uname, dev, mode, backend) in vals.disk:
240 config_vbd = ['vbd',
241 ['uname', uname],
242 ['dev', dev ],
243 ['mode', mode ] ]
244 if backend:
245 config_vbd.append(['backend', backend])
246 config_devs.append(['device', config_vbd])
248 def configure_pci(config_devs, vals):
249 """Create the config for pci devices.
250 """
251 for (bus, dev, func) in vals.pci:
252 config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]]
253 config_devs.append(['device', config_pci])
255 def randomMAC():
256 """Generate a random MAC address.
258 Uses OUI (Organizationally Unique Identifier) AA:00:00, an
259 unassigned one that used to belong to DEC. The OUI list is
260 available at 'standards.ieee.org'.
262 The remaining 3 fields are random, with the first bit of the first
263 random field set 0.
265 @return: MAC address string
266 """
267 random.seed()
268 mac = [ 0xaa, 0x00, 0x00,
269 random.randint(0x00, 0x7f),
270 random.randint(0x00, 0xff),
271 random.randint(0x00, 0xff) ]
272 return ':'.join(map(lambda x: "%02x" % x, mac))
274 def configure_vifs(config_devs, vals):
275 """Create the config for virtual network interfaces.
276 """
277 vifs = vals.vif
278 vifs_n = max(vals.nics, len(vifs))
280 for idx in range(0, vifs_n):
281 if idx < len(vifs):
282 d = vifs[idx]
283 mac = d.get('mac')
284 bridge = d.get('bridge')
285 script = d.get('script')
286 backend = d.get('backend')
287 else:
288 mac = randomMAC()
289 bridge = None
290 script = None
291 backend = None
292 config_vif = ['vif']
293 config_vif.append(['mac', mac])
294 if bridge:
295 config_vif.append(['bridge', bridge])
296 if script:
297 config_vif.append(['script', script])
298 if backend:
299 config_vif.append(['backend', backend])
300 config_devs.append(['device', config_vif])
302 def configure_vfr(config, vals):
303 if not vals.ipaddr: return
304 config_vfr = ['vfr']
305 idx = 0 # No way of saying which IP is for which vif?
306 for ip in vals.ipaddr:
307 config_vfr.append(['vif', ['id', idx], ['ip', ip]])
308 config.append(config_vfr)
311 def make_config(vals):
312 """Create the domain configuration.
313 """
315 config = ['vm',
316 ['name', vals.name ],
317 ['memory', vals.memory ]]
318 if vals.maxmem:
319 config.append(['maxmem', vals.maxmem])
320 if vals.cpu is not None:
321 config.append(['cpu', vals.cpu])
322 if vals.cpu_weight is not None:
323 config.append(['cpu_weight', vals.cpu_weight])
324 if vals.blkif:
325 config.append(['backend', ['blkif']])
326 if vals.netif:
327 config.append(['backend', ['netif']])
328 if vals.restart:
329 config.append(['restart', vals.restart])
330 if vals.console:
331 config.append(['console', vals.console])
333 configure_image(config, vals)
334 config_devs = []
335 configure_disks(config_devs, vals)
336 configure_pci(config_devs, vals)
337 configure_vifs(config_devs, vals)
338 config += config_devs
339 return config
341 def preprocess_disk(opts, vals):
342 if not vals.disk: return
343 disk = []
344 for v in vals.disk:
345 d = v.split(',')
346 n = len(d)
347 if n == 3:
348 d.append(None)
349 elif n == 4:
350 pass
351 else:
352 opts.err('Invalid disk specifier: ' + v)
353 disk.append(d)
354 vals.disk = disk
356 def preprocess_pci(opts, vals):
357 if not vals.pci: return
358 pci = []
359 for v in vals.pci:
360 d = v.split(',')
361 if len(d) != 3:
362 opts.err('Invalid pci specifier: ' + v)
363 # Components are in hex: add hex specifier.
364 hexd = map(lambda v: '0x'+v, d)
365 pci.append(hexd)
366 vals.pci = pci
368 def preprocess_vifs(opts, vals):
369 if not vals.vif: return
370 vifs = []
371 for vif in vals.vif:
372 d = {}
373 a = vif.split(',')
374 for b in a:
375 (k, v) = b.strip().split('=', 1)
376 k = k.strip()
377 v = v.strip()
378 if k not in ['mac', 'bridge', 'script', 'backend']:
379 opts.err('Invalid vif specifier: ' + vif)
380 d[k] = v
381 vifs.append(d)
382 vals.vif = vifs
384 def preprocess_ip(opts, vals):
385 if vals.ip or vals.dhcp != 'off':
386 dummy_nfs_server = '1.2.3.4'
387 ip = (vals.ip
388 + ':' + (vals.nfs_server or dummy_nfs_server)
389 + ':' + vals.gateway
390 + ':' + vals.netmask
391 + ':' + vals.hostname
392 + ':' + vals.interface
393 + ':' + vals.dhcp)
394 else:
395 ip = ''
396 vals.cmdline_ip = ip
398 def preprocess_nfs(opts, vals):
399 if not vals.nfs_root: return
400 if not vals.nfs_server:
401 opts.err('Must set nfs root and nfs server')
402 nfs = 'nfsroot=' + vals.nfs_server + ':' + vals.nfs_root
403 vals.extra = nfs + ' ' + vals.extra
405 def preprocess(opts, vals):
406 if not vals.kernel:
407 opts.err("No kernel specified")
408 preprocess_disk(opts, vals)
409 preprocess_pci(opts, vals)
410 preprocess_vifs(opts, vals)
411 preprocess_ip(opts, vals)
412 preprocess_nfs(opts, vals)
414 def make_domain(opts, config):
415 """Create, build and start a domain.
417 @param opts: options
418 @param config: configuration
419 @return: domain id, console port
420 @rtype: (int, int)
421 """
423 try:
424 if opts.vals.load:
425 filename = os.path.abspath(opts.vals.load)
426 dominfo = server.xend_domain_restore(filename, config)
427 else:
428 dominfo = server.xend_domain_create(config)
429 except XendError, ex:
430 opts.err(str(ex))
432 dom = sxp.child_value(dominfo, 'name')
433 console_info = sxp.child(dominfo, 'console')
434 if console_info:
435 console_port = int(sxp.child_value(console_info, 'console_port'))
436 else:
437 console_port = None
439 if not opts.vals.paused:
440 if server.xend_domain_unpause(dom) < 0:
441 server.xend_domain_destroy(dom)
442 opts.err("Failed to unpause domain %s" % dom)
443 opts.info("Started domain %s, console on port %d"
444 % (dom, console_port))
445 return (dom, console_port)
447 def main(argv):
448 opts = gopts
449 args = opts.parse(argv)
450 if opts.vals.help:
451 opts.usage()
452 if opts.vals.help or opts.vals.help_config:
453 opts.load_defconfig(help=1)
454 if opts.vals.help or opts.vals.help_config:
455 return
456 # Process remaining args as config variables.
457 for arg in args:
458 if '=' in arg:
459 (var, val) = arg.strip().split('=', 1)
460 gopts.setvar(var.strip(), val.strip())
461 if opts.vals.config:
462 config = opts.vals.config
463 else:
464 opts.load_defconfig()
465 preprocess(opts, opts.vals)
466 config = make_config(opts.vals)
467 if opts.vals.dryrun:
468 PrettyPrint.prettyprint(config)
469 else:
470 (dom, console) = make_domain(opts, config)
471 if opts.vals.console_autoconnect:
472 console_client.connect('localhost', console)
474 if __name__ == '__main__':
475 main(sys.argv)