diff options
Diffstat (limited to 'tools')
-rw-r--r-- | tools/examples/Makefile | 2 | ||||
-rw-r--r-- | tools/examples/xmdefaults | 96 | ||||
-rw-r--r-- | tools/xenmgr/Makefile | 2 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/create.py | 287 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/main.py | 161 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/opts.py | 183 | ||||
-rw-r--r-- | tools/xenmgr/lib/xm/shutdown.py | 65 | ||||
-rwxr-xr-x | tools/xenmgr/xm | 6 |
8 files changed, 800 insertions, 2 deletions
diff --git a/tools/examples/Makefile b/tools/examples/Makefile index fba54aae44..ed8e4c71b1 100644 --- a/tools/examples/Makefile +++ b/tools/examples/Makefile @@ -1,6 +1,6 @@ INSTALL = $(wildcard *.py) -ETC = defaults democd netbsd +ETC = defaults democd netbsd xmdefaults INITD = init.d/xendomains init.d/xend all: diff --git a/tools/examples/xmdefaults b/tools/examples/xmdefaults new file mode 100644 index 0000000000..61ebc5349b --- /dev/null +++ b/tools/examples/xmdefaults @@ -0,0 +1,96 @@ +# -*- mode: python; -*- +#============================================================================ +# Python defaults setup for 'xm create'. +# Edit this file to reflect the configuration of your system. +# This file expects the variable 'vmid' to be set. +#============================================================================ + +import sys +import xenctl.ip + +try: + vmid = int(vmid) # convert to integer +except: + raise ValueError, "Variable 'vmid' must be an integer" + +if vmid <= 0: + raise ValueError, "Variable 'vmid' must be greater than 0" + + +#---------------------------------------------------------------------------- +# Kernel image file. +kernel = "../../../install/boot/vmlinuz-2.4.26-xen" + +# Optional ramdisk. +#ramdisk = "/boot/initrd.gz" + +# The domain build function. Default is 'linux'. +#builder='linux' +#builder='netbsd' + +# Initial memory allocation (in megabytes) for the new domain. +memory = 64 + +# A handy name for your new domain. +name = "This is VM %d" % vmid + +# Which CPU to start domain on? +#cpu = -1 # leave to Xen to pick +cpu = vmid # set based on vmid (mod number of CPUs) + +#---------------------------------------------------------------------------- +# Define network interfaces. + +# Number of network interfaces. Default is 1. +#nics=1 + +# List of MAC addresses for the network interfaces. +# If there aren't enough addresses for all the interfaces +# the rest get random MACs. +#mac = [ "aa:00:00:00:00:11" ] + +#---------------------------------------------------------------------------- +# Define the disk devices you want the domain to have access to, and +# what you want them accessible as. +# Each disk entry is of the form phy:DEV,VDEV,MODE +# where DEV is the device, VDEV is the device name the domain will see, +# and MODE is r for read-only, w for read-write. + +disk = [ 'phy:sda%d,sda1,w' % (7+vmid), + 'phy:sda6,sda6,r' ] + +#---------------------------------------------------------------------------- +# Set the kernel command line for the new domain. +# You only need to define the IP parameters and hostname if the domain's +# IP config doesn't, e.g. in ifcfg-eth0 or via DHCP. +# You can use 'extra' to set the runlevel and custom environment +# variables used by custom rc scripts (e.g. VMID=, usr= ). + +# Set if you want dhcp to allocate the IP address. +#dhcp="dhcp" +# Set netmask. +#netmask= +# Set default gateway. +#gateway= +# Set the hostname. +#hostname= "vm%d" % vmid + +# Set root device. +root = "/dev/sda1 ro" + +# Root device for nfs. +#root = "/dev/nfs" +# The nfs server. +#nfs_server = '169.254.1.0' +# Root directory on the nfs server. +#nfs_root = '/full/path/to/root/directory' + +# Sets runlevel 4 and the device for /usr. +extra = "4 VMID=%d usr=/dev/sda6" % vmid + +#---------------------------------------------------------------------------- +# Set according to whether you want the domain restarted when it exits. +# The default is False. +#restart = True + +#============================================================================ diff --git a/tools/xenmgr/Makefile b/tools/xenmgr/Makefile index 125f8cf4e8..642cd2273e 100644 --- a/tools/xenmgr/Makefile +++ b/tools/xenmgr/Makefile @@ -11,9 +11,9 @@ install: all python setup.py install --root="$(prefix)"; \ fi mkdir -p $(prefix)/usr/sbin - install -m0755 xenmgrd $(prefix)/usr/sbin install -m0755 xend $(prefix)/usr/sbin install -m0755 netfix $(prefix)/usr/sbin + install -m0755 xm $(prefix)/usr/sbin clean: rm -rf build *.pyc *.pyo *.o *.a *~ diff --git a/tools/xenmgr/lib/xm/create.py b/tools/xenmgr/lib/xm/create.py new file mode 100644 index 0000000000..e09f567311 --- /dev/null +++ b/tools/xenmgr/lib/xm/create.py @@ -0,0 +1,287 @@ +import string +import sys + +from xenmgr import sxp +from xenmgr import PrettyPrint +from xenmgr.XendClient import server + +from xenmgr.xm.opts import * + +opts = Opts(use="""[options] + +Create a domain. +""") + +opts.opt('help', short='h', + fn=set_value, default=0, + use="Print this help.") + +opts.opt('quiet', short='q', + fn=set_true, default=0, + use="Quiet.") + +opts.opt('path', val='PATH', + fn=set_value, default='.:/etc/xc', + use="Search path for default scripts.") + +opts.opt('defaults', short='f', val='FILE', + fn=set_value, default='xmdefaults', + use="Use the given default script.") + +opts.opt('config', short='F', val='FILE', + fn=set_value, default=None, + use='Domain configuration to use.') + +def set_var(opt, k, v): + opt.set(v) + for d in string.split(v, ';' ): + (k, v) = string.split(d, '=') + opt.opts.setvar(k, v) + +opts.opt('define', short='D', val='VAR=VAL', + fn=set_var, default=None, + use="Set a variable before loading defaults,e.g. '-D vmid=3;ip=1.2.3.4'.") + +opts.opt('dryrun', short='n', + fn=set_true, default=0, + use="Dry run - print the config but don't create the domain.") + +opts.opt('console', short='c', + fn=set_true, default=0, + use="Connect to console after domain is created.") + +opts.opt('kernel', short='k', val='FILE', + use="Path to kernel image.") + +opts.opt('ramdisk', short='r', val='FILE', + fn=set_value, default='', + use="Path to ramdisk.") + +opts.opt('builder', short='b', val='FUNCTION', + fn=set_value, default='linux', + use="Function to use to build the domain.") + +opts.opt('memory', short='m', val='MEMORY', + fn=set_value, default=128, + use="Domain memory in MB.") + +opts.opt('disk', short='d', val='phy:DEV,VDEV,MODE', + fn=append_value, default=[], + use="""Add a disk device to a domain. The physical device is DEV, which + is exported to the domain as VDEV. The disk is read-only if MODE is r, + read-write if mode is 'w'.""") + +opts.opt('ipaddr', short='i', val="IPADDR", + fn=append_value, default=[], + use="Add an IP address to the domain.") + +opts.opt('mac', short='M', val="MAC", + fn=append_value, default=[], + use="""Add a network interface with the given mac address to the domain. + More than one interface may be specified. Interfaces with unspecified MAC addresses + are allocated a random address.""") + +opts.opt('nics', val="N", + fn=set_int, default="1", + use="Set the number of network interfaces.") + +opts.opt('vnet', val='VNET', + fn=set_append_value, default=[], + use="""Define the vnets for the network interfaces. + More than one vnet may be given, they are used in order. + """) + +opts.opt('root', short='R', val='DEVICE', + fn=set_value, default='', + use="""Set the root= parameter on the kernel command line. + Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""") + +opts.opt('extra', short='E', val="ARGS", + fn=set_value, default='', + use="Set extra arguments to append to the kernel command line.") + +opts.opt('ip', short='I', val='IPADDR', + fn=set_value, default=[], + use="Set the kernel IP interface address.") + +opts.opt('gateway', val="IPADDR", + fn=set_value, default='', + use="Set kernel IP gateway.") + +opts.opt('netmask', val="MASK", + fn=set_value, default = '', + use="Set kernel IP netmask.") + +opts.opt('hostname', val="NAME", + fn=set_value, default='', + use="Set kernel IP hostname.") + +opts.opt('interface', val="INTF", + fn=set_value, default="eth0", + use="Set the kernel IP interface name.") + +opts.opt('dhcp', val="off|dhcp", + fn=set_value, default='off', + use="Set kernel dhcp option.") + +opts.opt('nfs_server', val="IPADDR", + fn=set_value, default=None, + use="Set the address of the NFS server for NFS root.") + +opts.opt('nfs_root', val="PATH", + fn=set_value, default=None, + use="Set the path of the root NFS directory.") + +def strip(pre, s): + if s.startswith(pre): + return s[len(pre):] + else: + return s + +def make_domain_config(opts): + + config = ['config', + ['name', opts.name ], + ['memory', opts.memory ] ] + if opts.cpu: + config.append(['cpu', opts.cpu]) + + config_image = [ opts.builder ] + config_image.append([ 'kernel', os.path.abspath(opts.kernel) ]) + if opts.ramdisk: + config_image.append([ 'ramdisk', os.path.abspath(opts.ramdisk) ]) + if opts.cmdline_ip: + cmdline_ip = strip("ip=", opts.cmdline_ip) + config_image.append(['ip', cmdline_ip]) + if opts.root: + cmdline_root = strip("root=", opts.root) + config_image.append(['root', opts.root]) + if opts.extra: + config_image.append(['args', opts.extra]) + config.append(['image', config_image ]) + + config_devs = [] + for (uname, dev, mode) in opts.disk: + config_vbd = ['vbd', + ['uname', uname], + ['dev', dev ], + ['mode', mode ] ] + config_devs.append(['device', config_vbd]) + + for (bus, dev, func) in opts.pci: + config_pci = ['pci', ['bus', bus], ['dev', dev], ['func', func]] + config_devs.append(['device', config_pci]) + + for idx in range(0, opts.nics): + config_vif = ['vif' ['@', ['id', 'vif%d' % idx]]] + if idx < len(opts.mac): + config_vif.append(['mac', opts.mac[idx]]) + config_devs.append(['device', config_vif]) + + config += config_devs + +## if vfr_ipaddr: +## config_vfr = ['vfr'] +## idx = 0 # No way of saying which IP is for which vif? +## for ip in vfr_ipaddr: +## config_vfr.append(['vif', ['id', idx], ['ip', ip]]) +## config.append(config_vfr) + + if opts.vnet: + config_vnet = ['vnet'] + idx = 0 + for vnet in opts.vnet: + config_vif = ['vif', ['id', 'vif%d' % idx], ['vnet', vnet]] + config_vnet.append(config_vif) + idx += 1 + config.append(config_vnet) + + return config + +def preprocess_disk(opts): + if not opts.disk: return + disk = [] + for v in opts.disk: + d = v.split(',') + if len(d) != 3: + opts.err('Invalid disk specifier: ' + v) + disk.append(d) + opts.disk = d + +def preprocess_pci(opts): + if not opts.pci: return + pci = [] + for v in opts.pci: + d = v.split(',') + if len(d) != 3: + opts.err('Invalid pci specifier: ' + v) + pci.append(d) + opts.pci = pci + +def preprocess_ip(opts): + setip = (opts.hostname or opts.netmask + or opts.gateway or opts.dhcp or opts.interface) + if not setip: return + ip = (opts.ip + + ':' + + ':' + opts.gateway + + ':' + opts.netmask + + ':' + opts.hostname + + ':' : opts.interface + + ':' + opts.dhcp) + opts.cmdline_ip = ip + +def preprocess_nfs(opts): + if (opts.nfs_root or opts.nfs_server): + if (not opts.nfs_root) or (not opts.nfs_server): + opts.err('Must set nfs root and nfs server') + else: + return + nfs = 'nfsroot=' + opts.nfs_server + ':' + opts.nfs_root + opts.extra = nfs + ' ' + opts.extra + +def preprocess(opts): + preprocess_disk(opts) + preprocess_pci(opts) + preprocess_ip(opts) + preprocess_nfs(opts) + +def make_domain(config): + """Create, build and start a domain. + Returns: [int] the ID of the new domain. + """ + restore = 0 #todo + + if restore: + dominfo = server.xend_domain_restore(state_file, config) + else: + dominfo = server.xend_domain_create(config) + + dom = int(sxp.child_value(dominfo, 'id')) + console_info = sxp.child(dominfo, 'console') + if console_info: + console_port = int(sxp.child_value(console_info, 'port')) + else: + console_port = None + + if server.xend_domain_start(dom) < 0: + server.xend_domain_halt(dom) + opts.err("Failed to start domain %d" % dom) + opts.info("Started domain %d, console on port %d" + % (dom, console_port)) + return (dom, console_port) + +def main(argv): + args = opts.parse(argv) + if opts.config: + pass + else: + opts.load_defaults() + if opts.help: + opts.usage() + preprocess(opts) + config = make_config(opts) + make_domain(opts, config) + +if __name__ == '__main__': + main(sys.argv) diff --git a/tools/xenmgr/lib/xm/main.py b/tools/xenmgr/lib/xm/main.py new file mode 100644 index 0000000000..0bc56e699c --- /dev/null +++ b/tools/xenmgr/lib/xm/main.py @@ -0,0 +1,161 @@ +#!/usr/bin/python +import string +import sys + +from xenmgr.XendClient import server + +from xenmgr.xm import create, shutdown + +class Xm: + + def __init__(self): + self.prog = 'xm' + pass + + def err(self, msg): + print >>sys.stderr, "Error:", msg + sys.exit(1) + + def main(self, args): + self.prog = args[0] + if len(args) < 2: + self.err("Missing command\nTry '%s help' for more information." + % self.prog) + prog = 'xm_' + args[1] + help = self.helparg(args) + fn = getattr(self, prog, self.unknown) + fn(help, args[1:]) + + def helparg(self, args): + for a in args: + if a in ['-h', '--help']: + return 1 + return 0 + + def unknown(self, args): + self.err("Unknown command: %s\nTry '%s help' for more information." + % (args[0], self.prog)) + + def help(self, meth, args): + name = meth[3:] + f = getattr(self, meth) + print "%s\t%s" % (name, f.__doc__ or '') + + def xm_help(self, help, args): + """Print help.""" + for k in dir(self): + if not k.startswith('xm_'): continue + self.help(k, args) + print "\nTry '%s CMD -h' for help on CMD" % self.prog + + def xm_create(self, help, args): + """Create a domain.""" + create.main(args) + + def xm_save(self, help, args): + """Save domain state to file.""" + if help: + print "save DOM FILE" + print "\nSave domain with id DOM to FILE." + return + if len(args) < 3: self.err("%s: Missing arguments" % args[0]) + dom = args[1] + filename = args[2] + server.xend_domain_save(dom, filename) + + def xm_restore(self, help, args): + """Create a domain from a saved state.""" + if help: + print "restore FILE" + print "\nRestore a domain from FILE." + if len(args) < 2: self.err("%s: Missing file" % args[0]) + server.xend_domain_restore(dom, None, filename) + + def xm_ls(self, help, args): + """List domains.""" + if help: self.help('xm_ls'); return + doms = server.xend_domains() + for dom in doms: + d = server.domain(dom) + print d + + def xm_halt(self, help, args): + """Terminate a domain immediately.""" + if help: + print 'halt DOM' + print '\nTerminate domain DOM immediately.' + return + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_halt(dom) + + def xm_shutdown(self, help, args): + """Shutdown a domain.""" + shutdown.main(args) + + def xm_stop(self, help, args): + """Stop execution of a domain.""" + if help: + print 'stop DOM' + print '\nStop execution of domain DOM.' + return + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_stop(dom) + + def xm_start(self, help, args): + """Start execution of a domain.""" + if help: + print 'start DOM' + print '\nStart execution of domain DOM.' + return + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + server.xend_domain_start(dom) + + def xm_pincpu(self, help, args): + """Pin a domain to a cpu. """ + if help: + print 'pincpu DOM CPU' + print '\nPin domain DOM to cpu CPU.' + return + pass + + def xm_bvt(self, help, args): + pass + + def xm_bvtslice(self, help, args): + pass + + def xm_atropos(self, help, args): + pass + + def xm_rrslice(self, help, args): + pass + + def xm_info(self, help, args): + """Get information about the xen host.""" + if help: self.help('xm_info'); return + info = server.xend_node() + for x in info: + print "%-23s:" % x[0], x[1] + + def xm_console(self, help, args): + """Open a console to a domain.""" + if help: + print "console DOM" + print "\nOpen a console to domain DOM." + return + if len(args) < 2: self.err("%s: Missing domain" % args[0]) + dom = args[1] + info = server.xend_domain(dom) + console = sxp.child(info, "console") + if not console: + self.err("No console information") + port = sxp.child_value(console, "port") + from xenctl import console_client + console_client.connect("localhost", int(port)) + +def main(args): + xm = Xm() + xm.main(args) diff --git a/tools/xenmgr/lib/xm/opts.py b/tools/xenmgr/lib/xm/opts.py new file mode 100644 index 0000000000..61374cbf92 --- /dev/null +++ b/tools/xenmgr/lib/xm/opts.py @@ -0,0 +1,183 @@ +from getopt import getopt +import os +import os.path +import sys + +class Opt: + def __init__(self, opts, name, short=None, long=None, + val=None, fn=None, use=None, default=None): + self.opts = opts + self.name = name + self.short = short + if long is None: + long = name + self.long = long + self.val = val + self.use = use + self.default = default + self.optkeys = [] + if self.short: + self.optkeys.append('-' + self.short) + if self.long: + self.optkeys.append('--' + self.long) + self.fn = fn + self.specified_opt = None + self.specified_val = None + self.set(default) + + def set(self, value): + setattr(self.opts, self.name, value) + + def get(self): + return getattr(self.opts, self.name) + + def append(self, value): + self.set(self.get().append(value)) + + def short_opt(self): + if self.short: + if self.val: + return self.short + ':' + else: + return self.short + else: + return None + + def long_opt(self): + if self.long: + if self.val: + return self.long + '=' + else: + return self.long + else: + return None + + def show(self): + sep = '' + for x in self.optkeys: + print sep, x, + sep = ',' + if self.val: + print self.val, + print + if self.use: + print '\t', + print self.use + if self.val: + print '\tDefault', self.default or 'None' + + def specify(self, k, v): + if k in self.optkeys: + if self.val is None and v: + self.opts.err("Option '%s' does not take a value" % k) + self.specified_opt = k + self.specified_val = v + if self.fn: + self.fn(self, k, v) + return 1 + else: + return 0 + +class Opts: + def __init__(self, use=None): + self._usage = use + self._options = [] + self._argv = [] + self._vals = {} + self._globals = {} + self._locals = {} + + def opt(self, name, **args): + x = Opt(self, name, **args) + self._options.append(x) + return x + + def setvar(self, name, val): + self._globals[name] = val + + def err(self, msg): + print >>sys.stderr, "Error:", msg + sys.exit(1) + + def info(self, msg): + if self.quiet: return + print msg + + def warn(self, msg): + print >>sys.stderr, "Warning:", msg + + def parse(self, argv): + self._argv = argv + (vals, args) = getopt(argv[1:], self.short_opts(), self.long_opts()) + self._args = args + for (k, v) in vals: + for opt in self._options: + if opt.specify(k, v): break + else: + print >>sys.stderr, "Error: Unknown option:", k + self.usage() + return args + + def short_opts(self): + l = [] + for x in self._options: + y = x.short_opt() + if not y: continue + l.append(y) + return ''.join(l) + + def long_opts(self): + l = [] + for x in self._options: + y = x.long_opt() + if not y: continue + l.append(y) + return ''.join(l) + + def usage(self): + print 'Usage: ', self._argv[0], self._usage or 'OPTIONS' + for opt in self._options: + opt.show() + + def load_defaults(self): + print 'load_defaults>', 'defaults=', self.defaults + print 'load_defaults>', 'path=', self.path + for x in [ '' ] + self.path.split(':'): + print 'load_defaults>', 'x=', x, 'defaults=', self.defaults + if x: + p = os.path.join(x, self.defaults) + else: + p = self.defaults + if os.stat(p): + self.load(p) + break + else: + self.err("Cannot open defaults file %s" % self.defaults) + + def load(self, defaults): + print 'load>', 'defaults=', defaults + self._globals['sys'] = sys + self._globals['config_file'] = defaults + execfile(defaults, self._globals, self._locals) + print 'load>', 'globals=', self._globals + print 'load>', 'locals=', self._locals + + +def set_true(opt, k, v): + opt.set(1) + +def set_false(opt, k, v): + opt.set(0) + +def set_value(opt, k, v): + opt.set(v) + +def set_int(opt, k, v): + try: + v = int(v) + except: + opt.opts.err('Invalid value: ' + str(v)) + opt.set(v) + +def append_value(opt, k, v): + opt.append(v) diff --git a/tools/xenmgr/lib/xm/shutdown.py b/tools/xenmgr/lib/xm/shutdown.py new file mode 100644 index 0000000000..21782f36d0 --- /dev/null +++ b/tools/xenmgr/lib/xm/shutdown.py @@ -0,0 +1,65 @@ +import string +import sys +import time + +from xenmgr.XendClient import server +from xenmgr.xm import opts + +opts = Opts(use="""[options] [DOM] + +Shutdown one or more domains gracefully.""") + +opts.opt('help', short='h', + fn=set_value, default=0, + use="Print this help.") + +opts.opt('all', short='a', + fn=set_true, default=0, + use="Shutdown all domains.") + +opts.opt('wait', short='w', + fn=set_true, default=0, + use='Wait for shutdown to complete.') + +def shutdown(opts, doms, wait): + def domains(): + return [ int(a) for a in server.xend_domains() ] + if doms == None: doms = domains() + if 0 in doms: + doms.remove(0) + for d in doms: + server.xend_domain_shutdown(dom) + if wait: + while doms: + alive = domains() + dead = [] + for d in doms: + if d in alive: continue + dead.append(d) + for d in dead: + opts.info("Domain %d terminated" % d) + doms.remove(d) + time.sleep(1) + opts.info("All domains terminated") + +def main_all(opts, args): + shutdown(opts, None, opts.wait) + +def main_dom(opts, args): + if len(args) < 2: opts.err('Missing domain') + dom = argv[1] + try: + domid = int(dom) + except: + opts.err('Invalid domain: ' + dom) + shutdown(opts, [ domid ], opts.wait) + +def main(argv): + args = opts.parse(argv) + if opts.help: + opts.usage() + if opts.all: + main_all(opts, args) + else: + main_dom(opts, args) + diff --git a/tools/xenmgr/xm b/tools/xenmgr/xm new file mode 100755 index 0000000000..ff5bbd75ca --- /dev/null +++ b/tools/xenmgr/xm @@ -0,0 +1,6 @@ +#!/usr/bin/python +# -*- mode: python; -*- +import sys +from xenmgr.xm import main + +main.main(sys.argv) |