mirror of
https://github.com/xen-project/xen.git
synced 2026-10-30 03:00:05 +01:00
bitkeeper revision 1.1010.1.10 (40dfd40bszbCgYF9OJJKWOo1n7X8hw)
Rename xenctl, move things around.
This commit is contained in:
1 parent
9bdbe09f07
commit
af551a1bda
17 files changed
+117
-1237
No files matched your search
@@ -162,10 +162,9 @@
|
||||
40278d91ZjLhxdjjrGe8HEdwHLj5xQ tools/examples/netbsd
|
||||
401d7e16NpnVrFSsR7lKKKfTwCYvWA tools/examples/xc_dom_control.py
|
||||
401d7e16RJj-lbtsVEjua6HYAIiKiA tools/examples/xc_dom_create.py
|
||||
403b7cf7J7FsSSoEPGhx6gXR4pIdZg tools/examples/xc_physinfo.py
|
||||
40c9c4684Wfg8VgMKtRFa_ULi2e_tQ tools/examples/xm_dom_control.py
|
||||
40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py
|
||||
40cf2937oKlROYOJTN8GWwWM5AmjBg tools/examples/xmdefaults
|
||||
40dfd40auJwNnb8NoiSnRkvZaaXkUg tools/examples/xmnetbsd
|
||||
3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
|
||||
40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen
|
||||
3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
|
||||
@@ -200,6 +199,11 @@
|
||||
3fbd0a40yT6G3M9hMpaz5xTUdl0E4g tools/xc/py/setup.py
|
||||
40c9c468icGyC5RAF1bRKsCXPDCvsA tools/xen/Makefile
|
||||
40dc4076hGpwa8-sWRN0jtXZeQJuKg tools/xen/lib/__init__.py
|
||||
40dfd40aMOhnw_cQLve9462UR5yYxQ tools/xen/lib/ext/__init__.py
|
||||
40dfd40aGqGkiopOOgJxSF4iCbHM0Q tools/xen/lib/util/__init__.py
|
||||
4055ee4dwy4l0MghZosxoiu6zmhc9Q tools/xen/lib/util/console_client.py
|
||||
40c9c468IienauFHQ_xJIcqnPJ8giQ tools/xen/lib/util/ip.py
|
||||
4059c6a0pnxhG8hwSOivXybbGOwuXw tools/xen/lib/util/tempfile.py
|
||||
40c9c468SNuObE_YWARyS0hzTPSzKg tools/xen/lib/xend/Args.py
|
||||
40c9c468Um_qc66OQeLEceIz1pgD5g tools/xen/lib/xend/EventServer.py
|
||||
40c9c468U8EVl0d3G--8YXVg6VJD3g tools/xen/lib/xend/EventTypes.py
|
||||
@@ -250,13 +254,6 @@
|
||||
40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/xen/setup.py
|
||||
40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/xen/xend
|
||||
40cf2937dqM1jWW87O5OoOYND8leuA tools/xen/xm
|
||||
4055ee41IfFazrwadCH2J72nz-A9YA tools/xenctl/Makefile
|
||||
4055ee4b_4Rvns_KzE12csI14EKK6Q tools/xenctl/lib/__init__.py
|
||||
4055ee4dwy4l0MghZosxoiu6zmhc9Q tools/xenctl/lib/console_client.py
|
||||
40c9c468IienauFHQ_xJIcqnPJ8giQ tools/xenctl/lib/ip.py
|
||||
4059c6a0pnxhG8hwSOivXybbGOwuXw tools/xenctl/lib/tempfile.py
|
||||
3fbd4bd6GtGwZGxYUJPOheYIR7bPaA tools/xenctl/lib/utils.py
|
||||
4055ee44Bu6oP7U0WxxXypbUt4dNPQ tools/xenctl/setup.py
|
||||
40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend-old/Makefile
|
||||
4055ad95Se-FqttgxollqOAAHB94zA tools/xend-old/lib/__init__.py
|
||||
4092738fMRGC9fFBcPRCWaJaj9U3ag tools/xend-old/lib/blkif.py
|
||||
@@ -274,6 +271,7 @@
|
||||
403a3edbVpV2E_wq1zeEkJ_n4Uu2eg tools/xentrace/xentrace.c
|
||||
403a3edblCUrzSj0mmKhO5HOPrOrSQ tools/xentrace/xentrace_format
|
||||
4050c413NtuyIq5lsYJV4P7KIjujXw tools/xentrace/xentrace_format.1
|
||||
40dfd40a0QtsSGigB9TCpVGWZmhlNA tools/xu/Makefile
|
||||
40dc4076St6AmPTmQPrtQ6LGHPxGmw tools/xu/lib/__init__.py
|
||||
40dc4076pVeE1kEEWzcUaNZin65kCA tools/xu/lib/domain_controller.h
|
||||
40dc4076CwBYRTUQDdbdU1L6KcLgSw tools/xu/lib/xu.c
|
||||
|
||||
@@ -5,17 +5,14 @@ all:
|
||||
$(MAKE) -C misc
|
||||
$(MAKE) -C examples
|
||||
$(MAKE) -C xentrace
|
||||
$(MAKE) -C xenctl
|
||||
$(MAKE) -C xen
|
||||
|
||||
install: all
|
||||
$(MAKE) -C xc install
|
||||
$(MAKE) -C xu install
|
||||
$(MAKE) -C misc install
|
||||
$(MAKE) -C xenctl install
|
||||
$(MAKE) -C examples install
|
||||
$(MAKE) -C xentrace install
|
||||
$(MAKE) -C xenctl install
|
||||
$(MAKE) -C xen install
|
||||
|
||||
dist: $(TARGET)
|
||||
@@ -28,6 +25,5 @@ clean:
|
||||
$(MAKE) -C misc clean
|
||||
$(MAKE) -C examples clean
|
||||
$(MAKE) -C xentrace clean
|
||||
$(MAKE) -C xenctl clean
|
||||
$(MAKE) -C xen clean
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# Get information about the physical host machine
|
||||
|
||||
import Xc
|
||||
|
||||
xc = Xc.new()
|
||||
|
||||
info = xc.physinfo()
|
||||
|
||||
fmt_info = [ ( 'CPU cores', info['cores']),
|
||||
('Hyperthreads per core', info['ht_per_core']),
|
||||
('CPU Speed (MHz)', info['cpu_khz'] / 1000),
|
||||
('Total physical mem (MB)', info['total_pages'] / 256),
|
||||
('Free physical mem (MB)', info['free_pages'] / 256) ]
|
||||
|
||||
|
||||
for (item, val) in fmt_info:
|
||||
print "%-23s" % item, ':', val
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
import re
|
||||
import string
|
||||
import time
|
||||
import os
|
||||
import os.path
|
||||
import signal
|
||||
|
||||
from xenmgr import sxp
|
||||
from xenmgr.XendClient import server
|
||||
|
||||
# usage: xc_dom_control [command] <params>
|
||||
#
|
||||
# this script isn't very smart, but it'll do for now.
|
||||
#
|
||||
|
||||
def usage (rc=0):
|
||||
if rc:
|
||||
out = sys.stderr
|
||||
else:
|
||||
out = sys.stdout
|
||||
print >> out, """
|
||||
Usage: %s [command] <params>
|
||||
|
||||
help -- print usage
|
||||
pause [dom] -- pause a domain
|
||||
unpause [dom] -- un-pause a domain
|
||||
shutdown [dom] [[-w]] -- request a domain to shutdown (can specify 'all')
|
||||
(optionally wait for complete shutdown)
|
||||
destroy [dom] -- immediately terminate a domain
|
||||
pincpu [dom] [cpu] -- pin a domain to the specified CPU
|
||||
suspend [dom] [file] -- write domain's memory to a file and terminate
|
||||
(resume by re-running xc_dom_create with -L option)
|
||||
unwatch [dom] -- kill the auto-restart daemon for a domain
|
||||
list -- print info about all domains
|
||||
listvbds -- print info about all virtual block devs
|
||||
cpu_bvtset [dom] [mcuadv] [warp] [warpl] [warpu]
|
||||
-- set BVT scheduling parameters for domain
|
||||
cpu_bvtslice [slice] -- set default BVT scheduler slice
|
||||
cpu_atropos_set [dom] [period] [slice] [latency] [xtratime]
|
||||
-- set Atropos scheduling parameters for domain
|
||||
cpu_rrobin_slice [slice] -- set Round Robin scheduler slice
|
||||
vif_stats [dom] [vif] -- get stats for a given network vif
|
||||
vif_addip [dom] [vif] [ip] -- add an IP address to a given vif
|
||||
vif_setsched [dom] [vif] [bytes] [usecs] -- rate limit vif bandwidth
|
||||
vif_getsched [dom] [vif] -- print vif's scheduling parameters
|
||||
vbd_add [dom] [uname] [dev] [mode] -- make disk/partition uname available to
|
||||
domain as dev e.g. 'vbd_add 2 phy:sda3 hda1 w'
|
||||
vbd_remove [dom] [dev] -- remove disk or partition attached as 'dev'
|
||||
""" % sys.argv[0]
|
||||
if rc: sys.exit(rc)
|
||||
|
||||
if len(sys.argv) < 2: usage(1)
|
||||
cmd = sys.argv[1]
|
||||
|
||||
#todo: replace all uses of xc with the new api.
|
||||
import Xc; xc = Xc.new()
|
||||
|
||||
rc = ''
|
||||
dom = None
|
||||
|
||||
|
||||
def auto_restart_pid_file(dom):
|
||||
# The auto-restart daemon's pid file.
|
||||
return '/var/run/xendomains/%d.pid' % dom
|
||||
|
||||
def auto_restart_pid(dom):
|
||||
watcher = auto_restart_pid_file(dom)
|
||||
if os.path.isfile(watcher):
|
||||
fd = open(watcher,'r')
|
||||
pid = int(fd.readline())
|
||||
else:
|
||||
pid = None
|
||||
return pid
|
||||
|
||||
def auto_restart_kill(dom):
|
||||
#todo: replace this - tell xend not to restart any more.
|
||||
# Kill a domain's auto restart daemon.
|
||||
pid = auto_restart_pid(dom)
|
||||
if pid:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
|
||||
|
||||
if len( sys.argv ) > 2 and re.match('\d+$', sys.argv[2]):
|
||||
dom = long(sys.argv[2])
|
||||
|
||||
if cmd == "help":
|
||||
usage()
|
||||
|
||||
elif cmd == 'pause':
|
||||
rc = server.xend_domain_pause(dom)
|
||||
|
||||
elif cmd == 'unpause':
|
||||
rc = server.xend_domain_unpause(dom)
|
||||
|
||||
elif cmd == 'shutdown':
|
||||
doms = []
|
||||
shutdown = []
|
||||
if dom != None:
|
||||
doms = [ dom ]
|
||||
elif sys.argv[2] == 'all':
|
||||
doms = server.xend_domains()
|
||||
doms.remove('0')
|
||||
for d in doms:
|
||||
ret = server.xend_domain_shutdown(d)
|
||||
if ret == 0:
|
||||
shutdown.append(d)
|
||||
else:
|
||||
rc = ret
|
||||
|
||||
wait = (len(sys.argv) == 4 and sys.argv[3] == "-w")
|
||||
if wait:
|
||||
# wait for all domains we shut down to terminate
|
||||
for dom in shutdown:
|
||||
while True:
|
||||
info = server.xend_domain(dom)
|
||||
if not info: break
|
||||
time.sleep(1)
|
||||
|
||||
elif cmd == 'destroy':
|
||||
rc = server.xend_domain_halt(dom)
|
||||
|
||||
elif cmd == 'pincpu':
|
||||
if len(sys.argv) < 4: usage(1)
|
||||
cpu = int(sys.argv[3])
|
||||
rc = server.xend_domain_pincpu(dom, cpu)
|
||||
|
||||
elif cmd == 'list':
|
||||
print 'Dom Name Mem(MB) CPU State Time(s)'
|
||||
doms = server.xend_domains()
|
||||
doms.sort()
|
||||
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)7d %(cpu)3d %(state)5s %(cpu_time)8.2f" % d)
|
||||
|
||||
elif cmd == 'unwatch':
|
||||
auto_restart_kill(dom)
|
||||
|
||||
elif cmd == 'listvbds':
|
||||
print 'Dom Dev Mode Size(MB)'
|
||||
for dom in server.xend_domains():
|
||||
for vbd in server.xend_domain_vbds(dom):
|
||||
info = server.xend_domain_vbd(vbd)
|
||||
v['vbd'] = vbd
|
||||
v['size'] = int(sxp.get_child_value(info, 'size', '0'))
|
||||
v['mode'] = sxp.get_child_value(info, 'mode', '??')
|
||||
vbd['size_mb'] = vbd['nr_sectors'] / 2048
|
||||
print ('%(dom)-4d %(vbd)04x %(mode)-2s %(size)d' % v)
|
||||
|
||||
elif cmd == 'suspend':
|
||||
if len(sys.argv) < 4: usage(1)
|
||||
file = os.path.abspath(sys.argv[3])
|
||||
auto_restart_kill(dom)
|
||||
rc = server.xend_domain_save(dom, file, progress=1)
|
||||
|
||||
elif cmd == 'cpu_bvtslice':
|
||||
if len(sys.argv) < 3: usage(1)
|
||||
slice = sys.argv[2]
|
||||
rc = server.xend_node_cpu_bvt_slice_set(slice)
|
||||
|
||||
elif cmd == 'cpu_bvtset':
|
||||
if len(sys.argv) < 7: usage(1)
|
||||
(mcuadv, warp, warpl, warpu) = map(int, sys.argv[3:7])
|
||||
|
||||
rc = server.xend_domain_cpu_bvt_set(dom, mcuadv, warp, warpl, warpu)
|
||||
|
||||
elif cmd == 'vif_stats':
|
||||
if len(sys.argv) < 4: usage(1)
|
||||
vif = int(sys.argv[3])
|
||||
|
||||
print server.xend_domain_vif_stats(dom, vif)
|
||||
|
||||
elif cmd == 'vif_addip':
|
||||
if len(sys.argv) < 5: usage(1)
|
||||
vif = int(sys.argv[3])
|
||||
ip = sys.argv[4]
|
||||
rc = server.xend_domain_vif_addip(dom, vif, ip)
|
||||
|
||||
elif cmd == 'vif_setsched':
|
||||
if len(sys.argv) < 6: usage(1)
|
||||
(vif, bytes, usecs) = map(int, sys.argv[3:6])
|
||||
rc = server.xend_domain_vif_scheduler_set(dom, vif, bytes, usecs)
|
||||
|
||||
elif cmd == 'vif_getsched':
|
||||
if len(sys.argv) < 4: usage(1)
|
||||
vif = int(sys.argv[3])
|
||||
print server.xend_domain_vif_scheduler_get(dom, vif)
|
||||
|
||||
elif cmd == 'vbd_add':
|
||||
if len(sys.argv) < 6: usage(1)
|
||||
uname = sys.argv[3]
|
||||
dev = sys.argv[4]
|
||||
mode = sys.argv[5]
|
||||
try:
|
||||
vbd = server.xend_domain_vbd_add(dom, uname, dev, mode)
|
||||
except StandardError, ex:
|
||||
print "Error:", ex
|
||||
sys.exit(1)
|
||||
print "Added disk/partition %s to domain %d as device %s (%x)" % (uname, dom, dev, vbd)
|
||||
|
||||
elif cmd == 'vbd_remove':
|
||||
if len(sys.argv) < 4: usage(1)
|
||||
dev = sys.argv[3]
|
||||
vbd = server.xend_domain_vbd_remove(dom, dev)
|
||||
if vbd < 0:
|
||||
print "Failed"
|
||||
sys.exit(1)
|
||||
else:
|
||||
print "Removed disk/partition attached as device %s (%x) in domain %d" % (dev, vbd, dom)
|
||||
|
||||
elif cmd == 'cpu_atropos_set': # args: dom period slice latency xtratime
|
||||
if len(sys.argv) < 6: usage(1)
|
||||
(period, slice, latency, xtratime) = map(int, sys.argv[3:7])
|
||||
rc = server.xend_domain_cpu_atropos_set(
|
||||
dom, period, slice, latency, xtratime)
|
||||
|
||||
elif cmd == 'cpu_rrobin_slice':
|
||||
if len(sys.argv) < 3: usage(1)
|
||||
slice = int(sys.argv[2])
|
||||
rc = server.xend_node_rrobin_set(slice=slice)
|
||||
|
||||
else:
|
||||
usage(1)
|
||||
|
||||
if rc != '':
|
||||
print "return code %d" % rc
|
||||
@@ -44,10 +44,9 @@ cpu = vmid # set based on vmid (mod number of CPUs)
|
||||
# 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" ]
|
||||
# Optionally define mac and/or bridge for the network interfaces.
|
||||
# Random MACs are assigned if not given.
|
||||
#vif = [ 'mac=aa:00:00:00:00:11, bridge=nbe-br' ]
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Define the disk devices you want the domain to have access to, and
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# -*- 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.
|
||||
#============================================================================
|
||||
|
||||
def config_usage ():
|
||||
print >>sys.stderr,"""
|
||||
The config file '%s' requires the following variable to be defined:
|
||||
vmid -- Numeric identifier for the new domain, used to calculate
|
||||
the VM's IP address and root partition. E.g. -Dvmid=1
|
||||
""" % config_file
|
||||
|
||||
|
||||
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.
|
||||
image = "/boot/netbsd"
|
||||
|
||||
# The domain build function.
|
||||
builder='netbsd'
|
||||
|
||||
# Initial memory allocation (in megabytes) for the new domain.
|
||||
memory = 16
|
||||
|
||||
# A handy name for your new domain.
|
||||
name = "NetBSD VM %d" % vmid
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Define network interfaces.
|
||||
|
||||
# Number of network interfaces. Default is 1.
|
||||
#nics=1
|
||||
|
||||
# Optionally define mac and/or bridge for the network interfaces.
|
||||
# Random MACs are assigned if not given.
|
||||
#vif = [ 'mac=aa:00:00:00:00:11, bridge=nbe-br' ]
|
||||
|
||||
# Specify IP address(es), for the new domain. You need to
|
||||
# configure IP addrs within the domain just as you do normally. This
|
||||
# is just to let Xen know about them so it can route packets
|
||||
# appropriately.
|
||||
|
||||
#ipaddr = [ xenctl.utils.add_offset_to_ip(xenctl.utils.get_current_ipaddr(),vmid),
|
||||
# xenctl.utils.add_offset_to_ip('169.254.1.0',vmid),
|
||||
# ]
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# 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
|
||||
extra = "4 VMID=%d bootdev=xennet0" % vmid
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Set according to whether you want the domain restarted when it exits.
|
||||
# The default is False.
|
||||
#restart = True
|
||||
|
||||
#============================================================================
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -439,7 +439,7 @@ class ProgConsole(Prog):
|
||||
if not console:
|
||||
self.err("No console information")
|
||||
port = sxp.child_value(console, "port")
|
||||
from xenctl import console_client
|
||||
from xen.util import console_client
|
||||
console_client.connect("localhost", int(port))
|
||||
|
||||
xm.prog(ProgConsole)
|
||||
|
||||
@@ -8,6 +8,7 @@ setup(name = 'xen',
|
||||
author_email = 'mike.wray@hp.com',
|
||||
packages = ['xen',
|
||||
'xen.ext',
|
||||
'xen.util',
|
||||
'xen.xend',
|
||||
'xen.xend.server',
|
||||
'xen.xm',
|
||||
|
||||
Whitespace-only changes.
@@ -1,944 +0,0 @@
|
||||
import os, re, socket, string, sys, tempfile, xenctl.ip
|
||||
|
||||
##### 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_EXPERT_MODE = 0
|
||||
|
||||
##### 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
|
||||
|
||||
|
||||
##### Networking-related functions
|
||||
|
||||
def get_current_ipaddr(dev='eth0'):
|
||||
return xenctl.ip.get_current_ipaddr(dev)
|
||||
def get_current_ipmask(dev='eth0'):
|
||||
return xenctl.ip.get_current_ipmask(dev)
|
||||
def get_current_ipgw(dev='eth0'):
|
||||
return xenctl.ip.get_current_ipgw(dev)
|
||||
def setup_vfr_rules_for_vif(dom,vif,addr):
|
||||
return xenctl.ip.setup_vfr_rules_for_vif(dom,vif,addr)
|
||||
def inet_aton(addr):
|
||||
return xenctl.ip.inet_aton(addr)
|
||||
def inet_ntoa(n):
|
||||
return xenctl.ip.inet_ntoa(n)
|
||||
def add_offset_to_ip(addr, offset):
|
||||
return xenctl.ip.add_offset_to_ip(addr, offset)
|
||||
def check_subnet( ip, network, netmask ):
|
||||
return xenctl.ip.check_subnet( ip, network, netmask )
|
||||
|
||||
##### 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 = fd.readline()
|
||||
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 = fd.readlines()
|
||||
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
|
||||
|
||||
|
||||
##### Management of the Xen control daemon
|
||||
##### (c) Keir Fraser, University of Cambridge
|
||||
|
||||
def xend_control_message( message ):
|
||||
"""Takes a textual control message and sends it to the 'xend' Xen
|
||||
control daemon. Returns a dictionary containing the daemon's multi-part
|
||||
response."""
|
||||
tmpdir = tempfile.mkdtemp()
|
||||
try:
|
||||
ctl = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM, 0)
|
||||
ctl.bind(tmpdir+'/sock')
|
||||
ctl.sendto(message, '/var/run/xend/management_sock')
|
||||
data, addr = ctl.recvfrom(2048)
|
||||
ctl.close()
|
||||
finally:
|
||||
if os.path.exists(tmpdir+'/sock'):
|
||||
os.unlink(tmpdir+'/sock')
|
||||
if os.path.exists(tmpdir):
|
||||
os.rmdir(tmpdir)
|
||||
return eval(data)
|
||||
|
||||
|
||||
##### 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 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 * 2048 # 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 * 2048
|
||||
|
||||
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 * 2048
|
||||
|
||||
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 / 2048
|
||||
|
||||
|
||||
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 * 2048 # 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):
|
||||
"""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 = fd.readline()
|
||||
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 > VBD_EXPERT_MODE:
|
||||
print >> sys.stderr, """ERROR: This kind of disk sharing is not allowed
|
||||
at the current safety level (%d).""" % VBD_EXPERT_MODE
|
||||
level = -1
|
||||
|
||||
return level
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
from distutils.core import setup, Extension
|
||||
import sys
|
||||
|
||||
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.
|
||||
major = sys.version_info[0]
|
||||
minor = sys.version_info[1]
|
||||
if major == 2 and minor < 3:
|
||||
modules.append('xenctl.tempfile')
|
||||
|
||||
setup(name = 'xenctl',
|
||||
version = '1.0',
|
||||
py_modules = modules,
|
||||
package_dir = { 'xenctl' : 'lib' },
|
||||
)
|
||||
|
||||
@@ -12,4 +12,4 @@ install: all
|
||||
fi
|
||||
|
||||
clean:
|
||||
rm -rf build *.pyc *.pyo *.o *.a *~
|
||||
rm -rf build *.pyc *.pyo *.a *.so *.o *~ *.rpm
|
||||