diff options
Diffstat (limited to 'tools')
26 files changed, 1088 insertions, 1493 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/xc_vd_tool.py b/tools/examples/xc_vd_tool.py deleted file mode 100755 index 9184d357bd..0000000000 --- a/tools/examples/xc_vd_tool.py +++ /dev/null @@ -1,153 +0,0 @@ -#!/usr/bin/env python - -import xenctl.utils, sys, re, string - -def usage(): - - print >>sys.stderr,""" -Usage: %s command <params> - - initialise [dev] [[ext_size]] - init. a physcial partition to store vd's - create [size] [[expiry]] - allocate a vd of specified size (and expiry) - enlarge [vdid] [extra_size] - enlarge a specified vd by some amount - delete [vdid] - delete a vd - import [filename] [[expiry]] - create a vd and populate w/ image from file - export [vdid] [filename] - copy vd's contents to a file - setexpiry [vdid] [[expiry]] - update the expiry time for a vd - list - list all the unexpired virtual disks - undelete [vdid] [[expiry]] - attempts to recover an expired vd - freespace - print out the amount of space in free pool - -notes: - vdid - the virtual disk's identity string - size - measured in MB - expiry - is the expiry time of the virtual disk in seconds from now - (0 = don't expire) - device - physical partition to 'format' to hold vd's. e.g. hda4 - ext_size - extent size (default 64MB) -""" % sys.argv[0] - -if len(sys.argv) < 2: - usage() - sys.exit(-1) - -rc='' -src='' -expiry_time = 0 -cmd = sys.argv[1] - -if cmd == 'initialise': - - dev = sys.argv[2] - - if len(sys.argv) > 3: - extent_size = int(sys.argv[3]) - else: - print """No extent size specified - using default size of 64MB""" - extent_size = 64 - - print "Formatting for virtual disks" - print "Device: " + dev - print "Extent size: " + str(extent_size) + "MB" - - rc = xenctl.utils.vd_format(dev, extent_size) - -elif cmd == 'create': - - size = int(sys.argv[2]) - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Creating a virtual disk" - print "Size: %d" % size - print "Expiry time (seconds from now): %d" % expiry_time - - src = xenctl.utils.vd_create(size, expiry_time) - -elif cmd == 'enlarge': - - id = sys.argv[2] - - extra_size = int(sys.argv[3]) - - rc = xenctl.utils.vd_enlarge(id, extra_size) - -elif cmd == 'delete': - - id = sys.argv[2] - - print "Deleting a virtual disk with ID: " + id - - rc = xenctl.utils.vd_delete(id) - -elif cmd == 'import': - - file = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Allocate new virtual disk and populate from file : %s" % file - - print xenctl.utils.vd_read_from_file(file, expiry_time) - -elif cmd == 'export': - - id = sys.argv[2] - file = sys.argv[3] - - print "Dump contents of virtual disk to file : %s" % file - - rc = xenctl.utils.vd_cp_to_file(id, file ) - -elif cmd == 'setexpiry': - - id = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Refreshing a virtual disk" - print "Id: " + id - print "Expiry time (seconds from now [or 0]): " + str(expiry_time) - - rc = xenctl.utils.vd_refresh(id, expiry_time) - -elif cmd == 'list': - print 'ID Size(MB) Expiry' - - for vbd in xenctl.utils.vd_list(): - vbd['size_mb'] = vbd['size'] / 2048 - vbd['expiry'] = (vbd['expires'] and vbd['expiry_time']) or 'never' - print '%(vdisk_id)-4s %(size_mb)-12d %(expiry)s' % vbd - -elif cmd == 'freespace': - - print xenctl.utils.vd_freespace() - -elif cmd == 'undelete': - - id = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - if xenctl.utils.vd_undelete(id, expiry_time): - print "Undelete operation failed for virtual disk: " + id - else: - print "Undelete operation succeeded for virtual disk: " + id - -else: - usage() - sys.exit(-1) - - -if src != '': - print "Returned virtual disk id is : %s" % src - -if rc != '': - print "return code %d" % rc - - - diff --git a/tools/examples/xm_dom_control.py b/tools/examples/xm_dom_control.py index da30244652..8d1885be88 100755 --- a/tools/examples/xm_dom_control.py +++ b/tools/examples/xm_dom_control.py @@ -8,6 +8,7 @@ import os import os.path import signal +from xenmgr import sxp from xenmgr.XendClient import server # usage: xc_dom_control [command] <params> @@ -131,13 +132,13 @@ elif cmd == 'list': for dom in server.xend_domains(): info = server.xend_domain(dom) d = {} - d['dom'] = dom - d['name'] = sxp.get_child_value(info, 'name', '??') - d['mem'] = int(sxp.get_child_value(info, 'memory', '0')) - d['cpu'] = int(sxp.get_child_value(info, 'cpu', '0')) - d['state'] = sxp.get_child_value(info, 'state', '??') - d['cpu_time'] = sxp.get_child_value(info, 'cpu_time', '0') - print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)8d" + d['dom'] = int(dom) + d['name'] = sxp.child_value(info, 'name', '??') + d['mem'] = int(sxp.child_value(info, 'memory', '0')) + d['cpu'] = int(sxp.child_value(info, 'cpu', '0')) + d['state'] = sxp.child_value(info, 'state', '??') + d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0') + print ("%(dom)-4d %(name)-16s %(mem)7d %(cpu)3d %(state)5s %(cpu_time)8.2f" % d) elif cmd == 'unwatch': diff --git a/tools/examples/xm_vd_tool.py b/tools/examples/xm_vd_tool.py deleted file mode 100755 index 7207a594d3..0000000000 --- a/tools/examples/xm_vd_tool.py +++ /dev/null @@ -1,157 +0,0 @@ -#!/usr/bin/env python - -import sys -import re -import string - -from xenctl import vdisk - -def usage(): - - print >>sys.stderr,""" -Usage: %s command <params> - - initialise [dev] [[ext_size]] - init. a physcial partition to store vd's - create [size] [[expiry]] - allocate a vd of specified size (and expiry) - enlarge [vdid] [extra_size] - enlarge a specified vd by some amount - delete [vdid] - delete a vd - import [filename] [[expiry]] - create a vd and populate w/ image from file - export [vdid] [filename] - copy vd's contents to a file - setexpiry [vdid] [[expiry]] - update the expiry time for a vd - list - list all the unexpired virtual disks - undelete [vdid] [[expiry]] - attempts to recover an expired vd - freespace - print out the amount of space in free pool - -notes: - vdid - the virtual disk's identity string - size - measured in MB - expiry - is the expiry time of the virtual disk in seconds from now - (0 = don't expire) - device - physical partition to 'format' to hold vd's. e.g. hda4 - ext_size - extent size (default 64MB) -""" % sys.argv[0] - -if len(sys.argv) < 2: - usage() - sys.exit(-1) - -rc='' -src='' -expiry_time = 0 -cmd = sys.argv[1] - -if cmd == 'initialise': - - dev = sys.argv[2] - - if len(sys.argv) > 3: - extent_size = int(sys.argv[3]) - else: - print """No extent size specified - using default size of 64MB""" - extent_size = 64 - - print "Formatting for virtual disks" - print "Device: " + dev - print "Extent size: " + str(extent_size) + "MB" - - rc = vdisk.vd_format(dev, extent_size) - -elif cmd == 'create': - - size = int(sys.argv[2]) - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Creating a virtual disk" - print "Size: %d" % size - print "Expiry time (seconds from now): %d" % expiry_time - - src = vdisk.vd_create(size, expiry_time) - -elif cmd == 'enlarge': - - id = sys.argv[2] - - extra_size = int(sys.argv[3]) - - rc = vdisk.vd_enlarge(id, extra_size) - -elif cmd == 'delete': - - id = sys.argv[2] - - print "Deleting a virtual disk with ID: " + id - - rc = vdisk.vd_delete(id) - -elif cmd == 'import': - - file = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Allocate new virtual disk and populate from file : %s" % file - - print vdisk.vd_read_from_file(file, expiry_time) - -elif cmd == 'export': - - id = sys.argv[2] - file = sys.argv[3] - - print "Dump contents of virtual disk to file : %s" % file - - rc = vdisk.vd_cp_to_file(id, file ) - -elif cmd == 'setexpiry': - - id = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - print "Refreshing a virtual disk" - print "Id: " + id - print "Expiry time (seconds from now [or 0]): " + str(expiry_time) - - rc = vdisk.vd_refresh(id, expiry_time) - -elif cmd == 'list': - print 'ID Size(MB) Expiry' - - for vbd in vdisk.vd_list(): - vbd['size_mb'] = vbd['size'] / vdisk.VBD_SECTORS_PER_MB - vbd['expiry'] = (vbd['expires'] and vbd['expiry_time']) or 'never' - print '%(vdisk_id)-4s %(size_mb)-12d %(expiry)s' % vbd - -elif cmd == 'freespace': - - print vdisk.vd_freespace() - -elif cmd == 'undelete': - - id = sys.argv[2] - - if len(sys.argv) > 3: - expiry_time = int(sys.argv[3]) - - if vdisk.vd_undelete(id, expiry_time): - print "Undelete operation failed for virtual disk: " + id - else: - print "Undelete operation succeeded for virtual disk: " + id - -else: - usage() - sys.exit(-1) - - -if src != '': - print "Returned virtual disk id is : %s" % src - -if rc != '': - print "return code %d" % rc - - - 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/xenctl/lib/vdisk.py b/tools/xenctl/lib/vdisk.py deleted file mode 100644 index 6c4d144f9f..0000000000 --- a/tools/xenctl/lib/vdisk.py +++ /dev/null @@ -1,944 +0,0 @@ -import os -import re -import socket -import string -import sys -import tempfile -import struct - -##### Module variables - -"""Location of the Virtual Disk management database. - defaults to /var/db/xen_vdisks.sqlite -""" -VD_DB_FILE = "/var/db/xen_vdisks.sqlite" - -"""VBD expertise level - determines the strictness of the sanity checking. - This mode determines the level of complaints when disk sharing occurs - through the current VBD mappings. - 0 - only allow shared mappings if both domains have r/o access (always OK) - 1 - also allow sharing with one dom r/w and the other r/o - 2 - allow sharing with both doms r/w -""" -VBD_SAFETY_RR = 0 -VBD_SAFETY_RW = 1 -VBD_SAFETY_WW = 2 - -VBD_SECTORS_PER_MB = 2048 - -##### Module initialisation - -try: - # try to import sqlite (not everyone will have it installed) - import sqlite -except ImportError: - # on failure, just catch the error, don't do anything - pass - - - -##### VBD-related Functions - -def blkdev_name_to_number(name): - """Take the given textual block-device name (e.g., '/dev/sda1', - 'hda') and return the device number used by the OS. """ - - if not re.match( '/dev/', name ): - name = '/dev/' + name - - return os.stat(name).st_rdev - -# lookup_blkdev_partn_info( '/dev/sda3' ) -def lookup_raw_partn(partition): - """Take the given block-device name (e.g., '/dev/sda1', 'hda') - and return a dictionary { device, start_sector, - nr_sectors, type } - device: Device number of the given partition - start_sector: Index of first sector of the partition - nr_sectors: Number of sectors comprising this partition - type: 'Disk' or identifying name for partition type - """ - - if not re.match( '/dev/', partition ): - partition = '/dev/' + partition - - drive = re.split( '[0-9]', partition )[0] - - if drive == partition: - fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' ) - line = readline(fd) - if line: - return [ { 'device' : blkdev_name_to_number(drive), - 'start_sector' : long(0), - 'nr_sectors' : long(line) * 2, - 'type' : 'Disk' } ] - return None - - # determine position on disk - fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' ) - - #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012'] - lines = readlines(fd) - for line in lines: - m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' + - 'size=\s*([0-9]+), Id=\s*(\S+).*$', line) - if m: - return [ { 'device' : blkdev_name_to_number(drive), - 'start_sector' : long(m.group(1)), - 'nr_sectors' : long(m.group(2)), - 'type' : m.group(3) } ] - - return None - -def lookup_disk_uname( uname ): - """Lookup a list of segments for either a physical or a virtual device. - uname [string]: name of the device in the format \'vd:id\' for a virtual - disk, or \'phy:dev\' for a physical device - returns [list of dicts]: list of extents that make up the named device - """ - ( type, d_name ) = string.split( uname, ':' ) - - if type == "phy": - segments = lookup_raw_partn( d_name ) - elif type == "vd": - segments = vd_lookup( d_name ) - - return segments - - -##### VD Management-related functions - -##### By Mark Williamson, <mark.a.williamson@intel.com> -##### (C) Intel Research Cambridge - -# TODO: -# -# Plenty of room for enhancement to this functionality (contributions -# welcome - and then you get to have your name in the source ;-)... -# -# vd_unformat() : want facilities to unallocate virtual disk -# partitions, possibly migrating virtual disks of them, with checks to see if -# it's safe and options to force it anyway -# -# vd_create() : should have an optional argument specifying a physical -# disk preference - useful to allocate for guest doms to do RAID -# -# vd_undelete() : add ability to "best effort" undelete as much of a -# vdisk as is left in the case that some of it has already been -# reallocated. Some people might still be able to recover some of -# their data this way, even if some of the disk has disappeared. -# -# It'd be nice if we could wipe virtual disks for security purposes - -# should be easy to do this using dev if=/dev/{zero,random} on each -# extent in turn. There could be another optional flag to vd_create -# in order to allow this. -# -# Error codes could be more expressive - i.e. actually tell why the -# error occurred rather than "it broke". Currently the code avoids -# using exceptions to make control scripting simpler and more -# accessible to beginners - therefore probably should just use more -# return codes. -# -# Enhancements / additions to the example scripts are also welcome: -# some people will interact with this code mostly through those -# scripts. -# -# More documentation of how this stuff should be used is always nice - -# if you have a novel configuration that you feel isn't discussed -# enough in the HOWTO (which is currently a work in progress), feel -# free to contribute a walkthrough, or something more substantial. -# - - -def __vd_no_database(): - """Called when no database found - exits with an error - """ - print >> sys.stderr, "ERROR: Could not locate the database file at " + VD_DB_FILE - sys.exit(1) - -def readlines(fd): - """Version of readlines safe against EINTR. - """ - import errno - - lines = [] - while 1: - try: - line = fd.readline() - except IOError, ex: - if ex.errno == errno.EINTR: - continue - else: - raise - if line == '': break - lines.append(line) - return lines - -def readline(fd): - """Version of readline safe against EINTR. - """ - while 1: - try: - return fd.readline() - except IOError, ex: - if ex.errno == errno.EINTR: - continue - else: - raise - - -def vd_format(partition, extent_size_mb): - """Format a partition or drive for use a virtual disk storage. - partition [string]: device file representing the partition - extent_size_mb [string]: extent size in megabytes to use on this disk - """ - - if not os.path.isfile(VD_DB_FILE): - vd_init_db(VD_DB_FILE) - - if not re.match( '/dev/', partition ): - partition = '/dev/' + partition - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("select * from vdisk_part where partition = \'" - + partition + "\'") - row = cu.fetchone() - - extent_size = extent_size_mb * VBD_SECTORS_PER_MB # convert megabytes to sectors - - if not row: - part_info = lookup_raw_partn(partition)[0] - - cu.execute("INSERT INTO vdisk_part(partition, part_id, extent_size) " + - "VALUES ( \'" + partition + "\', " - + str(blkdev_name_to_number(partition)) - + ", " + str(extent_size) + ")") - - - cu.execute("SELECT max(vdisk_extent_no) FROM vdisk_extents " - + "WHERE vdisk_id = 0") - - max_id, = cu.fetchone() - - if max_id != None: - new_id = max_id + 1 - else: - new_id = 0 - - num_extents = part_info['nr_sectors'] / extent_size - - for i in range(num_extents): - sql ="""INSERT INTO vdisk_extents(vdisk_extent_no, vdisk_id, - part_id, part_extent_no) - VALUES ("""+ str(new_id + i) + ", 0, "\ - + str(blkdev_name_to_number(partition))\ - + ", " + str(num_extents - (i + 1)) + ")" - cu.execute(sql) - - cx.commit() - cx.close() - return 0 - - -def vd_create(size_mb, expiry): - """Create a new virtual disk. - size_mb [int]: size in megabytes for the new virtual disk - expiry [int]: expiry time in seconds from now - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - size = size_mb * VBD_SECTORS_PER_MB - - cu.execute("SELECT max(vdisk_id) FROM vdisks") - max_id, = cu.fetchone() - new_id = int(max_id) + 1 - - # fetch a list of extents from the expired disks, along with information - # about their size - cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no, - vdisk_extents.part_id, extent_size - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - WHERE expires AND expiry_time <= datetime('now') - ORDER BY expiry_time ASC, vdisk_extent_no DESC - """) # aims to reuse the last extents - # from the longest-expired disks first - - allocated = 0 - - if expiry: - expiry_ts = "datetime('now', '" + str(expiry) + " seconds')" - expires = 1 - else: - expiry_ts = "NULL" - expires = 0 - - # we'll use this to build the SQL statement we want - building_sql = "INSERT INTO vdisks(vdisk_id, size, expires, expiry_time)" \ - +" VALUES ("+str(new_id)+", "+str(size)+ ", " \ - + str(expires) + ", " + expiry_ts + "); " - - counter = 0 - - while allocated < size: - row = cu.fetchone() - if not row: - print "ran out of space, having allocated %d meg of %d" % (allocated, size) - cx.close() - return -1 - - - (vdisk_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row - allocated += extent_size - building_sql += "UPDATE vdisk_extents SET vdisk_id = " + str(new_id) \ - + ", " + "vdisk_extent_no = " + str(counter) \ - + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \ - + " AND vdisk_id = " + str(vdisk_id) + "; " - - counter += 1 - - - # this will execute the SQL query we build to store details of the new - # virtual disk and allocate space to it print building_sql - cu.execute(building_sql) - - cx.commit() - cx.close() - return str(new_id) - - -def vd_lookup(id): - """Lookup a Virtual Disk by ID. - id [string]: a virtual disk identifier - Returns [list of dicts]: a list of extents as dicts, containing fields: - device : Linux device number of host disk - start_sector : within the device - nr_sectors : size of this extent - type : set to \'VD Extent\' - - part_device : Linux device no of host partition - part_start_sector : within the partition - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("-- types int") - cu.execute("""SELECT COUNT(*) - FROM vdisks - WHERE (expiry_time > datetime('now') OR NOT expires) - AND vdisk_id = """ + id) - count, = cu.fetchone() - - if not count: - cx.close() - return None - - cu.execute("SELECT size from vdisks WHERE vdisk_id = " + id) - real_size, = cu.fetchone() - - # This query tells PySQLite how to convert the data returned from the - # following query - the use of the multiplication confuses it otherwise ;-) - # This row is significant to PySQLite but is syntactically an SQL comment. - - cu.execute("-- types str, int, int, int") - - # This SQL statement is designed so that when the results are fetched they - # will be in the right format to return immediately. - cu.execute("""SELECT partition, vdisk_part.part_id, - round(part_extent_no * extent_size) as start, - extent_size - - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - - WHERE vdisk_extents.vdisk_id = """ + id - + " ORDER BY vdisk_extents.vdisk_extent_no ASC" - ) - - extent_tuples = cu.fetchall() - - # use this function to map the results from the database into a dict - # list of extents, for consistency with the rest of the code - def transform ((partition, part_device, part_offset, nr_sectors)): - return { - # the disk device this extent is on - for passing to Xen - 'device' : lookup_raw_partn(partition)[0]['device'], - # the offset of this extent within the disk - for passing to Xen - 'start_sector' : long(part_offset + lookup_raw_partn(partition)[0]['start_sector']), - # extent size, in sectors - 'nr_sectors' : nr_sectors, - # partition device this extent is on (useful to know for xenctl.utils fns) - 'part_device' : part_device, - # start sector within this partition (useful to know for xenctl.utils fns) - 'part_start_sector' : part_offset, - # type of this extent - handy to know - 'type' : 'VD Extent' } - - cx.commit() - cx.close() - - extent_dicts = map(transform, extent_tuples) - - # calculate the over-allocation in sectors (happens because - # we allocate whole extents) - allocated_size = 0 - for i in extent_dicts: - allocated_size += i['nr_sectors'] - - over_allocation = allocated_size - real_size - - # trim down the last extent's length so the resulting VBD will be the - # size requested, rather than being rounded up to the nearest extent - extent_dicts[len(extent_dicts) - 1]['nr_sectors'] -= over_allocation - - return extent_dicts - - -def vd_enlarge(vdisk_id, extra_size_mb): - """Create a new virtual disk. - vdisk_id [string] : ID of the virtual disk to enlarge - extra_size_mb [int]: size in megabytes to increase the allocation by - returns [int] : 0 on success, otherwise non-zero - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - extra_size = extra_size_mb * VBD_SECTORS_PER_MB - - cu.execute("-- types int") - cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id - + " AND (expiry_time > datetime('now') OR NOT expires)") - count, = cu.fetchone() - - if not count: # no such vdisk - cx.close() - return -1 - - cu.execute("-- types int") - cu.execute("""SELECT SUM(extent_size) - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - WHERE vdisks.vdisk_id = """ + vdisk_id) - - real_size, = cu.fetchone() # get the true allocated size - - cu.execute("-- types int") - cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id) - - old_size, = cu.fetchone() - - - cu.execute("--- types int") - cu.execute("""SELECT MAX(vdisk_extent_no) - FROM vdisk_extents - WHERE vdisk_id = """ + vdisk_id) - - counter = cu.fetchone()[0] + 1 # this stores the extent numbers - - - # because of the extent-based allocation, the VD may already have more - # allocated space than they asked for. Find out how much we really - # need to add. - add_size = extra_size + old_size - real_size - - # fetch a list of extents from the expired disks, along with information - # about their size - cu.execute("""SELECT vdisks.vdisk_id, vdisk_extent_no, part_extent_no, - vdisk_extents.part_id, extent_size - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - WHERE expires AND expiry_time <= datetime('now') - ORDER BY expiry_time ASC, vdisk_extent_no DESC - """) # aims to reuse the last extents - # from the longest-expired disks first - - allocated = 0 - - building_sql = "UPDATE vdisks SET size = " + str(old_size + extra_size)\ - + " WHERE vdisk_id = " + vdisk_id + "; " - - while allocated < add_size: - row = cu.fetchone() - if not row: - cx.close() - return -1 - - (dead_vd_id, vdisk_extent_no, part_extent_no, part_id, extent_size) = row - allocated += extent_size - building_sql += "UPDATE vdisk_extents SET vdisk_id = " + vdisk_id \ - + ", " + "vdisk_extent_no = " + str(counter) \ - + " WHERE vdisk_extent_no = " + str(vdisk_extent_no) \ - + " AND vdisk_id = " + str(dead_vd_id) + "; " - - counter += 1 - - - # this will execute the SQL query we build to store details of the new - # virtual disk and allocate space to it print building_sql - cu.execute(building_sql) - - cx.commit() - cx.close() - return 0 - - -def vd_undelete(vdisk_id, expiry_time): - """Create a new virtual disk. - vdisk_id [int]: size in megabytes for the new virtual disk - expiry_time [int]: expiry time, in seconds from now - returns [int]: zero on success, non-zero on failure - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - if vdisk_id == '0': # undeleting vdisk 0 isn't sane! - return -1 - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("-- types int") - cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + vdisk_id) - count, = cu.fetchone() - - if not count: - cx.close() - return -1 - - cu.execute("-- types int") - cu.execute("""SELECT SUM(extent_size) - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - WHERE vdisks.vdisk_id = """ + vdisk_id) - - real_size, = cu.fetchone() # get the true allocated size - - - cu.execute("-- types int") - cu.execute("SELECT size FROM vdisks WHERE vdisk_id = " + vdisk_id) - - old_size, = cu.fetchone() - - if real_size < old_size: - cx.close() - return -1 - - if expiry_time == 0: - expires = '0' - else: - expires = '1' - - # this will execute the SQL query we build to store details of the new - # virtual disk and allocate space to it print building_sql - cu.execute("UPDATE vdisks SET expiry_time = datetime('now','" - + str(expiry_time) + " seconds'), expires = " + expires - + " WHERE vdisk_id = " + vdisk_id) - - cx.commit() - cx.close() - return 0 - - - - -def vd_list(): - """Lists all the virtual disks registered in the system. - returns [list of dicts] - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("""SELECT vdisk_id, size, expires, expiry_time - FROM vdisks - WHERE (NOT expires) OR expiry_time > datetime('now') - """) - - ret = cu.fetchall() - - cx.close() - - def makedicts((vdisk_id, size, expires, expiry_time)): - return { 'vdisk_id' : str(vdisk_id), 'size': size, - 'expires' : expires, 'expiry_time' : expiry_time } - - return map(makedicts, ret) - - -def vd_refresh(id, expiry): - """Change the expiry time of a virtual disk. - id [string] : a virtual disk identifier - expiry [int] : expiry time in seconds from now (0 = never expire) - returns [int]: zero on success, non-zero on failure - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("-- types int") - cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id - + " AND (expiry_time > datetime('now') OR NOT expires)") - count, = cu.fetchone() - - if not count: - cx.close() - return -1 - - if expiry: - expires = 1 - expiry_ts = "datetime('now', '" + str(expiry) + " seconds')" - else: - expires = 0 - expiry_ts = "NULL" - - cu.execute("UPDATE vdisks SET expires = " + str(expires) - + ", expiry_time = " + expiry_ts - + " WHERE (expiry_time > datetime('now') OR NOT expires)" - + " AND vdisk_id = " + id) - - cx.commit() - cx.close() - - return 0 - - -def vd_delete(id): - """Deletes a Virtual Disk, making its extents available for future VDs. - id [string] : identifier for the virtual disk to delete - returns [int] : 0 on success, -1 on failure (VD not found - or already deleted) - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("-- types int") - cu.execute("SELECT COUNT(*) FROM vdisks WHERE vdisk_id = " + id - + " AND (expiry_time > datetime('now') OR NOT expires)") - count, = cu.fetchone() - - if not count: - cx.close() - return -1 - - cu.execute("UPDATE vdisks SET expires = 1, expiry_time = datetime('now')" - + " WHERE vdisk_id = " + id) - - cx.commit() - cx.close() - - return 0 - - -def vd_freespace(): - """Returns the amount of free space available for new virtual disks, in MB - returns [int] : free space for VDs in MB - """ - - if not os.path.isfile(VD_DB_FILE): - __vd_no_database() - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("-- types int") - - cu.execute("""SELECT SUM(extent_size) - FROM vdisks NATURAL JOIN vdisk_extents - NATURAL JOIN vdisk_part - WHERE expiry_time <= datetime('now') AND expires""") - - sum, = cu.fetchone() - - cx.close() - - return sum / VBD_SECTORS_PER_MB - - -def vd_init_db(path): - """Initialise the VD SQLite database - path [string]: path to the SQLite database file - """ - - cx = sqlite.connect(path) - cu = cx.cursor() - - cu.execute( - """CREATE TABLE vdisk_extents - ( vdisk_extent_no INT, - vdisk_id INT, - part_id INT, - part_extent_no INT ) - """) - - cu.execute( - """CREATE TABLE vdisk_part - ( part_id INT, - partition VARCHAR, - extent_size INT ) - """) - - cu.execute( - """CREATE TABLE vdisks - ( vdisk_id INT, - size INT, - expires BOOLEAN, - expiry_time TIMESTAMP ) - """) - - - cu.execute( - """INSERT INTO vdisks ( vdisk_id, size, expires, expiry_time ) - VALUES ( 0, 0, 1, datetime('now') ) - """) - - cx.commit() - cx.close() - - VD_DB_FILE = path - - - -def vd_cp_to_file(vdisk_id,filename): - """Writes the contents of a specified vdisk out into a disk file, leaving - the original copy in the virtual disk pool.""" - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - extents = vd_lookup(vdisk_id) - - if not extents: - return -1 - - file_idx = 0 # index into source file, in sectors - - for i in extents: - cu.execute("""SELECT partition, extent_size FROM vdisk_part - WHERE part_id = """ + str(i['part_device'])) - - (partition, extent_size) = cu.fetchone() - - os.system("dd bs=1b if=" + partition + " of=" + filename - + " skip=" + str(i['part_start_sector']) - + " seek=" + str(file_idx) - + " count=" + str(i['nr_sectors']) - + " > /dev/null") - - file_idx += i['nr_sectors'] - - cx.close() - - return 0 # should return -1 if something breaks - - -def vd_mv_to_file(vdisk_id,filename): - """Writes a vdisk out into a disk file and frees the space originally - taken within the virtual disk pool. - vdisk_id [string]: ID of the vdisk to write out - filename [string]: file to write vdisk contents out to - returns [int]: zero on success, nonzero on failure - """ - - if vd_cp_to_file(vdisk_id,filename): - return -1 - - if vd_delete(vdisk_id): - return -1 - - return 0 - - -def vd_read_from_file(filename,expiry): - """Reads the contents of a file directly into a vdisk, which is - automatically allocated to fit. - filename [string]: file to read disk contents from - returns [string] : vdisk ID for the destination vdisk - """ - - size_bytes = os.stat(filename).st_size - - (size_mb,leftover) = divmod(size_bytes,1048580) # size in megabytes - if leftover > 0: size_mb += 1 # round up if not an exact number of MB - - vdisk_id = vd_create(size_mb, expiry) - - if vdisk_id < 0: - return -1 - - cx = sqlite.connect(VD_DB_FILE) - cu = cx.cursor() - - cu.execute("""SELECT partition, extent_size, part_extent_no - FROM vdisk_part NATURAL JOIN vdisk_extents - WHERE vdisk_id = """ + vdisk_id + """ - ORDER BY vdisk_extent_no ASC""") - - extents = cu.fetchall() - - size_sectors = size_mb * VBD_SECTORS_PER_MB # for feeding to dd - - file_idx = 0 # index into source file, in sectors - - def write_extent_to_vd((partition, extent_size, part_extent_no), - file_idx, filename): - """Write an extent out to disk and update file_idx""" - - os.system("dd bs=512 if=" + filename + " of=" + partition - + " skip=" + str(file_idx) - + " seek=" + str(part_extent_no * extent_size) - + " count=" + str(min(extent_size, size_sectors - file_idx)) - + " > /dev/null") - - return extent_size - - for i in extents: - file_idx += write_extent_to_vd(i, file_idx, filename) - - cx.close() - - return vdisk_id - - - - -def vd_extents_validate(new_extents, new_writeable, safety=VBD_SAFETY_RR): - """Validate the extents against the existing extents. - Complains if the list supplied clashes against the extents that - are already in use in the system. - new_extents [list of dicts]: list of new extents, as dicts - new_writeable [int]: 1 if they are to be writeable, 0 otherwise - returns [int]: either the expertise level of the mapping if it doesn't - exceed VBD_EXPERT_MODE or -1 if it does (error) - """ - - import Xc # this is only needed in this function - - xc = Xc.new() - - ##### Probe for explicitly created virtual disks and build a list - ##### of extents for comparison with the ones that are being added - - probe = xc.vbd_probe() - - old_extents = [] # this will hold a list of all existing extents and - # their writeable status, as a list of (device, - # start, size, writeable?) tuples - - for vbd in probe: - this_vbd_extents = xc.vbd_getextents(vbd['dom'],vbd['vbd']) - for vbd_ext in this_vbd_extents: - vbd_ext['writeable'] = vbd['writeable'] - old_extents.append(vbd_ext) - - ##### Now scan /proc/mounts for compile a list of extents corresponding to - ##### any devices mounted in DOM0. This list is added on to old_extents - - regexp = re.compile("/dev/(\S*) \S* \S* (..).*") - fd = open('/proc/mounts', "r") - - while True: - line = readline(fd) - if not line: # if we've run out of lines then stop reading - break - - m = regexp.match(line) - - # if the regexp didn't match then it's probably a line we don't - # care about - skip to next line - if not m: - continue - - # lookup the device - ext_list = lookup_raw_partn(m.group(1)) - - # if lookup failed, skip to next mounted device - if not ext_list: - continue - - # set a writeable flag as appropriate - for ext in ext_list: - ext['writeable'] = m.group(2) == 'rw' - - # now we've got here, the contents of ext_list are in a - # suitable format to be added onto the old_extents list, ready - # for checking against the new extents - - old_extents.extend(ext_list) - - fd.close() # close /proc/mounts - - ##### By this point, old_extents contains a list of extents, in - ##### dictionary format corresponding to every extent of physical - ##### disk that's either part of an explicitly created VBD, or is - ##### mounted under DOM0. We now check these extents against the - ##### proposed additions in new_extents, to see if a conflict will - ##### happen if they are added with write status new_writeable - - level = 0 # this'll accumulate the max warning level - - # Search for clashes between the new extents and the old ones - # Takes time O(len(new_extents) * len(old_extents)) - for new_ext in new_extents: - for old_ext in old_extents: - if(new_ext['device'] == old_ext['device']): - - new_ext_start = new_ext['start_sector'] - new_ext_end = new_ext_start + new_ext['nr_sectors'] - 1 - - old_ext_start = old_ext['start_sector'] - old_ext_end = old_ext_start + old_ext['nr_sectors'] - 1 - - if((old_ext_start <= new_ext_start <= old_ext_end) or - (old_ext_start <= new_ext_end <= old_ext_end)): - if (not old_ext['writeable']) and new_writeable: - level = max(1,level) - elif old_ext['writeable'] and (not new_writeable): - level = max(1,level) - elif old_ext['writeable'] and new_writeable: - level = max(2,level) - - - ##### level now holds the warning level incurred by the current - ##### VBD setup and we complain appropriately to the user - - - if level == 1: - print >> sys.stderr, """Warning: one or more hard disk extents - writeable by one domain are also readable by another.""" - elif level == 2: - print >> sys.stderr, """Warning: one or more hard disk extents are - writeable by two or more domains simultaneously.""" - - if level > safety: - print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed - at the current safety level (%d).""" % safety - level = -1 - - return level - diff --git a/tools/xenctl/setup.py b/tools/xenctl/setup.py index 1d67a843ef..03f3f1e25c 100644 --- a/tools/xenctl/setup.py +++ b/tools/xenctl/setup.py @@ -2,7 +2,7 @@ from distutils.core import setup, Extension import sys -modules = [ 'xenctl.console_client', 'xenctl.utils', 'xenctl.ip' , 'xenctl.vdisk' ] +modules = [ 'xenctl.console_client', 'xenctl.utils', 'xenctl.ip' ] # We need the 'tempfile' module from Python 2.3. We install this ourselves # if the installed Python is older than 2.3. 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/XendClient.py b/tools/xenmgr/lib/XendClient.py index feed16a7de..76f49f49bd 100644 --- a/tools/xenmgr/lib/XendClient.py +++ b/tools/xenmgr/lib/XendClient.py @@ -97,7 +97,7 @@ def xend_request(url, method, data=None): #hdr['Accept'] = 'text/html,text/plain' conn = httplib.HTTPConnection(ulocation) #conn.response_class = Foo - conn.set_debuglevel(1) + if DEBUG: conn.set_debuglevel(1) conn.request(method, upath, args, hdr) resp = conn.getresponse() if DEBUG: print resp.status, resp.reason @@ -118,10 +118,7 @@ def xend_request(url, method, data=None): # val = val[1] if isinstance(val, types.ListType) and sxp.name(val) == 'err': raise RuntimeError(val[1]) - if DEBUG: print '**val=' - #sxp.show(val); print - PrettyPrint.prettyprint(val) - if DEBUG: print '**' + if DEBUG: print '**val='; sxp.show(val); print return val def xend_get(url, args=None): @@ -334,7 +331,9 @@ def main(argv): if not fn.startswith('xend'): fn = 'xend_' + fn args = argv[2:] - getattr(server, fn)(*args) + val = getattr(server, fn)(*args) + PrettyPrint.prettyprint(val) + print if __name__ == "__main__": main(sys.argv) diff --git a/tools/xenmgr/lib/XendDomain.py b/tools/xenmgr/lib/XendDomain.py index 60e207da65..94afe3e87d 100644 --- a/tools/xenmgr/lib/XendDomain.py +++ b/tools/xenmgr/lib/XendDomain.py @@ -49,18 +49,28 @@ class XendDomain: def initial_refresh(self): """Refresh initial domain info from domain_db. """ + print "initial_refresh> db=", self.domain_db.values() domlist = xc.domain_getinfo() + print "doms=", domlist doms = {} for d in domlist: domid = str(d['dom']) doms[domid] = d for config in self.domain_db.values(): domid = int(sxp.child_value(config, 'id')) + print "dom=", domid, "config=", config if domid in doms: + print "dom=", domid, "new" self._new_domain(config) else: + print "dom=", domid, "del" self._delete_domain(domid) + print "doms:" + for d in self.domain.values(): print 'dom', d + print "refresh..." self.refresh() + print "doms:" + for d in self.domain.values(): print 'dom', d def sync(self): """Sync domain db to disk. @@ -132,7 +142,7 @@ class XendDomain: image = None newinfo = XendDomainInfo.XendDomainInfo( config, d['dom'], d['name'], d['mem_kb']/1024, image) - self._add_domain(id, newinfo) + self._add_domain(newinfo.id, newinfo) # Remove entries for domains that no longer exist. for d in self.domain.values(): dominfo = doms.get(d.id) @@ -152,7 +162,7 @@ class XendDomain: else: d = self.domain.get(id) if d: - d.update(dominfo) + d.update(dominfo[0]) def domain_ls(self): # List domains. @@ -319,18 +329,18 @@ class XendDomain: return v return None - def domain_vbd_add(self, dom, uname, dev, mode): - dom = int(dom) - vbd = vm.make_disk(dom, uname, dev, mode) - return vbd - - def domain_vbd_remove(self, dom, dev): - dom = int(dom) - vbd = xenctl.vdisk.blkdev_name_to_number(dev) - if vbd < 0: return vbd - err = xc.vbd_destroy(dom, vbd) - if err < 0: return err - return vbd +## def domain_vbd_add(self, dom, uname, dev, mode): +## dom = int(dom) +## vbd = vm.make_disk(dom, uname, dev, mode) +## return vbd + +## def domain_vbd_remove(self, dom, dev): +## dom = int(dom) +## vbd = xenctl.vdisk.blkdev_name_to_number(dev) +## if vbd < 0: return vbd +## err = xc.vbd_destroy(dom, vbd) +## if err < 0: return err +## return vbd def domain_shadow_control(self, dom, op): dom = int(dom) diff --git a/tools/xenmgr/lib/XendDomainInfo.py b/tools/xenmgr/lib/XendDomainInfo.py index efa56d7f32..2318df68fc 100644 --- a/tools/xenmgr/lib/XendDomainInfo.py +++ b/tools/xenmgr/lib/XendDomainInfo.py @@ -9,6 +9,8 @@ Author: Mike Wray <mike.wray@hpl.hp.com> """ +import string +import re import sys import os @@ -17,7 +19,6 @@ from twisted.internet import defer import Xc; xc = Xc.new() import xenctl.ip -import xenctl.vdisk import sxp @@ -27,6 +28,24 @@ xendConsole = XendConsole.instance() import server.SrvConsoleServer xend = server.SrvConsoleServer.instance() +def readlines(fd): + """Version of readlines safe against EINTR. + """ + import errno + + lines = [] + while 1: + try: + line = fd.readline() + except IOError, ex: + if ex.errno == errno.EINTR: + continue + else: + raise + if line == '': break + lines.append(line) + return lines + class VmError(ValueError): """Vm construction error.""" @@ -60,6 +79,7 @@ class XendDomainInfo: self.devices = {} self.configs = [] self.info = None + self.ipaddrs = [] #todo: state: running, suspended self.state = 'running' @@ -93,7 +113,7 @@ class XendDomainInfo: if self.info: run = (self.info['running'] and 'r') or '-' stop = (self.info['stopped'] and 's') or '-' - state = run + state + state = run + stop sxpr.append(['cpu', self.info['cpu']]) sxpr.append(['state', state]) sxpr.append(['cpu_time', self.info['cpu_time']/1e8]) @@ -127,6 +147,12 @@ class XendDomainInfo: return sxp.child_with_id(self.get_devices(type), id) def get_device_by_index(self, type, idx): + """Get the device with the given index. + + idx device index + + returns device or None + """ dl = self.get_devices(type) if 0 <= idx < len(dl): return dl[idx] @@ -163,48 +189,81 @@ class XendDomainInfo: print print "]" -def safety_level(sharing): - if sharing == 'rw': - return xenctl.vdisk.VBD_SAFETY_RW - if sharing == 'ww': - return xenctl.vdisk.VBD_SAFETY_WW - return xenctl.vdisk.VBD_SAFETY_RR - - -def make_disk_old(dom, uname, dev, mode, sharing): - writeable = ('w' in mode) - safety = safety_level(sharing) - vbd = xenctl.vdisk.blkdev_name_to_number(dev) - extents = xenctl.vdisk.lookup_disk_uname(uname) - if not extents: - raise VmError("vbd: Extents not found: uname=%s" % uname) - - # check that setting up this VBD won't violate the sharing - # allowed by the current VBD expertise level - if xenctl.vdisk.vd_extents_validate(extents, writeable, safety=safety) < 0: - raise VmError("vbd: Extents invalid: uname=%s" % uname) - - if xc.vbd_create(dom=dom, vbd=vbd, writeable=writeable): - raise VmError("vbd: Creating device failed: dom=%d uname=%s vbd=%d mode=%s" - % (dom, uname, vbdmode)) - - if xc.vbd_setextents(dom=dom, vbd=vbd, extents=extents): - raise VMError("vbd: Setting extents failed: dom=%d uname=%s vbd=%d" - % (dom, uname, vbd)) - return vbd +def blkdev_name_to_number(name): + """Take the given textual block-device name (e.g., '/dev/sda1', + 'hda') and return the device number used by the OS. """ + + if not re.match( '/dev/', name ): + name = '/dev/' + name + + return os.stat(name).st_rdev + +def lookup_raw_partn(partition): + """Take the given block-device name (e.g., '/dev/sda1', 'hda') + and return a dictionary { device, start_sector, + nr_sectors, type } + device: Device number of the given partition + start_sector: Index of first sector of the partition + nr_sectors: Number of sectors comprising this partition + type: 'Disk' or identifying name for partition type + """ + + if not re.match( '/dev/', partition ): + partition = '/dev/' + partition + + drive = re.split( '[0-9]', partition )[0] + + if drive == partition: + fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' ) + line = readline(fd) + if line: + return [ { 'device' : blkdev_name_to_number(drive), + 'start_sector' : long(0), + 'nr_sectors' : long(line) * 2, + 'type' : 'Disk' } ] + return None + + # determine position on disk + fd = os.popen( '/sbin/sfdisk -d ' + drive + ' 2>/dev/null' ) + + #['/dev/sda3 : start= 16948575, size=16836120, Id=83, bootable\012'] + lines = readlines(fd) + for line in lines: + m = re.search( '^' + partition + '\s*: start=\s*([0-9]+), ' + + 'size=\s*([0-9]+), Id=\s*(\S+).*$', line) + if m: + return [ { 'device' : blkdev_name_to_number(drive), + 'start_sector' : long(m.group(1)), + 'nr_sectors' : long(m.group(2)), + 'type' : m.group(3) } ] + + return None + +def lookup_disk_uname( uname ): + """Lookup a list of segments for a physical device. + uname [string]: name of the device in the format \'phy:dev\' for a physical device + returns [list of dicts]: list of extents that make up the named device + """ + ( type, d_name ) = string.split( uname, ':' ) + + if type == "phy": + segments = lookup_raw_partn( d_name ) + else: + segments = None + return segments def make_disk(dom, uname, dev, mode, sharing): """Create a virtual disk device for a domain. @returns Deferred """ - segments = xenctl.vdisk.lookup_disk_uname(uname) + segments = lookup_disk_uname(uname) if not segments: raise VmError("vbd: Segments not found: uname=%s" % uname) if len(segments) > 1: raise VmError("vbd: Multi-segment vdisk: uname=%s" % uname) segment = segments[0] - vdev = xenctl.vdisk.blkdev_name_to_number(dev) + vdev = blkdev_name_to_number(dev) ctrl = xend.blkif_create(dom) def fn(ctrl): @@ -212,13 +271,6 @@ def make_disk(dom, uname, dev, mode, sharing): ctrl.addCallback(fn) return ctrl -def make_vif_old(dom, vif, vmac, vnet): - return # todo: Not supported yet. - err = xc.vif_setinfo(dom=dom, vif=vif, vmac=vmac, vnet=vnet) - if err < 0: - raise VmError('vnet: Error %d setting vif mac dom=%d vif=%d vmac=%s vnet=%d' % - (err, dom, vif, vmac, vnet)) - def make_vif(dom, vif, vmac): """Create a virtual network device for a domain. @@ -230,8 +282,10 @@ def make_vif(dom, vif, vmac): return d def vif_up(iplist): - #todo: Need a better way. - # send an unsolicited ARP reply for all non link-local IPs + """send an unsolicited ARP reply for all non link-local IP addresses. + + iplist IP addresses + """ IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind' @@ -260,6 +314,18 @@ def vif_up(iplist): if not nlb: set_ip_nonlocal_bind(0) def xen_domain_create(config, ostype, name, memory, kernel, ramdisk, cmdline, vifs_n): + """Create a domain. Builds the image but does not configure it. + + config configuration + ostype OS type + name domain name + memory domain memory (MB) + kernel kernel image + ramdisk kernel ramdisk + cmdline kernel commandline + vifs_n number of network interfaces + returns vm + """ if not os.path.isfile(kernel): raise VmError('Kernel image does not exist: %s' % kernel) if ramdisk and not os.path.isfile(ramdisk): @@ -380,11 +446,22 @@ def vm_create(config): return deferred def vm_restore(src, config, progress=0): + """Restore a VM. + + src saved state to restore + config configuration + progress progress reporting flag + returns deferred + raises VmError for invalid configuration + """ ostype = "linux" #todo set from config restorefn = getattr(xc, "%s_restore" % ostype) dom = restorefn(state_file=src, progress=progress) if dom < 0: return dom deferred = dom_configure(dom, config) + def vifs_cb(val, vm): + vif_up(vm.ipaddrs) + deferred.addCallback(vifs_cb, vm) return deferred def dom_get(dom): @@ -394,6 +471,12 @@ def dom_get(dom): return None def dom_configure(dom, config): + """Configure a domain. + + dom domain id + config configuration + returns deferred + """ d = dom_get(dom) if not d: raise VMError("Domain not found: %d" % dom) @@ -610,6 +693,12 @@ def vm_dev_pci(vm, val, index): func = sxp.child_value(val, 'func') if not func: raise VMError('pci: Missing func') + try: + bus = int(bus, 16) + dev = int(dev, 16) + func = int(func, 16) + except: + raise VMError('pci: invalid parameter') rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, func=func, enable=1) if rc < 0: #todo non-fatal @@ -628,6 +717,7 @@ def vm_field_vfr(vm, config, val, index): # Get the rules and add them. # (vfr (vif (id foo) (ip x.x.x.x)) ... ) list = sxp.children(val, 'vif') + ipaddrs = [] for v in list: id = sxp.child_value(v, 'id') if id is None: @@ -640,9 +730,11 @@ def vm_field_vfr(vm, config, val, index): ip = sxp.child_value(v, 'ip') if not ip: raise VmError('vfr: missing ip address') + ipaddrs.append(ip); #Don't do this in new i/o model. #print 'vm_field_vfr> add rule', 'dom=', vm.dom, 'vif=', vif, 'ip=', ip #xenctl.ip.setup_vfr_rules_for_vif(vm.dom, vif, ip) + vm.ipaddrs = ipaddrs def vnet_bridge(vnet, vmac, dom, idx): """Add the device for the vif to the bridge for its vnet. diff --git a/tools/xenmgr/lib/XendNode.py b/tools/xenmgr/lib/XendNode.py index 9be3d642bc..d293da8e77 100644 --- a/tools/xenmgr/lib/XendNode.py +++ b/tools/xenmgr/lib/XendNode.py @@ -7,15 +7,9 @@ """ +import os import Xc -class XendNodeInfo: - """Node information record. - """ - - def __init__(self): - pass - class XendNode: def __init__(self): @@ -45,6 +39,28 @@ class XendNode: #ret = self.xc.rrobin_global_set(slice) return ret + def info(self): + return self.nodeinfo() + self.physinfo() + + def nodeinfo(self): + (sys, host, rel, ver, mch) = os.uname() + return [['system', sys], + ['host', host], + ['release', rel], + ['version', ver], + ['machine', mch]] + + def physinfo(self): + pinfo = self.xc.physinfo() + info = [['cores', pinfo['cores']], + ['hyperthreads_per_core', pinfo['ht_per_core']], + ['cpu_mhz', pinfo['cpu_khz']/1000], + ['memory', pinfo['total_pages']/256], + ['free_memory', pinfo['free_pages']/256]] + return info + + + def instance(): global inst try: diff --git a/tools/xenmgr/lib/XendVdisk.py b/tools/xenmgr/lib/XendVdisk.py deleted file mode 100644 index 241d29be87..0000000000 --- a/tools/xenmgr/lib/XendVdisk.py +++ /dev/null @@ -1,111 +0,0 @@ -# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> - -"""Handler for vdisk operations. - -""" - -import os -import os.path - -from xenctl import vdisk - -import sxp - -class XendVdiskInfo: - - def __init__(self, info): - self.info = info - self.id = info['vdisk_id'] - - def __str__(self): - return ("vdisk id=%(vdisk_id)s size=%(size)d expires=%(expires)d expiry_time=%(expiry_time)d" - % self.info) - - def sxpr(self): - val = ['vdisk'] - for (k,v) in self.info.items(): - val.append([k, str(v)]) - return val - -class XendVdisk: - """Index of all vdisks. Singleton. - """ - - dbpath = "vdisk" - - def __init__(self): - # Table of vdisk info indexed by vdisk id. - self.vdisk = {} - if not os.path.isfile(vdisk.VD_DB_FILE): - vdisk.vd_init_db(vdisk.VD_DB_FILE) - self.vdisk_refresh() - - def vdisk_refresh(self): - # vdisk = {vdisk_id, size, expires, expiry_time} - try: - vdisks = vdisk.vd_list() - except: - vdisks = [] - for vdisk in vdisks: - vdiskinfo = XendVdiskInfo(vdisk) - self.vdisk[vdiskinfo.id] = vdiskinfo - - def vdisk_ls(self): - """List all vdisk ids. - """ - return self.vdisk.keys() - - def vdisks(self): - return self.vdisk.values() - - def vdisk_get(self, id): - """Get a vdisk. - - id vdisk id - """ - return self.vdisk.get(id) - - def vdisk_create(self, info): - """Create a vdisk. - - info config - """ - # Need to configure for real. - # vdisk.vd_create(size, expiry) - - def vdisk_configure(self, info): - """Configure a vdisk. - id vdisk id - info config - """ - # Need to configure for real. - # Make bigger: vdisk.vd_enlarge(id, extra_size) - # Update expiry time: vdisk.vd_refresh(id, expiry) - # Try to recover an expired vdisk : vdisk.vd_undelete(id, expiry) - - - def vdisk_delete(self, id): - """Delete a vdisk. - - id vdisk id - """ - # Need to delete vdisk for real. What if fails? - del self.vdisk[id] - vdisk.vd_delete(id) - - # def vdisk_copy: copy contents to file, vdisk still exists - # def vdisk_export: copy contents to file then delete the vdisk - # def vdisk_import: create a vdisk from a file - # def vdisk_space: space left for new vdisks - - # def vdisk_recover: recover an expired vdisk - - # def vdisk_init_partition: setup a physical partition for vdisks - -def instance(): - global inst - try: - inst - except: - inst = XendVdisk() - return inst diff --git a/tools/xenmgr/lib/server/SrvNode.py b/tools/xenmgr/lib/server/SrvNode.py index 3c6168e337..d4411d5e5d 100644 --- a/tools/xenmgr/lib/server/SrvNode.py +++ b/tools/xenmgr/lib/server/SrvNode.py @@ -45,15 +45,10 @@ class SrvNode(SrvDir): self.print_path(req) req.write('<ul>') for d in self.info(): - req.write('<li> %10s: %s' % (d[0], d[1])) + req.write('<li> %10s: %s' % (d[0], str(d[1]))) req.write('</ul>') req.write('</body></html>') return '' def info(self): - (sys, host, rel, ver, mch) = os.uname() - return [['system', sys], - ['host', host], - ['release', rel], - ['version', ver], - ['machine', mch]] + return self.xn.info() diff --git a/tools/xenmgr/lib/server/SrvRoot.py b/tools/xenmgr/lib/server/SrvRoot.py index b002d2cf76..6256d83a4b 100644 --- a/tools/xenmgr/lib/server/SrvRoot.py +++ b/tools/xenmgr/lib/server/SrvRoot.py @@ -17,7 +17,6 @@ class SrvRoot(SrvDir): ('domain', 'SrvDomainDir' ), ('console', 'SrvConsoleDir' ), ('event', 'SrvEventDir' ), - ('vdisk', 'SrvVdiskDir' ), ('device', 'SrvDeviceDir' ), ('vnet', 'SrvVnetDir' ), ] diff --git a/tools/xenmgr/lib/server/SrvVdisk.py b/tools/xenmgr/lib/server/SrvVdisk.py deleted file mode 100644 index 4200b9b0e5..0000000000 --- a/tools/xenmgr/lib/server/SrvVdisk.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> - -from xenmgr import XendVdisk -from SrvVdiskDir import SrvVdiskDir - -class SrvVdisk(SrvDir): - """A virtual disk. - """ - - def __init__(self): - SrvDir.__init__(self) - self.xvdisk = XendVdisk.instance() diff --git a/tools/xenmgr/lib/server/SrvVdiskDir.py b/tools/xenmgr/lib/server/SrvVdiskDir.py deleted file mode 100644 index 67d0955259..0000000000 --- a/tools/xenmgr/lib/server/SrvVdiskDir.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2004 Mike Wray <mike.wray@hp.com> - -from xenmgr import XendVdisk -from SrvDir import SrvDir - -class SrvVdiskDir(SrvDir): - """Virtual disk directory. - """ - - def __init__(self): - SrvDir.__init__(self) - #self.xvdisk = XendVdisk.instance() - - def vdisk(self, x): - val = None - try: - dom = self.xvdisk.vdisk_get(x) - val = SrvVdisk(dom) - except KeyError: - pass - return val - - def get(self, x): - v = SrvDir.get(self, x) - if v is not None: - return v - v = self.vdisk(x) - return v diff --git a/tools/xenmgr/lib/xm/__init__.py b/tools/xenmgr/lib/xm/__init__.py new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tools/xenmgr/lib/xm/__init__.py diff --git a/tools/xenmgr/lib/xm/create.py b/tools/xenmgr/lib/xm/create.py new file mode 100644 index 0000000000..863227ad4c --- /dev/null +++ b/tools/xenmgr/lib/xm/create.py @@ -0,0 +1,296 @@ +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 variables before loading defaults, e.g. '-D vmid=3;ip=1.2.3.4' + to set vmid and ip.""") + +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('pci', val='BUS,DEV,FUNC', + fn=append_value, default=[], + use="""Add a PCI device to a domain.""") + +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=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_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(',') + print 'disk', v, d + if len(d) != 3: + opts.err('Invalid disk specifier: ' + v) + disk.append(d) + opts.disk = disk + +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(opts, 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) + if opts.dryrun: + PrettyPrint.prettyprint(config) + else: + 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..4c70717521 --- /dev/null +++ b/tools/xenmgr/lib/xm/main.py @@ -0,0 +1,224 @@ +#!/usr/bin/python +import string +import sys + +from xenmgr import sxp +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, help, 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 args[0], "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 args[0], "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_' + args[0]); return + doms = server.xend_domains() + print 'Dom Name Mem(MB) CPU State Time(s)' + for dom in doms: + info = server.xend_domain(dom) + d = {} + d['dom'] = int(dom) + d['name'] = sxp.child_value(info, 'name', '??') + d['mem'] = int(sxp.child_value(info, 'memory', '0')) + d['cpu'] = int(sxp.child_value(info, 'cpu', '0')) + d['state'] = sxp.child_value(info, 'state', '??') + d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0')) + print ("%(dom)-4d %(name)-16s %(mem)4d %(cpu)3d %(state)5s %(cpu_time)10.2f" % d) + + def xm_halt(self, help, args): + """Terminate a domain immediately.""" + if help: + print args[0], '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 args[0], '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 args[0], '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 args[0],'DOM CPU' + print '\nPin domain DOM to cpu CPU.' + return + if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:3]) + server.xend_domain_pincpu(*v) + + def xm_vif_stats(self, help, args): + """Get stats for a virtual interface.""" + if help: + print args[0], 'DOM VIF' + print '\nGet stats for interface VIF on domain DOM.' + return + if len(args) != 3: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:3]) + print server.xend_domain_vif_stats(*v) + + def xm_vif_rate(self, help, args): + """Set or get vif rate params.""" + if help: + print args[0], "DOM VIF [BYTES USECS]" + print '\nSet or get rate controls for interface VIF on domain DOM.' + return + n = len(args) + if n == 3: + v = map(int, args[1:n]) + print server.xend_domain_vif_scheduler_get(*v) + elif n == 5: + v = map(int, args[1:n]) + server.xend_domain_vif_scheduler_set(*v) + else: + self.err("%s: Invalid argument(s)" % args[0]) + + def xm_bvt(self, help, args): + """Set BVT scheduler parameters.""" + if help: + print args[0], "DOM MCUADV WARP WARPL WARPU" + print '\nSet Borrowed Virtual Time scheduler parameters.' + return + if len(args) != 6: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:6]) + server.xend_domain_cpu_bvt_set(*v) + + def xm_bvtslice(self, help, args): + """Set the BVT scheduler slice.""" + if help: + print args[0], 'SLICE' + print '\nSet Borrowed Virtual Time scheduler slice.' + return + if len(args) < 2: self.err('%s: Missing slice' % args[0]) + server.xend_node_cpu_bvt_slice_set(slice) + + def xm_atropos(self, help, args): + """Set atropos parameters.""" + if help: + print args[0], "DOM PERIOD SLICE LATENCY XTRATIME" + print "\nSet atropos parameters." + return + if len(args) != 5: self.err("%s: Invalid argument(s)" % args[0]) + v = map(int, args[1:5]) + server.xend_domain_cpu_atropos_set(*v) + + def xm_rrobin(self, help, args): + """Set round robin slice.""" + if help: + print args[0], "SLICE" + print "\nSet round robin scheduler slice." + return + if len(args) != 2: self.err("%s: Invalid argument(s)" % args[0]) + slice = int(args[1]) + server.xend_node_rrobin_set(slice) + + 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[1:]: + 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..7b32daeae2 --- /dev/null +++ b/tools/xenmgr/lib/xm/opts.py @@ -0,0 +1,199 @@ +from getopt import getopt +import os +import os.path +import sys +import types + +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 + + def specified(self): + return self.specified_opt + +class Opts: + def __init__(self, use=None): + self._usage = use + self._options = [] + self._options_map = {} + self._argv = [] + self._vals = {} + self._globals = {} + self._locals = {} + + def opt(self, name, **args): + x = Opt(self, name, **args) + self._options.append(x) + self._options_map[name] = x + return x + + def getopt(self, name): + return self._options_map.get(name) + + def specified(self, name): + opt = self.getopt(name) + return opt and opt.specified() + + 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): + for x in [ '' ] + self.path.split(':'): + if x: + p = os.path.join(x, self.defaults) + else: + p = self.defaults + if os.path.exists(p): + self.load(p) + break + else: + self.err("Cannot open defaults file %s" % self.defaults) + + def load(self, defaults): + self._globals['sys'] = sys + self._globals['config_file'] = defaults + execfile(defaults, self._globals, self._locals) + vtypes = [ types.StringType, + types.ListType, + types.IntType, + types.FloatType + ] + for (k, v) in self._locals.items(): + if self.specified(k): continue + if not(type(v) in vtypes): continue + print 'SET ', k, v + setattr(self, k, v) + +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..805af2929d --- /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.opts import * + +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/netfix b/tools/xenmgr/netfix index fcde6292cb..70bc1094bd 100644 --- a/tools/xenmgr/netfix +++ b/tools/xenmgr/netfix @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- mode: python; -*- # Copyright (C) 2004 Mike Wray <mike.wray@hp.com> #============================================================================ # Move the IP address from eth0 onto the Xen bridge (nbe-br). diff --git a/tools/xenmgr/setup.py b/tools/xenmgr/setup.py index b08cf29c31..b7f76139be 100644 --- a/tools/xenmgr/setup.py +++ b/tools/xenmgr/setup.py @@ -9,6 +9,6 @@ setup(name = PACKAGE, description = 'Xen Management API', author = 'Mike Wray', author_email = 'mike.wray@hp.com', - packages = [ PACKAGE, PACKAGE + '.server' ], + packages = [ PACKAGE, PACKAGE + '.server', PACKAGE + '.xm' ], package_dir = { PACKAGE: 'lib' }, ) diff --git a/tools/xenmgr/xend b/tools/xenmgr/xend index f2355f1e51..e575eeccd5 100644 --- a/tools/xenmgr/xend +++ b/tools/xenmgr/xend @@ -1,4 +1,5 @@ #!/usr/bin/python +# -*- mode: python; -*- # Copyright (C) 2004 Mike Wray <mike.wray@hp.com> """Xen management daemon. Lives in /usr/sbin. 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) |