bitkeeper revision 1.959.1.3 (40cf2938j76NDs4xtyvevBrKpmogjA)
authormjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Tue, 15 Jun 2004 16:52:08 +0000 (16:52 +0000)
committermjw@wray-m-3.hpl.hp.com <mjw@wray-m-3.hpl.hp.com>
Tue, 15 Jun 2004 16:52:08 +0000 (16:52 +0000)
Initial checkin for grand unified management app xm.

.rootkeys
tools/examples/Makefile
tools/examples/xmdefaults [new file with mode: 0644]
tools/xenmgr/Makefile
tools/xenmgr/lib/xm/create.py [new file with mode: 0644]
tools/xenmgr/lib/xm/main.py [new file with mode: 0644]
tools/xenmgr/lib/xm/opts.py [new file with mode: 0644]
tools/xenmgr/lib/xm/shutdown.py [new file with mode: 0644]
tools/xenmgr/xm [new file with mode: 0755]

index 63f51f11ae8b8be29a30f3bfe1dc7468ec33be20..a20dd32e1fe0d46d8166759bc204e4a89fb73a1d 100644 (file)
--- a/.rootkeys
+++ b/.rootkeys
 40c9c4684Wfg8VgMKtRFa_ULi2e_tQ tools/examples/xm_dom_control.py
 40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py
 40c9c468QKoqBHjb5Qwrm60pNVcVng tools/examples/xm_vd_tool.py
+40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmdefaults
 3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
 40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen
 3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
 40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/xenmgr/lib/server/netif.py
 40c9c469ZqILEQ8x6yWy0_51jopiCg tools/xenmgr/lib/server/params.py
 40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/xenmgr/lib/sxp.py
+40cf2937gKQcATgXKGtNeWb1PDH5nA tools/xenmgr/lib/xm/create.py
+40cf2937isyS250zyd0Q2GuEDoNXfQ tools/xenmgr/lib/xm/main.py
+40cf2937PSslwBliN1g7ofDy2H_RhA tools/xenmgr/lib/xm/opts.py
+40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/xenmgr/lib/xm/shutdown.py
 40c9c469kT0H9COWzA4XzPBjWK0WsA tools/xenmgr/netfix
 40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/xenmgr/setup.py
 40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/xenmgr/xend
 40c9c469JkN47d1oXi-e0RjAP-C6uQ tools/xenmgr/xenmgrd
+40cf2937dqM1jWW87O5OoOYND8leuA tools/xenmgr/xm
 403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
 40a107afN60pFdURgBv9KwEzgRl5mQ tools/xentrace/formats
 4050c413PhhLNAYk3TEwP37i_iLw9Q tools/xentrace/xentrace.8
index fba54aae447bc748fd79e2cd306708864aa46f1c..ed8e4c71b121e6a9f90f9ce2e10ddcd6305c8160 100644 (file)
@@ -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 (file)
index 0000000..61ebc53
--- /dev/null
@@ -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
+
+#============================================================================
index 125f8cf4e88082ff221993062975037575c3370f..642cd2273e238202e0be51f3d9e40647c72dec89 100644 (file)
@@ -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 (file)
index 0000000..e09f567
--- /dev/null
@@ -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 (file)
index 0000000..0bc56e6
--- /dev/null
@@ -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 (file)
index 0000000..61374cb
--- /dev/null
@@ -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 (file)
index 0000000..21782f3
--- /dev/null
@@ -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 (executable)
index 0000000..ff5bbd7
--- /dev/null
@@ -0,0 +1,6 @@
+#!/usr/bin/python
+#  -*- mode: python; -*-
+import sys
+from xenmgr.xm import main
+
+main.main(sys.argv)