aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/examples/Makefile2
-rwxr-xr-xtools/examples/xc_vd_tool.py153
-rwxr-xr-xtools/examples/xm_dom_control.py15
-rwxr-xr-xtools/examples/xm_vd_tool.py157
-rw-r--r--tools/examples/xmdefaults96
-rw-r--r--tools/xenctl/lib/vdisk.py944
-rw-r--r--tools/xenctl/setup.py2
-rw-r--r--tools/xenmgr/Makefile2
-rw-r--r--tools/xenmgr/lib/XendClient.py11
-rw-r--r--tools/xenmgr/lib/XendDomain.py38
-rw-r--r--tools/xenmgr/lib/XendDomainInfo.py176
-rw-r--r--tools/xenmgr/lib/XendNode.py30
-rw-r--r--tools/xenmgr/lib/XendVdisk.py111
-rw-r--r--tools/xenmgr/lib/server/SrvNode.py9
-rw-r--r--tools/xenmgr/lib/server/SrvRoot.py1
-rw-r--r--tools/xenmgr/lib/server/SrvVdisk.py12
-rw-r--r--tools/xenmgr/lib/server/SrvVdiskDir.py28
-rw-r--r--tools/xenmgr/lib/xm/__init__.py0
-rw-r--r--tools/xenmgr/lib/xm/create.py296
-rw-r--r--tools/xenmgr/lib/xm/main.py224
-rw-r--r--tools/xenmgr/lib/xm/opts.py199
-rw-r--r--tools/xenmgr/lib/xm/shutdown.py65
-rw-r--r--tools/xenmgr/netfix1
-rw-r--r--tools/xenmgr/setup.py2
-rw-r--r--tools/xenmgr/xend1
-rwxr-xr-xtools/xenmgr/xm6
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)