aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/examples/Makefile2
-rw-r--r--tools/examples/xmdefaults96
-rw-r--r--tools/xenmgr/Makefile2
-rw-r--r--tools/xenmgr/lib/xm/create.py287
-rw-r--r--tools/xenmgr/lib/xm/main.py161
-rw-r--r--tools/xenmgr/lib/xm/opts.py183
-rw-r--r--tools/xenmgr/lib/xm/shutdown.py65
-rwxr-xr-xtools/xenmgr/xm6
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)