bitkeeper revision 1.946.1.3 (40c9c475sJLMEuc6TqUt-bX6hVFOpQ)

Add source tree for the management utils.
This commit is contained in:
mjw@wray-m-3.hpl.hp.com committed 2004-06-11 14:40:53 +00:00
1 parent 9eb865c540
commit 8b980d633c
57 files changed
+8896 -2

No files matched your search

+52
View File
@@ -174,6 +174,9 @@
401d7e16RJj-lbtsVEjua6HYAIiKiA tools/examples/xc_dom_create.py
403b7cf7J7FsSSoEPGhx6gXR4pIdZg tools/examples/xc_physinfo.py
401d7e16X4iojyKopo_j63AyzYZd2A tools/examples/xc_vd_tool.py
40c9c4684Wfg8VgMKtRFa_ULi2e_tQ tools/examples/xm_dom_control.py
40c9c468pXANclL7slGaoD0kSrIwoQ tools/examples/xm_dom_create.py
40c9c468QKoqBHjb5Qwrm60pNVcVng tools/examples/xm_vd_tool.py
3f776bd2Xd-dUcPKlPN2vG89VGtfvQ tools/misc/Makefile
40ab2cfawIw8tsYo0dQKtp83h4qfTQ tools/misc/fakei386xen
3f6dc136ZKOjd8PIqLbFBl_v-rnkGg tools/misc/miniterm/Makefile
@@ -218,8 +221,10 @@
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
40c9c468F36e06WH38kpYrD3JfXC0Q tools/xenctl/lib/vdisk.py
4055ee44Bu6oP7U0WxxXypbUt4dNPQ tools/xenctl/setup.py
40431ac64Hj4ixUnKmlugZKhXPFE_Q tools/xend/Makefile
4055ad95Se-FqttgxollqOAAHB94zA tools/xend/lib/__init__.py
@@ -232,6 +237,53 @@
40431ac8wrUEj-XM7B8smFtx_HA7lQ tools/xend/lib/utils.c
4054a2fdkdATEnRw-U7AUlgu-6JiUA tools/xend/setup.py
4056cd26Qyp09iNoOjrvzg8KYzSqOw tools/xend/xend
40c9c468icGyC5RAF1bRKsCXPDCvsA tools/xenmgr/Makefile
40c9c468SNuObE_YWARyS0hzTPSzKg tools/xenmgr/lib/Args.py
40c9c468Um_qc66OQeLEceIz1pgD5g tools/xenmgr/lib/EventServer.py
40c9c468U8EVl0d3G--8YXVg6VJD3g tools/xenmgr/lib/EventTypes.py
40c9c468QJTEuk9g4qHxGpmIi70PEQ tools/xenmgr/lib/PrettyPrint.py
40c9c4688m3eqnC8fhLu1APm36VOVA tools/xenmgr/lib/XendClient.py
40c9c468t6iIKTjwuYoe-UMCikDcOQ tools/xenmgr/lib/XendConsole.py
40c9c468WnXs6eOUSff23IIGI4kMfQ tools/xenmgr/lib/XendDB.py
40c9c468fSl3H3IypyT0ppkbb0ZT9A tools/xenmgr/lib/XendDomain.py
40c9c468bbKq3uC7_fuNUkiMMjArdw tools/xenmgr/lib/XendDomainConfig.py
40c9c4685ykq87_n1kVUbMr9flx9fg tools/xenmgr/lib/XendDomainInfo.py
40c9c46854nsHmuxHQHncKk5rAs5NA tools/xenmgr/lib/XendMigrate.py
40c9c468M96gA1EYDvNa5w5kQNYLFA tools/xenmgr/lib/XendNode.py
40c9c4686jruMyZIqiaZRMiMoqMJtg tools/xenmgr/lib/XendRoot.py
40c9c468P75aHqyIE156JXwc-5W92A tools/xenmgr/lib/XendVdisk.py
40c9c468xzANp6o2D_MeCYwNmOIUsQ tools/xenmgr/lib/XendVnet.py
40c9c468x191zetrVlMnExfsQWHxIQ tools/xenmgr/lib/__init__.py
40c9c468S2YnCEKmk4ey8XQIST7INg tools/xenmgr/lib/encode.py
40c9c468DCpMe542varOolW1Xc68ew tools/xenmgr/lib/server/SrvBase.py
40c9c468IxQabrKJSWs0aEjl-27mRQ tools/xenmgr/lib/server/SrvConsole.py
40c9c4689Io5bxfbYIfRiUvsiLX0EQ tools/xenmgr/lib/server/SrvConsoleDir.py
40c9c468woSmBByfeXA4o_jGf2gCgA tools/xenmgr/lib/server/SrvConsoleServer.py
40c9c468kACsmkqjxBWKHRo071L26w tools/xenmgr/lib/server/SrvDeviceDir.py
40c9c468EQZJVkCLds-OhesJVVyZbQ tools/xenmgr/lib/server/SrvDir.py
40c9c468TyHZUq8sk0FF_vxM6Sozrg tools/xenmgr/lib/server/SrvDomain.py
40c9c469WzajDjutou3X7FmL9hMf3g tools/xenmgr/lib/server/SrvDomainDir.py
40c9c469-8mYEJJTAR6w_ClrJRAfwQ tools/xenmgr/lib/server/SrvEventDir.py
40c9c4694eu5759Dehr4Uhakei0EMg tools/xenmgr/lib/server/SrvNode.py
40c9c469TaZ83ypsrktmPSHLEZiP5w tools/xenmgr/lib/server/SrvRoot.py
40c9c469W3sgDMbBJYQdz5wbQweL0Q tools/xenmgr/lib/server/SrvServer.py
40c9c469JlUVPkwGWZyVnqIsU8U6Bw tools/xenmgr/lib/server/SrvVdisk.py
40c9c469sG8iuyxjVH3zKw8XOAyxtQ tools/xenmgr/lib/server/SrvVdiskDir.py
40c9c469aq7oXrE1Ngqf3_lBqL0RoQ tools/xenmgr/lib/server/SrvVnetDir.py
40c9c469Y_aimoOFfUZoS-4eV8gEKg tools/xenmgr/lib/server/__init__.py
40c9c4692hckPol_EK0EGB16ZyDsyQ tools/xenmgr/lib/server/blkif.py
40c9c469N2-b3GqpLHHHPZykJPLVvA tools/xenmgr/lib/server/channel.py
40c9c469hJ_IlatRne-9QEa0-wlquw tools/xenmgr/lib/server/console.py
40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/xenmgr/lib/server/controller.py
40c9c469vHh-qLiiubdbKEQbJf18Zw tools/xenmgr/lib/server/cstruct.py
40c9c469yrm31i60pGKslTi2Zgpotg tools/xenmgr/lib/server/messages.py
40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/xenmgr/lib/server/netif.py
40c9c469ZqILEQ8x6yWy0_51jopiCg tools/xenmgr/lib/server/params.py
40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/xenmgr/lib/sxp.py
40c9c469kT0H9COWzA4XzPBjWK0WsA tools/xenmgr/netfix
40c9c469n2RRwCmjWdjdyyVRWKmgWg tools/xenmgr/setup.py
40c9c4697z76HDfkCLdMhmaEwzFoNQ tools/xenmgr/xend
40c9c469JkN47d1oXi-e0RjAP-C6uQ tools/xenmgr/xenmgrd
403a3edbrr8RE34gkbR40zep98SXbg tools/xentrace/Makefile
40a107afN60pFdURgBv9KwEzgRl5mQ tools/xentrace/formats
4050c413PhhLNAYk3TEwP37i_iLw9Q tools/xentrace/xentrace.8
+124
View File
@@ -30,3 +30,127 @@ linux-2.4.26-xen/.config.old
linux-2.4.26-xen/.depend
linux-2.4.26-xen/.hdepend
linux-2.4.26-xen/.version
linux-2.4.26-xen/.config
linux-2.4.26-xen/COPYING
linux-2.4.26-xen/CREDITS
linux-2.4.26-xen/Documentation/00-INDEX
linux-2.4.26-xen/Documentation/BK-usage/00-INDEX
linux-2.4.26-xen/Documentation/BK-usage/bk-kernel-howto.txt
linux-2.4.26-xen/Documentation/BK-usage/bk-make-sum
linux-2.4.26-xen/Documentation/BK-usage/bksend
linux-2.4.26-xen/Documentation/BK-usage/bz64wrap
linux-2.4.26-xen/Documentation/BK-usage/cset-to-linus
linux-2.4.26-xen/Documentation/BK-usage/csets-to-patches
linux-2.4.26-xen/Documentation/BK-usage/unbz64wrap
linux-2.4.26-xen/Documentation/BUG-HUNTING
linux-2.4.26-xen/Documentation/Changes
linux-2.4.26-xen/Documentation/CodingStyle
linux-2.4.26-xen/Documentation/Configure.help
linux-2.4.26-xen/Documentation/DMA-mapping.txt
linux-2.4.26-xen/Documentation/DocBook/Makefile
linux-2.4.26-xen/Documentation/DocBook/deviceiobook.tmpl
linux-2.4.26-xen/Documentation/DocBook/journal-api.tmpl
linux-2.4.26-xen/Documentation/DocBook/kernel-api.tmpl
linux-2.4.26-xen/Documentation/DocBook/kernel-hacking.tmpl
linux-2.4.26-xen/Documentation/DocBook/kernel-locking.tmpl
linux-2.4.26-xen/Documentation/DocBook/mcabook.tmpl
linux-2.4.26-xen/Documentation/DocBook/mousedrivers.tmpl
linux-2.4.26-xen/Documentation/DocBook/parport-multi.fig
linux-2.4.26-xen/Documentation/DocBook/parport-share.fig
linux-2.4.26-xen/Documentation/DocBook/parport-structure.fig
linux-2.4.26-xen/Documentation/DocBook/parportbook.tmpl
linux-2.4.26-xen/Documentation/DocBook/procfs-guide.tmpl
linux-2.4.26-xen/Documentation/DocBook/procfs_example.c
linux-2.4.26-xen/Documentation/DocBook/sis900.tmpl
linux-2.4.26-xen/Documentation/DocBook/tulip-user.tmpl
linux-2.4.26-xen/Documentation/DocBook/via-audio.tmpl
linux-2.4.26-xen/Documentation/DocBook/videobook.tmpl
linux-2.4.26-xen/Documentation/DocBook/wanbook.tmpl
linux-2.4.26-xen/Documentation/DocBook/z8530book.tmpl
linux-2.4.26-xen/Documentation/IO-mapping.txt
linux-2.4.26-xen/Documentation/IPMI.txt
linux-2.4.26-xen/Documentation/IRQ-affinity.txt
linux-2.4.26-xen/Documentation/LVM-HOWTO
linux-2.4.26-xen/Documentation/README.DAC960
linux-2.4.26-xen/Documentation/README.moxa
linux-2.4.26-xen/Documentation/README.nsp32_cb.eng
linux-2.4.26-xen/Documentation/README.nsp_cs.eng
linux-2.4.26-xen/Documentation/SAK.txt
linux-2.4.26-xen/Documentation/SubmittingDrivers
linux-2.4.26-xen/Documentation/SubmittingPatches
linux-2.4.26-xen/Documentation/VGA-softcursor.txt
linux-2.4.26-xen/Documentation/arm/Booting
linux-2.4.26-xen/Documentation/arm/ConfigVars
linux-2.4.26-xen/Documentation/arm/MEMC
linux-2.4.26-xen/Documentation/arm/Netwinder
linux-2.4.26-xen/Documentation/arm/README
linux-2.4.26-xen/Documentation/arm/SA1100/ADSBitsy
linux-2.4.26-xen/Documentation/arm/SA1100/Assabet
linux-2.4.26-xen/Documentation/arm/SA1100/Brutus
linux-2.4.26-xen/Documentation/arm/SA1100/CERF
linux-2.4.26-xen/Documentation/arm/SA1100/DMA
linux-2.4.26-xen/Documentation/arm/SA1100/FreeBird
linux-2.4.26-xen/Documentation/arm/SA1100/GraphicsClient
linux-2.4.26-xen/Documentation/arm/SA1100/GraphicsMaster
linux-2.4.26-xen/Documentation/arm/SA1100/HUW_WEBPANEL
linux-2.4.26-xen/Documentation/arm/SA1100/Itsy
linux-2.4.26-xen/Documentation/arm/SA1100/LART
linux-2.4.26-xen/Documentation/arm/SA1100/PCMCIA
linux-2.4.26-xen/Documentation/arm/SA1100/PLEB
linux-2.4.26-xen/Documentation/arm/SA1100/Pangolin
linux-2.4.26-xen/Documentation/arm/SA1100/SA1100_USB
linux-2.4.26-xen/Documentation/arm/SA1100/Tifon
linux-2.4.26-xen/Documentation/arm/SA1100/Victor
linux-2.4.26-xen/Documentation/arm/SA1100/Yopy
linux-2.4.26-xen/Documentation/arm/SA1100/empeg
linux-2.4.26-xen/Documentation/arm/SA1100/nanoEngine
linux-2.4.26-xen/Documentation/arm/SA1100/serial_UART
linux-2.4.26-xen/Documentation/arm/Setup
linux-2.4.26-xen/Documentation/arm/empeg/README
linux-2.4.26-xen/Documentation/arm/empeg/ir.txt
linux-2.4.26-xen/Documentation/arm/empeg/mkdevs
linux-2.4.26-xen/Documentation/arm/nwfpe/NOTES
linux-2.4.26-xen/Documentation/arm/nwfpe/README
linux-2.4.26-xen/Documentation/arm/nwfpe/README.FPE
linux-2.4.26-xen/Documentation/arm/nwfpe/TODO
linux-2.4.26-xen/Documentation/binfmt_misc.txt
linux-2.4.26-xen/Documentation/cachetlb.txt
linux-2.4.26-xen/Documentation/cciss.txt
linux-2.4.26-xen/Documentation/cdrom/00-INDEX
linux-2.4.26-xen/Documentation/cdrom/Makefile
linux-2.4.26-xen/Documentation/cdrom/aztcd
linux-2.4.26-xen/Documentation/cdrom/cdrom-standard.tex
linux-2.4.26-xen/Documentation/cdrom/cdu31a
linux-2.4.26-xen/Documentation/cdrom/cm206
linux-2.4.26-xen/Documentation/cdrom/gscd
linux-2.4.26-xen/Documentation/cdrom/ide-cd
linux-2.4.26-xen/Documentation/cdrom/isp16
linux-2.4.26-xen/Documentation/cdrom/mcd
linux-2.4.26-xen/Documentation/cdrom/mcdx
linux-2.4.26-xen/Documentation/cdrom/optcd
linux-2.4.26-xen/Documentation/cdrom/sbpcd
linux-2.4.26-xen/Documentation/cdrom/sjcd
linux-2.4.26-xen/Documentation/cdrom/sonycd535
linux-2.4.26-xen/Documentation/computone.txt
linux-2.4.26-xen/Documentation/cpqarray.txt
linux-2.4.26-xen/Documentation/cris/README
linux-2.4.26-xen/Documentation/crypto/api-intro.txt
linux-2.4.26-xen/Documentation/crypto/descore-readme.txt
linux-2.4.26-xen/Documentation/devices.txt
linux-2.4.26-xen/Documentation/digiboard.txt
linux-2.4.26-xen/Documentation/digiepca.txt
linux-2.4.26-xen/Documentation/dnotify.txt
linux-2.4.26-xen/Documentation/exception.txt
linux-2.4.26-xen/Documentation/fb/00-INDEX
linux-2.4.26-xen/Documentation/fb/README-sstfb.txt
linux-2.4.26-xen/Documentation/fb/aty128fb.txt
linux-2.4.26-xen/Documentation/fb/clgenfb.txt
linux-2.4.26-xen/Documentation/fb/framebuffer.txt
linux-2.4.26-xen/Documentation/fb/internals.txt
linux-2.4.26-xen/Documentation/fb/matroxfb.txt
linux-2.4.26-xen/Documentation/fb/modedb.txt
linux-2.4.26-xen/Documentation/fb/pvr2fb.txt
linux-2.4.26-xen/Documentation/fb/sa1100fb.txt
tools/xenmgr/lib/server/blkif.py~
tools/xenmgr/lib/server/messages.py~
tools/xenmgr/lib/server/netif.py~
+3
View File
@@ -7,6 +7,7 @@ all:
$(MAKE) -C xentrace
$(MAKE) -C xenctl
$(MAKE) -C xend
$(MAKE) -C xenmgr
install: all
$(MAKE) -C balloon install
@@ -16,6 +17,7 @@ install: all
$(MAKE) -C xentrace install
$(MAKE) -C xenctl install
$(MAKE) -C xend install
$(MAKE) -C xenmgr install
dist: $(TARGET)
$(MAKE) prefix=`pwd`/../../install dist=yes install
@@ -30,4 +32,5 @@ clean:
$(MAKE) -C xentrace clean
$(MAKE) -C xenctl clean
$(MAKE) -C xend clean
$(MAKE) -C xenmgr install
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env python
import sys
import re
import string
import time
import os
import os.path
import signal
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
stop [dom] -- pause a domain
start [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 == 'stop':
rc = server.xend_domain_stop(dom)
elif cmd == 'start':
rc = server.xend_domain_start(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)'
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)
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
+402
View File
@@ -0,0 +1,402 @@
#!/usr/bin/env python
import string
import sys
import os
import os.path
import time
import socket
import getopt
import signal
import syslog
import xenctl.console_client
from xenmgr import sxp
from xenmgr import PrettyPrint
from xenmgr.XendClient import server
config_dir = '/etc/xc/'
config_file = xc_config_file = config_dir + 'defaults'
def main_usage ():
print >>sys.stderr,"""
Usage: %s <args>
This tool is used to create and start new domains. It reads defaults
from a file written in Python, having allowed variables to be set and
passed into the file. Further command line arguments allow the
defaults to be overridden. The defaults for each parameter are listed
in [] brackets. Arguments are as follows:
Arguments to control the parsing of the defaults file:
-f config_file -- Use the specified defaults script.
Default: ['%s']
-L state_file -- Load virtual machine memory state from state_file
-D foo=bar -- Set variable foo=bar before parsing config
E.g. '-D vmid=3;ip=1.2.3.4'
-h -- Print extended help message, including all arguments
-n -- Dry run only, don't actually create domain
Prints the config, suitable for -F.
-q -- Quiet - write output only to the system log
-F domain_config -- Build domain using the config in the file.
Suitable files can be made using '-n' to output a config.
""" % (sys.argv[0], xc_config_file)
def extra_usage ():
print >>sys.stderr,"""
Arguments to override current config read from '%s':
-c -- Turn into console terminal after domain is created
-k image -- Path to kernel image ['%s']
-r ramdisk -- Path to ramdisk (or empty) ['%s']
-b builder_fn -- Function to use to build domain ['%s']
-m mem_size -- Initial memory allocation in MB [%dMB]
-N domain_name -- Set textual name of domain ['%s']
-a auto_restart -- Restart domain on exit, yes/no ['%d']
-e vbd_expert -- Safety catch to avoid some disk accidents ['%s']
-d udisk,dev,rw -- Add disk, partition, or virtual disk to domain. E.g. to
make partion sda4 available to the domain as hda1 with
read-write access: '-d phy:sda4,hda1,rw' To add
multiple disks use multiple -d flags or seperate with ';'
Default: ['%s']
-i vfr_ipaddr -- Add IP address to the list which Xen will route to
the domain. Use multiple times to add more IP addrs.
Default: ['%s']
Args to override the kernel command line, which is concatenated from these:
-I cmdline_ip -- Override 'ip=ipaddr:nfsserv:gateway:netmask::eth0:off'
Default: ['%s']
-R cmdline_root -- Override root device parameters.
Default: ['%s']
-E cmdline_extra -- Override extra kernel args and rc script env vars.
Default: ['%s']
""" % (config_file,
image, ramdisk, builder_fn, mem_size, domain_name, auto_restart,
vbd_expert,
printvbds( vbd_list ),
reduce ( (lambda a,b: a+':'+b), vfr_ipaddr,'' )[1:],
cmdline_ip, cmdline_root, cmdline_extra)
def config_usage (): pass
def answer ( s ):
s = string.lower(s)
if s == 'yes' or s == 'true' or s == '1': return 1
return 0
def printvbds ( v ):
s=''
for (a,b,c) in v:
s = s + '; %s,%s,%s' % (a,b,c)
return s[2:]
def output(string):
global quiet
syslog.syslog(string)
if not quiet:
print string
return
bail=False; dryrun=False; extrahelp=False; quiet = False
image=''; ramdisk=''; builder_fn='linux'; restore=0; state_file=''
mem_size=0; domain_name=''; vfr_ipaddr=[];
vbd_expert='rr'; auto_restart=False;
vbd_list = []; cmdline_ip = ''; cmdline_root=''; cmdline_extra=''
pci_device_list = []; console_port = -1
auto_console = False
config_from_file = False
##### Determine location of defaults file
#####
try:
opts, args = getopt.getopt(sys.argv[1:], "h?nqcf:F:D:k:r:b:m:N:a:e:d:i:I:R:E:L:" )
for opt in opts:
if opt[0] == '-f': config_file= opt[1]
if opt[0] == '-h' or opt[0] == '-?' : bail=True; extrahelp=True
if opt[0] == '-n': dryrun=True
if opt[0] == '-D':
for o in string.split( opt[1], ';' ):
(l,r) = string.split( o, '=' )
exec "%s='%s'" % (l,r)
if opt[0] == '-q': quiet = True
if opt[0] == '-L': restore = True; state_file = opt[1]
if opt[0] == '-F': config_from_file = True; domain_config = opt[1]
except getopt.GetoptError:
bail=True
if not config_from_file:
try:
os.stat( config_file )
except:
try:
d = config_dir + config_file
os.stat( d )
config_file = d
except:
print >> sys.stderr, "Unable to open config file '%s'" % config_file
bail = True
##### Parse the config file
#####
if not config_from_file:
if not quiet:
print "Parsing config file '%s'" % config_file
try:
execfile ( config_file )
except (AssertionError,IOError):
print >>sys.stderr,"Exiting %s" % sys.argv[0]
bail = True
##### Print out config if necessary
#####
def bailout():
global extrahelp
main_usage()
config_usage()
if extrahelp: extra_usage()
sys.exit(1)
if bail:
bailout()
##### Parse any command line overrides
#####
x_vbd_list = []
x_vfr_ipaddr = []
for opt in opts:
if opt[0] == '-k': image = opt[1]
if opt[0] == '-r': ramdisk = opt[1]
if opt[0] == '-b': builder_fn = opt[1]
if opt[0] == '-m': mem_size = int(opt[1])
if opt[0] == '-C': cpu = int(opt[1])
if opt[0] == '-N': domain_name = opt[1]
if opt[0] == '-a': auto_restart = answer(opt[1])
if opt[0] == '-e': vbd_expert = opt[1]
if opt[0] == '-I': cmdline_ip = opt[1]
if opt[0] == '-R': cmdline_root = opt[1]
if opt[0] == '-E': cmdline_extra = opt[1]
if opt[0] == '-i': x_vfr_ipaddr.append(opt[1])
if opt[0] == '-c': auto_console = True
if opt[0] == '-d':
try:
vv = string.split(opt[1],';')
for v in vv:
(udisk,dev,mode) = string.split(v,',')
x_vbd_list.append( (udisk,dev,mode) )
except:
print >>sys.stderr, "Invalid block device specification : %s" % opt[1]
sys.exit(1)
if x_vbd_list: vbd_list = x_vbd_list
if x_vfr_ipaddr: vfr_ipaddr = x_vfr_ipaddr
syslog.openlog('xc_dom_create.py %s' % config_file, 0, syslog.LOG_DAEMON)
def strip(pre, s):
if s.startswith(pre):
return s[len(pre):]
else:
return s
def make_domain_config():
global builder_fn, image, ramdisk, mem_size, domain_name
global cpu
global cmdline, cmdline_ip, cmdline_root
global vfr_ipaddr, vbd_list, vbd_expert
config = ['config',
['name', domain_name ],
['memory', mem_size ],
]
if cpu:
config.append(['cpu', cpu])
config_image = [ builder_fn ]
config_image.append([ 'kernel', os.path.abspath(image) ])
if ramdisk:
config_image.append([ 'ramdisk', os.path.abspath(ramdisk) ])
if cmdline_ip:
cmdline_ip = strip("ip=", cmdline_ip)
config_image.append(['ip', cmdline_ip])
if cmdline_root:
cmdline_root = strip("root=", cmdline_root)
config_image.append(['root', cmdline_root])
if cmdline_extra:
config_image.append(['args', cmdline_extra])
config.append(['image', config_image ])
config_devs = []
for (uname, dev, mode) in vbd_list:
config_vbd = ['vbd',
['uname', uname],
['dev', dev ],
['mode', mode ] ]
if vbd_expert != 'rr':
config_vbd.append(['sharing', vbd_expert])
config_devs.append(['device', config_vbd])
for (bus, dev, func) in pci_device_list:
config_pci = ['pci',
['bus', bus ],
['dev', dev ],
['func', func] ]
config_devs.append(['device', config_pci])
config += config_devs
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)
return config
def parse_config_file(domain_file):
config = None
fin = None
try:
fin = file(domain_file, "rb")
config = sxp.parse(fin)
if len(config) >= 1:
config = config[0]
else:
raise StandardError("Invalid configuration")
except StandardError, ex:
print >> sys.stderr, "Error :", ex
sys.exit(1)
#finally:
if fin: fin.close()
return config
# This function creates, builds and starts a domain, using the values
# in the global variables, set above. It is used in the subsequent
# code for starting the new domain and rebooting it if appropriate.
def make_domain(config):
"""Create, build and start a domain.
Returns: [int] the ID of the new domain.
"""
global restore
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:
print "Error starting domain"
server.xend_domain_halt(dom)
sys.exit()
return (dom, console_port)
PID_DIR = '/var/run/xendomains/'
def pidfile(dom):
return PID_DIR + '%d.pid' % dom
def mkpidfile():
global current_id
if not os.path.isdir(PID_DIR):
os.mkdir(PID_DIR)
fd = open(pidfile(current_id), 'w')
print >> fd, str(os.getpid())
fd.close()
return
def rmpidfile():
global current_id
os.unlink(pidfile(current_id))
def death_handler(dummy1,dummy2):
global current_id
os.unlink(pidfile(current_id))
output('Auto-restart daemon: daemon PID = %d for domain %d is now exiting'
% (os.getpid(), current_id))
sys.exit(0)
return
#============================================================================
# The starting / monitoring of the domain actually happens here...
if config_from_file:
config = parse_config_file(domain_config)
else:
config = make_domain_config()
if dryrun:
print "# %s" % ' '.join(sys.argv)
PrettyPrint.prettyprint(config)
sys.exit(0)
elif quiet:
pass
else:
PrettyPrint.prettyprint(config)
# start the domain and record its ID number
(current_id, current_port) = make_domain(config)
def start_msg(prefix, dom, port):
output(prefix + "VM started in domain %d" % dom)
if port:
output(prefix + "Console I/O available on TCP port %d." % port)
start_msg('', current_id, current_port)
if current_port and auto_console:
xenctl.console_client.connect('127.0.0.1', current_port)
# if the auto_restart flag is set then keep polling to see if the domain is
# alive - restart if it is not by calling make_domain() again (it's necessary
# to update the id variable, since the new domain may have a new ID)
#todo: Replace this - get xend to watch them.
if auto_restart:
ARD = "Auto-restart daemon: "
# turn ourselves into a background daemon
try:
pid = os.fork()
if pid > 0:
sys.exit(0)
os.setsid()
pid = os.fork()
if pid > 0:
output(ARD + 'PID = %d' % pid)
sys.exit(0)
signal.signal(signal.SIGTERM,death_handler)
except OSError:
print >> sys.stderr, ARD+'Startup failed'
sys.exit(1)
mkpidfile()
while True:
time.sleep(1)
# todo: use new interface
info = xc.domain_getinfo(current_id, 1)
if info == [] or info[0]['dom'] != current_id:
output(ARD + "Domain %d terminated, restarting VM in new domain"
% current_id)
rmpidfile()
(current_id, current_port) = make_domain()
mkpidfile()
start_msg(ARD, current_id, current_port)
+157
View File
@@ -0,0 +1,157 @@
#!/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
+95
View File
@@ -0,0 +1,95 @@
import os
import re
import socket
import struct
##### Networking-related functions
def get_current_ipaddr(dev='eth0'):
"""Return a string containing the primary IP address for the given
network interface (default 'eth0').
"""
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
lines = readlines(fd)
for line in lines:
m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
line )
if m:
return m.group(1)
return None
def get_current_ipmask(dev='eth0'):
"""Return a string containing the primary IP netmask for the given
network interface (default 'eth0').
"""
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
lines = readlines(fd)
for line in lines:
m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
line )
if m:
return m.group(1)
return None
def get_current_ipgw(dev='eth0'):
"""Return a string containing the IP gateway for the given
network interface (default 'eth0').
"""
fd = os.popen( '/sbin/route -n' )
lines = readlines(fd)
for line in lines:
m = re.search( '^\S+\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)' +
'\s+\S+\s+\S*G.*' + dev + '.*', line )
if m:
return m.group(1)
return None
def setup_vfr_rules_for_vif(dom,vif,addr):
"""Takes a tuple ( domain-id, vif-id, ip-addr ), where the ip-addr
is expressed as a textual dotted quad, and set up appropriate routing
rules in Xen. No return value.
"""
fd = os.open( '/proc/xen/vfr', os.O_WRONLY )
if ( re.search( '169\.254', addr) ):
os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
' srcaddrmask=255.255.255.255' +
' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
' dstdom=0 dstidx=0 proto=any\n' )
else:
os.write( fd, 'ADD ACCEPT srcaddr=' + addr +
' srcaddrmask=255.255.255.255' +
' srcdom=' + str(dom) + ' srcidx=' + str(vif) +
' dst=PHYS proto=any\n' )
os.write( fd, 'ADD ACCEPT dstaddr=' + addr +
' dstaddrmask=255.255.255.255' +
' src=ANY' +
' dstdom=' + str(dom) + ' dstidx=' + str(vif) +
' proto=any\n' )
os.close( fd )
return None
def inet_aton(addr):
"""Convert an IP addr in IPv4 dot notation into an int.
"""
b = socket.inet_aton(addr)
return struct.unpack('!I', b)[0]
def inet_ntoa(n):
"""Convert an int into an IP addr in IPv4 dot notation.
"""
b = struct.pack('!I', n)
return socket.inet_ntoa(b)
def add_offset_to_ip(addr, offset):
"""Add a numerical offset to an IP addr in IPv4 dot notation.
"""
n = inet_aton(addr)
n += offset
return inet_ntoa(n)
def check_subnet( ip, network, netmask ):
n_ip = inet_aton(ip)
n_net = inet_aton(network)
n_mask = inet_aton(netmask)
return (n_ip & n_mask) == (n_net & n_mask)
+944
View File
@@ -0,0 +1,944 @@
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
+1 -1
View File
@@ -2,7 +2,7 @@
from distutils.core import setup, Extension
import sys
modules = [ 'xenctl.console_client', 'xenctl.utils' ]
modules = [ 'xenctl.console_client', 'xenctl.utils', 'xenctl.ip' , 'xenctl.vdisk' ]
# We need the 'tempfile' module from Python 2.3. We install this ourselves
# if the installed Python is older than 2.3.
+5 -1
View File
@@ -412,7 +412,8 @@ static PyObject *xu_message_get_payload(PyObject *self, PyObject *args)
C2P(netif_fe_interface_status_changed_t, evtchn, Int, Long);
return dict;
case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
C2P(netif_fe_driver_status_changed_t, status, Int, Long);
C2P(netif_fe_driver_status_changed_t, status, Int, Long);
C2P(netif_fe_driver_status_changed_t, nr_interfaces, Int, Long);
return dict;
case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT):
C2P(netif_fe_interface_connect_t, handle, Int, Long);
@@ -603,6 +604,9 @@ static PyObject *xu_message_new(PyObject *self, PyObject *args)
P2C(netif_be_disconnect_t, domid, u32);
P2C(netif_be_disconnect_t, netif_handle, u32);
break;
case TYPE(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED):
P2C(netif_fe_driver_status_changed_t, status, u32);
P2C(netif_fe_driver_status_changed_t, nr_interfaces, u32);
}
if ( dict_items_parsed != PyDict_Size(payload) )
+19
View File
@@ -0,0 +1,19 @@
all:
python setup.py build
install: all
if [ "$(prefix)" = "" ]; then \
python setup.py install; \
elif [ "$(dist)" = "yes" ]; then \
python setup.py install --home="$(prefix)"; \
else \
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
clean:
rm -rf build *.pyc *.pyo *.o *.a *~
+126
View File
@@ -0,0 +1,126 @@
import sxp
class ArgError(StandardError):
pass
class Args:
"""Argument encoding support for HTTP.
"""
def __init__(self, paramspec, keyspec):
self.arg_ord = []
self.arg_dict = {}
self.key_ord = []
self.key_dict = {}
for (name, type) in paramspec:
self.arg_ord.append(name)
self.arg_dict[name] = type
for (name, type) in keyspec:
self.key_ord.append(name)
self.key_dict[name] = type
def get_args(self, d, xargs=None):
args = {}
keys = {}
params = []
if xargs:
self.split_args(xargs, args, keys)
self.split_args(d, args, keys)
for a in self.arg_ord:
if a in args:
params.append(args[a])
else:
raise ArgError('Missing parameter: %s' % a)
return (params, keys)
def split_args(self, d, args, keys):
for (k, v) in d.items():
if k in self.arg_dict:
type = self.arg_dict[k]
val = self.coerce(type, v)
args[k] = val
elif k in self.key_dict:
type = self.key_dict[k]
val = self.coerce(type, v)
keys[k] = val
else:
raise ArgError('Invalid parameter: %s' % k)
def get_form_args(self, f, xargs=None):
d = {}
for (k, v) in f.items():
n = len(v)
if ((k not in self.arg_dict) and
(k not in self.key_dict)):
continue
if n == 0:
continue
elif n == 1:
d[k] = v[0]
else:
raise ArgError('Too many values for %s' % k)
return self.get_args(d, xargs=xargs)
def coerce(self, type, v):
try:
if type == 'int':
return int(v)
if type == 'str':
return str(v)
if type == 'sxpr':
return self.sxpr(v)
except ArgError:
raise
except StandardError, ex:
raise ArgError(str(ex))
def sxpr(self, v):
if instanceof(v, types.ListType):
return v
if instanceof(v, types.File) or hasattr(v, 'readline'):
return sxpr_file(v)
if instanceof(v, types.StringType):
return sxpr_file(StringIO(v))
return str(v)
def sxpr_file(self, fin):
try:
vals = sxp.parse(fin)
except:
raise ArgError('Coercion to sxpr failed')
if len(vals) == 1:
return vals[0]
else:
raise ArgError('Too many sxprs')
def call_with_args(self, fn, args, xargs=None):
(params, keys) = self.get_args(args, xargs=xargs)
fn(*params, **keys)
def call_with_form_args(self, fn, fargs, xargs=None):
(params, keys) = self.get_form_args(fargs, xargs=xargs)
fn(*params, **keys)
class ArgFn(Args):
"""Represent a remote HTTP operation as a function.
Used on the client.
"""
def __init__(self, fn, paramspec, keyspec={}):
Args.__init__(self, paramspec, keyspec)
self.fn = fn
def __call__(self, fargs, xargs=None):
return self.call_with_args(self.fn, fargs, xargs=xargs)
class FormFn(Args):
"""Represent an operation as a function over a form.
Used in the HTTP server.
"""
def __init__(self, fn, paramspec, keyspec={}):
Args.__init__(self, paramspec, keyspec)
self.fn = fn
def __call__(self, fargs, xargs=None):
return self.call_with_form_args(self.fn, fargs, xargs=xargs)
+204
View File
@@ -0,0 +1,204 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Simple publish/subscribe event server.
"""
import string
# subscribe a.b.c h: map a.b.c -> h
# subscribe a.b.* h: map a.b.* -> h
# subscribe a.b.? h: map a.b.? -> h
#
# for event a.b.c.d:
#
# lookup a.b.c.d, call handlers
#
# lookup a.b.c.?, call handlers
#
# lookup a.b.c.d.*, call handlers
# lookup a.b.c.*, call handlers
# lookup a.b.*, call handlers
# lookup a.*, call handlers
# lookup *, call handlers
# a.b.c.d = (a b c d)
# a.b.c.? = (a b c _)
# a.b.c.* = (a b c . _)
class EventServer:
DOT = '.'
QUERY = '?'
DOT_QUERY = DOT + QUERY
STAR = '*'
DOT_STAR = DOT + STAR
def __init__(self, run=0):
self.handlers = {}
self.run = run
self.queue = []
def start(self):
"""Enable event handling. Sends any queued events.
"""
self.run = 1
for (e,v) in self.queue:
self.inject(e, v)
self.queue = []
def stop(self):
"""Suspend event handling. Events injected while suspended
are queued until we are started again.
"""
self.run = 0
def subscribe(self, event, handler):
"""Subscribe to an event. For example 'a.b.c.d'.
A subcription like 'a.b.c.?' ending in '?' matches any value
for the '?'. A subscription like 'a.b.c.*' ending in '*' matches
any event type with the same prefix, 'a.b.c' in this case.
event event name
handler event handler fn(event, val)
"""
hl = self.handlers.get(event)
if hl is None:
self.handlers[event] = [handler]
else:
hl.append(handler)
def unsubscribe_all(self, event=None):
"""Unsubscribe all handlers for a given event, or all handlers.
event event (optional)
"""
if event == None:
self.handlers.clear()
else:
del self.handlers[event]
def unsubscribe(self, event, handler):
"""Unsubscribe a given event and handler.
event event
handler handler
"""
hl = self.handlers.get(event)
if hl is None:
return
hl.remove(handler)
def inject(self, event, val):
"""Inject an event. Handlers for it are called if runing, otherwise
it is queued.
event event type
val event value
"""
if self.run:
#print ">event", event, val
self.call_event_handlers(event, event, val)
self.call_query_handlers(event, val)
self.call_star_handlers(event, val)
else:
self.queue.append( (event, val) )
def call_event_handlers(self, key, event, val):
"""Call the handlers for an event.
It is safe for handlers to subscribe or unsubscribe.
key key for handler list
event event type
val event value
"""
hl = self.handlers.get(key)
if hl is None:
return
# Copy the handler list so that handlers can call
# subscribe/unsubscribe safely - python list iteration
# is not safe against list modification.
for h in hl[:]:
try:
h(event, val)
except:
pass
def call_query_handlers(self, event, val):
"""Call regex handlers for events matching 'event' that end in '?'.
event event type
val event value
"""
dot_idx = event.rfind(self.DOT)
if dot_idx == -1:
self.call_event_handlers(self.QUERY, event, val)
else:
event_query = event[0:dot_idx] + self.DOT_QUERY
self.call_event_handlers(event_query, event, val)
def call_star_handlers(self, event, val):
"""Call regex handlers for events matching 'event' that end in '*'.
event event type
val event value
"""
etype = string.split(event, self.DOT)
for i in range(len(etype), 0, -1):
event_star = self.DOT.join(etype[0:i]) + self.DOT_STAR
self.call_event_handlers(event_star, event, val)
self.call_event_handlers(self.STAR, event, val)
def instance():
global inst
try:
inst
except:
inst = EventServer()
inst.start()
return inst
def main():
def sys_star(event, val):
print 'sys_star', event, val
def sys_foo(event, val):
print 'sys_foo', event, val
s.unsubscribe('sys.foo', sys_foo)
def sys_foo2(event, val):
print 'sys_foo2', event, val
def sys_bar(event, val):
print 'sys_bar', event, val
def sys_foo_bar(event, val):
print 'sys_foo_bar', event, val
def foo_bar(event, val):
print 'foo_bar', event, val
s = EventServer()
s.start()
s.subscribe('sys.*', sys_star)
s.subscribe('sys.foo', sys_foo)
s.subscribe('sys.foo', sys_foo2)
s.subscribe('sys.bar', sys_bar)
s.subscribe('sys.foo.bar', sys_foo_bar)
s.subscribe('foo.bar', foo_bar)
s.inject('sys.foo', 'hello')
print
s.inject('sys.bar', 'hello again')
print
s.inject('sys.foo.bar', 'hello again')
print
s.inject('foo.bar', 'hello again')
print
s.inject('foo', 'hello again')
print
s.start()
s.unsubscribe('sys.*', sys_star)
s.unsubscribe_all('sys.*')
s.inject('sys.foo', 'hello')
if __name__ == "__main__":
main()
+34
View File
@@ -0,0 +1,34 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
## XEND_DOMAIN_CREATE = "xend.domain.create": dom
## create:
## xend.domain.destroy: dom, reason:died/crashed
## xend.domain.up ?
## xend.domain.start: dom
## xend.domain.stop: dom
## xend.domain.shutdown: dom
## xend.domain.halt: dom
## xend.domain.migrate.begin: dom, to
## Begin tells: src host, src domain uri, dst host. Dst id known?
## err: src host, src domain uri, dst host, dst id if known, status (of domain: ok, dead,...), reason
## end: src host, src domain uri, dst host, dst uri
## Events for both ends of migrate: for exporter and importer?
## Include migrate id so can tie together.
## Have uri /xend/migrate/<id> for migrate info (migrations in progress).
## (xend.domain.migrate.begin (src <host>) (src.domain <id>)
## (dst <host>) (id <migrate id>))
## xend.domain.migrate.end:
## (xend.domain.migrate.end (domain <id>) (to <host>)
## xend.node.up: xend uri
## xend.node.down: xend uri
## xend.error ?
## format:
+299
View File
@@ -0,0 +1,299 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""General pretty-printer, including support for SXP.
"""
import sys
import types
import StringIO
import sxp
class PrettyItem:
def __init__(self, width):
self.width = width
def insert(self, block):
block.addtoline(self)
def get_width(self):
return self.width
def output(self, out):
print '***PrettyItem>output>', self
pass
def prettyprint(self, out, width):
print '***PrettyItem>prettyprint>', self
return width
class PrettyString(PrettyItem):
def __init__(self, x):
PrettyItem.__init__(self, len(x))
self.value = x
def output(self, out):
out.write(self.value)
def prettyprint(self, line):
line.output(self)
def show(self, out):
print >> out, ("(string (width %d) '%s')" % (self.width, self.value))
class PrettySpace(PrettyItem):
def output(self, out):
out.write(' ' * self.width)
def prettyprint(self, line):
line.output(self)
def show(self, out):
print >> out, ("(space (width %d))" % self.width)
class PrettyBreak(PrettyItem):
def __init__(self, width, indent):
PrettyItem.__init__(self, width)
self.indent = indent
self.space = 0
self.active = 0
def output(self, out):
out.write(' ' * self.width)
def prettyprint(self, line):
if line.breaks(self.space):
self.active = 1
line.newline(self.indent)
else:
line.output(self)
def show(self, out):
print >> out, ("(break (width %d) (indent %d) (space %d) (active %d))"
% (self.width, self.indent, self.space, self.lspace, self.active))
class PrettyNewline(PrettySpace):
def __init__(self, indent):
PrettySpace.__init__(self, indent)
def insert(self, block):
block.newline()
block.addtoline(self)
def output(self, out):
out.write(' ' * self.width)
def prettyprint(self, line):
line.newline(0)
line.output(self)
def show(self, out):
print >> out, ("(nl (indent %d))" % self.indent)
class PrettyLine(PrettyItem):
def __init__(self):
PrettyItem.__init__(self, 0)
self.content = []
def write(self, x):
self.content.append(x)
def end(self):
width = 0
lastwidth = 0
lastbreak = None
for x in self.content:
if isinstance(x, PrettyBreak):
if lastbreak:
lastbreak.space = (width - lastwidth)
lastbreak = x
lastwidth = width
width += x.get_width()
if lastbreak:
lastbreak.space = (width - lastwidth)
self.width = width
def prettyprint(self, line):
for x in self.content:
x.prettyprint(line)
def show(self, out):
print >> out, '(LINE (width %d)' % self.width
for x in self.content:
x.show(out)
print >> out, ')'
class PrettyBlock(PrettyItem):
def __init__(self, all=0, parent=None):
self.width = 0
self.lines = []
self.parent = parent
self.indent = 0
self.all = all
self.broken = 0
self.newline()
def add(self, item):
item.insert(self)
def end(self):
self.width = 0
for l in self.lines:
l.end()
if self.width < l.width:
self.width = l.width
def breaks(self, n):
return self.all and self.broken
def newline(self):
self.lines.append(PrettyLine())
def addtoline(self, x):
self.lines[-1].write(x)
def prettyprint(self, line):
self.indent = line.used
line.block = self
if not line.fits(self.width):
self.broken = 1
for l in self.lines:
l.prettyprint(line)
line.block = self.parent
def show(self, out):
print >> out, ('(BLOCK (width %d) (indent %d) (all %d) (broken %d)' %
(self.width, self.indent, self.all, self.broken))
for l in self.lines:
l.show(out)
print >> out, ')'
class Line:
def __init__(self, out, width):
self.out = out
self.width = width
self.used = 0
self.space = self.width
def newline(self, indent):
indent += self.block.indent
self.out.write('\n')
self.out.write(' ' * indent)
self.used = indent
self.space = self.width - self.used
def fits(self, n):
return self.space - n >= 0
def breaks(self, n):
return self.block.breaks(n) or not self.fits(n)
def output(self, x):
n = x.get_width()
self.space -= n
self.used += n
if self.space < 0:
self.space = 0
x.output(self.out)
class PrettyPrinter:
"""A prettyprinter based on what I remember of Derek Oppen's
prettyprint algorithm from TOPLAS way back.
"""
def __init__(self, width=40):
self.width = width
self.block = None
self.top = None
def write(self, x):
self.block.add(PrettyString(x))
def add(self, item):
self.block.add(item)
def addbreak(self, width=1, indent=4):
self.add(PrettyBreak(width, indent))
def addspace(self, width=1):
self.add(PrettySpace(width))
def addnl(self, indent=0):
self.add(PrettyNewline(indent))
def begin(self, all=0):
block = PrettyBlock(all=all, parent=self.block)
self.block = block
def end(self):
self.block.end()
if self.block.parent:
self.block.parent.add(self.block)
else:
self.top = self.block
self.block = self.block.parent
def prettyprint(self, out=sys.stdout):
line = Line(out, self.width)
self.top.prettyprint(line)
class SXPPrettyPrinter(PrettyPrinter):
"""An SXP prettyprinter.
"""
def pstring(self, x):
io = StringIO.StringIO()
sxp.show(x, out=io)
io.seek(0)
val = io.getvalue()
io.close()
return val
def pprint(self, l):
if isinstance(l, types.ListType):
self.begin(all=1)
self.write('(')
i = 0
for x in l:
if(i): self.addbreak()
self.pprint(x)
i += 1
self.addbreak(width=0, indent=0)
self.write(')')
self.end()
else:
self.write(self.pstring(l))
def prettyprint(sxpr, out=sys.stdout, width=80):
"""Prettyprint an SXP form.
sxpr s-expression
out destination
width maximum output width
"""
if isinstance(sxpr, types.ListType):
pp = SXPPrettyPrinter(width=width)
pp.pprint(sxpr)
pp.prettyprint(out=out)
else:
sxp.show(sxpr, out=out)
print >> out
def main():
pin = sxp.Parser()
while 1:
buf = sys.stdin.read(100)
pin.input(buf)
if buf == '': break
l = pin.get_val()
prettyprint(l, width=80)
if __name__ == "__main__":
main()
+368
View File
@@ -0,0 +1,368 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Client API for the HTTP interface on xend.
Callable as a script - see main().
"""
import sys
import httplib
import types
from StringIO import StringIO
import urlparse
from encode import *
import sxp
import PrettyPrint
DEBUG = 1
class Foo(httplib.HTTPResponse):
def begin(self):
fin = self.fp
while(1):
buf = fin.readline()
print "***", buf
if buf == '':
print
sys.exit()
def sxprio(sxpr):
io = StringIO()
sxp.show(sxpr, out=io)
print >> io
io.seek(0)
return io
def fileof(val):
"""Converter for passing configs.
Handles lists, files directly.
Assumes a string is a file name and passes its contents.
"""
if isinstance(val, types.ListType):
return sxprio(val)
if isinstance(val, types.StringType):
return file(val)
if hasattr(val, 'readlines'):
return val
# todo: need to sort of what urls/paths are using for objects.
# e.g. for domains at the moment return '0'.
# should probably return abs path w.r.t. server, e.g. /xend/domain/0.
# As an arg, assume abs path is obj uri, otherwise just id.
# Function to convert to full url: Xend.uri(path), e.g.
# maps /xend/domain/0 to http://wray-m-3.hpl.hp.com:8000/xend/domain/0
# And should accept urls for ids?
def urljoin(location, root, prefix='', rest=''):
base = 'http://' + location + root + prefix
url = urlparse.urljoin(base, rest)
return url
def nodeurl(location, root, id=''):
return urljoin(location, root, 'node/', id)
def domainurl(location, root, id=''):
return urljoin(location, root, 'domain/', id)
def consoleurl(location, root, id=''):
return urljoin(location, root, 'console/', id)
def vbdurl(location, root, id=''):
return urljoin(location, root, 'vbd/', id)
def deviceurl(location, root, id=''):
return urljoin(location, root, 'device/', id)
def vneturl(location, root, id=''):
return urljoin(location, root, 'vnet/', id)
def eventurl(location, root, id=''):
return urljoin(location, root, 'event/', id)
def xend_request(url, method, data=None):
urlinfo = urlparse.urlparse(url)
(uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo
if DEBUG: print url, urlinfo
if uproto != 'http':
raise StandardError('Invalid protocol: ' + uproto)
if DEBUG: print '>xend_request', ulocation, upath, method, data
(hdr, args) = encode_data(data)
if data and method == 'GET':
upath += '?' + args
args = None
if method == "POST" and upath.endswith('/'):
upath = upath[:-1]
if DEBUG: print "ulocation=", ulocation, "upath=", upath, "args=", args
#hdr['User-Agent'] = 'Mozilla'
#hdr['Accept'] = 'text/html,text/plain'
conn = httplib.HTTPConnection(ulocation)
#conn.response_class = Foo
conn.set_debuglevel(1)
conn.request(method, upath, args, hdr)
resp = conn.getresponse()
if DEBUG: print resp.status, resp.reason
if DEBUG: print resp.msg.headers
if resp.status in [204, 404]:
return None
if resp.status not in [200, 201, 202, 203]:
raise RuntimeError(resp.reason)
pin = sxp.Parser()
data = resp.read()
if DEBUG: print "***data" , data
if DEBUG: print "***"
pin.input(data);
pin.input_eof()
conn.close()
val = pin.get_val()
#if isinstance(val, types.ListType) and sxp.name(val) == 'val':
# 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 '**'
return val
def xend_get(url, args=None):
return xend_request(url, "GET", args)
def xend_call(url, data):
return xend_request(url, "POST", data)
class Xend:
SRV_DEFAULT = "localhost:8000"
ROOT_DEFAULT = "/xend/"
def __init__(self, srv=None, root=None):
self.bind(srv, root)
def bind(self, srv=None, root=None):
if srv is None: srv = self.SRV_DEFAULT
if root is None: root = self.ROOT_DEFAULT
if not root.endswith('/'): root += '/'
self.location = srv
self.root = root
def nodeurl(self, id=''):
return nodeurl(self.location, self.root, id)
def domainurl(self, id=''):
return domainurl(self.location, self.root, id)
def consoleurl(self, id=''):
return consoleurl(self.location, self.root, id)
def vbdurl(self, id=''):
return vbdurl(self.location, self.root, id)
def deviceurl(self, id=''):
return deviceurl(self.location, self.root, id)
def vneturl(self, id=''):
return vneturl(self.location, self.root, id)
def eventurl(self, id=''):
return eventurl(self.location, self.root, id)
def xend(self):
return xend_get(urljoin(self.location, self.root))
def xend_node(self):
return xend_get(self.nodeurl())
def xend_node_cpu_rrobin_slice_set(self, slice):
return xend_call(self.nodeurl(),
{'op' : 'cpu_rrobin_slice_set',
'slice' : slice })
def xend_node_cpu_bvt_slice_set(self, slice):
return xend_call(self.nodeurl(),
{'op' : 'cpu_bvt_slice_set',
'slice' : slice })
def xend_domains(self):
return xend_get(self.domainurl())
def xend_domain_create(self, conf):
return xend_call(self.domainurl(),
{'op' : 'create',
'config' : fileof(conf) })
def xend_domain(self, id):
return xend_get(self.domainurl(id))
def xend_domain_start(self, id):
return xend_call(self.domainurl(id),
{'op' : 'start'})
def xend_domain_stop(self, id):
return xend_call(self.domainurl(id),
{'op' : 'stop'})
def xend_domain_shutdown(self, id):
return xend_call(self.domainurl(id),
{'op' : 'shutdown'})
def xend_domain_halt(self, id):
return xend_call(self.domainurl(id),
{'op' : 'halt'})
def xend_domain_save(self, id, filename):
return xend_call(self.domainurl(id),
{'op' : 'save',
'file' : filename})
def xend_domain_restore(self, id, filename, conf):
return xend_call(self.domainurl(id),
{'op' : 'restore',
'file' : filename,
'config' : fileof(conf) })
def xend_domain_migrate(self, id, dst):
return xend_call(self.domainurl(id),
{'op' : 'migrate',
'dst' : dst})
def xend_domain_pincpu(self, id, cpu):
return xend_call(self.domainurl(id),
{'op' : 'pincpu',
'cpu' : cpu})
def xend_domain_cpu_bvt_set(self, id, mcuadv, warp, warpl, warpu):
return xend_call(self.domainurl(id),
{'op' : 'cpu_bvt_set',
'mcuadv' : mvuadv,
'warp' : warp,
'warpl' : warpl,
'warpu' : warpu })
def xend_domain_cpu_atropos_set(self, id, period, slice, latency, xtratime):
return xend_call(self.domainurl(id),
{'op' : 'cpu_atropos_set',
'period' : period,
'slice' : slice,
'latency' : latency,
'xtratime': xtratime })
def xend_domain_vifs(self, id):
return xend_get(self.domainurl(id),
{ 'op' : 'vifs' })
def xend_domain_vif_stats(self, id, vif):
return xend_get(self.domainurl(id),
{ 'op' : 'vif_stats',
'vif' : vif})
def xend_domain_vif_ip_add(self, id, vif, ipaddr):
return xend_call(self.domainurl(id),
{'op' : 'vif_ip_add',
'vif' : vif,
'ip' : ipaddr })
def xend_domain_vif_scheduler_set(id, vif, bytes, usecs):
return xend_call(self.domainurl(id),
{'op' : 'vif_scheduler_set',
'vif' : vif,
'bytes' : bytes,
'usecs' : usecs })
def xend_domain_vif_scheduler_get(id, vif):
return xend_get(self.domainurl(id),
{'op' : 'vif_scheduler_get',
'vif' : vif})
def xend_domain_vbds(self, id):
return xend_get(self.domainurl(id),
{'op' : 'vbds'})
def xend_domain_vbd(self, id, vbd):
return xend_get(self.domainurl(id),
{'op' : 'vbd',
'vbd' : vbd})
def xend_domain_vbd_add(self, id, uname, dev, mode):
return xend_call(self.domainurl(id),
{'op' : 'vbd_add',
'uname' : uname,
'dev' : dev,
'mode' : mode})
def xend_domain_vbd_remove(self, id, dev):
return xend_call(self.domainurl(id),
{'op' : 'vbd_remove',
'dev' : dev})
def xend_consoles(self):
return xend_get(self.consoleurl())
def xend_console(self, id):
return xend_get(self.consoleurl(id))
def xend_vbds(self):
return xend_get(self.vbdurl())
def xend_vbd_create(self, conf):
return xend_call(self.vbdurl(),
{'op': 'create', 'config': fileof(conf) })
def xend_vbd(self, id):
return xend_get(self.vbdurl(id))
def xend_vbd_delete(self, id):
return xend_call(self.vbdurl(id),
{'op': 'delete'})
def xend_vbd_refresh(self, id, expiry):
return xend_call(self.vbdurl(id),
{'op': 'refresh', 'expiry': expiry })
def xend_vbd_expand(self, id, size):
return xend_call(self.vbdurl(id),
{'op': 'expand', 'size': size})
def xend_vnets(self):
return xend_get(self.vneturl())
def xend_vnet_create(self, conf):
return xend_call(self.vneturl(),
{'op': 'create', 'config': fileof(conf) })
def xend_vnet(self, id):
return xend_get(self.vneturl(id))
def xend_vnet_delete(self, id):
return xend_call(self.vneturl(id),
{'op': 'delete'})
def xend_event_inject(self, sxpr):
val = xend_call(self.eventurl(),
{'op': 'inject', 'event': fileof(sxpr) })
def main(argv):
"""Call an API function:
python XendClient.py fn args...
The leading 'xend_' on the function can be omitted.
Example:
> python XendClient.py domains
(domain 0 8)
> python XendClient.py domain 0
(domain (id 0) (name Domain-0) (memory 128))
"""
server = Xend()
fn = argv[1]
if not fn.startswith('xend'):
fn = 'xend_' + fn
args = argv[2:]
getattr(server, fn)(*args)
if __name__ == "__main__":
main(sys.argv)
else:
server = Xend()
+172
View File
@@ -0,0 +1,172 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
import socket
import Xc
xc = Xc.new()
import sxp
import XendRoot
xroot = XendRoot.instance()
import XendDB
import EventServer
eserver = EventServer.instance()
from xenmgr.server import SrvConsoleServer
xcd = SrvConsoleServer.instance()
class XendConsoleInfo:
"""Console information record.
"""
def __init__(self, console, dom1, port1, dom2, port2, conn=None):
self.console = console
self.dom1 = dom1
self.port1 = port1
self.dom2 = dom2
self.port2 = port2
self.conn = conn
#self.id = "%d.%d-%d.%d" % (self.dom1, self.port1, self.dom2, self.port2)
self.id = str(port1)
def __str__(self):
s = "console"
s += " id=%s" % self.id
s += " src=%d.%d" % (self.dom1, self.port1)
s += " dst=%d.%d" % (self.dom2, self.port2)
s += " port=%s" % self.console
if self.conn:
s += " conn=%s:%s" % (self.conn[0], self.conn[1])
return s
def sxpr(self):
sxpr = ['console',
['id', self.id],
['src', self.dom1, self.port1],
['dst', self.dom2, self.port2],
['port', self.console],
]
if self.conn:
sxpr.append(['connected', self.conn[0], self.conn[1]])
return sxpr
def connection(self):
return self.conn
def update(self, consinfo):
conn = sxp.child(consinfo, 'connected')
if conn:
self.conn = conn[1:]
else:
self.conn = None
def uri(self):
"""Get the uri to use to connect to the console.
This will be a telnet: uri.
return uri
"""
host = socket.gethostname()
return "telnet://%s:%s" % (host, self.console)
class XendConsole:
dbpath = "console"
def __init__(self):
self.db = XendDB.XendDB(self.dbpath)
self.console = {}
self.console_db = self.db.fetchall("")
if xroot.get_rebooted():
print 'XendConsole> rebooted: removing all console info'
self.rm_all()
eserver.subscribe('xend.domain.died', self.onDomainDied)
def rm_all(self):
"""Remove all console info. Used after reboot.
"""
for (k, v) in self.console_db.items():
self._delete_console(k)
def refresh(self):
consoles = xcd.consoles()
cons = {}
for consinfo in consoles:
id = str(sxp.child_value(consinfo, 'id'))
cons[id] = consinfo
if id not in self.console:
self._new_console(consinfo)
for c in self.console.values():
consinfo = cons.get(c.id)
if consinfo:
c.update(consinfo)
else:
self._delete_console(c.id)
def onDomainDied(self, event, val):
dom = int(val)
for c in self.consoles():
if (c.dom1 == dom) or (c.dom2 == dom):
self._delete_console(c.id)
def sync(self):
self.db.saveall("", self.console_db)
def sync_console(self, id):
self.db.save(id, self.console_db[id])
def _new_console(self, consinfo):
# todo: xen needs a call to get current domain id.
dom1 = 0
port1 = sxp.child_value(consinfo, 'local_port')
dom2 = sxp.child_value(consinfo, 'domain')
port2 = sxp.child_value(consinfo, 'remote_port')
console = sxp.child_value(consinfo, 'console_port')
info = XendConsoleInfo(console, dom1, int(port1), int(dom2), int(port2))
info.update(consinfo)
self._add_console(info.id, info)
return info
def _add_console(self, id, info):
self.console[id] = info
self.console_db[id] = info.sxpr()
self.sync_console(id)
def _delete_console(self, id):
if id in self.console:
del self.console[id]
if id in self.console_db:
del self.console_db[id]
self.db.delete(id)
def console_ls(self):
self.refresh()
return self.console.keys()
def consoles(self):
self.refresh()
return self.console.values()
def console_create(self, dom):
consinfo = xcd.console_create(dom)
info = self._new_console(consinfo)
return info
def console_get(self, id):
self.refresh()
return self.console.get(id)
def console_delete(self, id):
self._delete_console(id)
def console_disconnect(self, id):
id = int(id)
xcd.console_disconnect(id)
def instance():
global inst
try:
inst
except:
inst = XendConsole()
return inst
+91
View File
@@ -0,0 +1,91 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
import os
import os.path
import errno
import dircache
import time
import sxp
import XendRoot
xroot = XendRoot.instance()
class XendDB:
"""Persistence for Xend. Stores data in files and directories.
"""
def __init__(self, path=None):
self.dbpath = xroot.get_dbroot()
if path:
self.dbpath = os.path.join(self.dbpath, path)
pass
def filepath(self, path):
return os.path.join(self.dbpath, path)
def fetch(self, path):
fpath = self.filepath(path)
return self.fetchfile(fpath)
def fetchfile(self, fpath):
pin = sxp.Parser()
fin = file(fpath, "rb")
try:
while 1:
try:
buf = fin.read(1024)
except IOError, ex:
if ex.errno == errno.EINTR:
continue
else:
raise
pin.input(buf)
if buf == '':
pin.input_eof()
break
finally:
fin.close()
return pin.get_val()
def save(self, path, sxpr):
fpath = self.filepath(path)
return self.savefile(fpath, sxpr)
def savefile(self, fpath, sxpr):
fdir = os.path.dirname(fpath)
if not os.path.isdir(fdir):
os.makedirs(fdir)
fout = file(fpath, "wb+")
try:
t = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
fout.write("# %s %s\n" % (fpath, t))
sxp.show(sxpr, out=fout)
finally:
fout.close()
def fetchall(self, path):
dpath = self.filepath(path)
d = {}
for k in dircache.listdir(dpath):
try:
v = self.fetchfile(os.path.join(dpath, k))
d[k] = v
except:
pass
return d
def saveall(self, path, d):
for (k, v) in d.items():
self.save(os.path.join(path, k), v)
def delete(self, path):
dpath = self.filepath(path)
os.unlink(dpath)
def ls(self, path):
dpath = self.filepath(path)
return dircache.listdir(dpath)
+347
View File
@@ -0,0 +1,347 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Handler for domain operations.
Nothing here is persistent (across reboots).
Needs to be persistent for one uptime.
"""
import sys
import Xc; xc = Xc.new()
import xenctl.ip
import xenctl.vdisk
import sxp
import XendRoot
xroot = XendRoot.instance()
import XendDB
import XendDomainInfo
import XendConsole
import EventServer
eserver = EventServer.instance()
__all__ = [ "XendDomain" ]
class XendDomain:
"""Index of all domains. Singleton.
"""
dbpath = "domain"
domain = {}
def __init__(self):
self.xconsole = XendConsole.instance()
# Table of domain info indexed by domain id.
self.db = XendDB.XendDB(self.dbpath)
#self.domain = {}
self.domain_db = self.db.fetchall("")
if xroot.get_rebooted():
print 'XendDomain> rebooted: removing all domain info'
self.rm_all()
self.initial_refresh()
def rm_all(self):
"""Remove all domain info. Used after reboot.
"""
for (k, v) in self.domain_db.items():
self._delete_domain(k, notify=0)
def initial_refresh(self):
"""Refresh initial domain info from domain_db.
"""
domlist = xc.domain_getinfo()
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'))
if domid in doms:
self._new_domain(config)
else:
self._delete_domain(domid)
self.refresh()
def sync(self):
"""Sync domain db to disk.
"""
self.db.saveall("", self.domain_db)
def sync_domain(self, dom):
"""Sync info for a domain to disk.
dom domain id (string)
"""
self.db.save(dom, self.domain_db[dom])
def close(self):
pass
def _new_domain(self, info):
"""Create a domain entry from saved info.
"""
console = None
kernel = None
id = sxp.child_value(info, 'id')
dom = int(id)
name = sxp.child_value(info, 'name')
memory = int(sxp.child_value(info, 'memory'))
consoleinfo = sxp.child(info, 'console')
if consoleinfo:
consoleid = sxp.child_value(consoleinfo, 'id')
console = self.xconsole.console_get(consoleid)
if dom and console is None:
# Try to connect a console.
console = self.xconsole.console_create(dom)
config = sxp.child(info, 'config')
if config:
image = sxp.child(info, 'image')
if image:
image = sxp.child0(image)
kernel = sxp.child_value(image, 'kernel')
dominfo = XendDomainInfo.XendDomainInfo(
config, dom, name, memory, kernel, console)
self.domain[id] = dominfo
def _add_domain(self, id, info, notify=1):
self.domain[id] = info
self.domain_db[id] = info.sxpr()
self.sync_domain(id)
if notify: eserver.inject('xend.domain.created', id)
def _delete_domain(self, id, notify=1):
if id in self.domain:
if notify: eserver.inject('xend.domain.died', id)
del self.domain[id]
if id in self.domain_db:
del self.domain_db[id]
self.db.delete(id)
def refresh(self):
"""Refresh domain list from Xen.
"""
domlist = xc.domain_getinfo()
# Index the domlist by id.
# Add entries for any domains we don't know about.
doms = {}
for d in domlist:
id = str(d['dom'])
doms[id] = d
if id not in self.domain:
config = None
image = None
newinfo = XendDomainInfo.XendDomainInfo(
config, d['dom'], d['name'], d['mem_kb']/1024, image)
self._add_domain(id, newinfo)
# Remove entries for domains that no longer exist.
for d in self.domain.values():
dominfo = doms.get(d.id)
if dominfo:
d.update(dominfo)
else:
self._delete_domain(d.id)
def refresh_domain(self, id):
dom = int(id)
dominfo = xc.domain_getinfo(dom, 1)
if dominfo == [] or dominfo[0]['dom'] != dom:
try:
self._delete_domain(id)
except:
pass
else:
d = self.domain.get(id)
if d:
d.update(dominfo)
def domain_ls(self):
# List domains.
# Update info from kernel first.
self.refresh()
return self.domain.keys()
def domains(self):
self.refresh()
return self.domain.values()
def domain_create(self, config):
# Create domain, log it.
deferred = XendDomainInfo.vm_create(config)
def fn(dominfo):
self._add_domain(dominfo.id, dominfo)
return dominfo
deferred.addCallback(fn)
return deferred
def domain_get(self, id):
id = str(id)
self.refresh_domain(id)
return self.domain[id]
def domain_start(self, id):
"""Start domain running.
"""
dom = int(id)
eserver.inject('xend.domain.start', id)
return xc.domain_start(dom=dom)
def domain_stop(self, id):
"""Stop domain running.
"""
dom = int(id)
return xc.domain_stop(dom=dom)
def domain_shutdown(self, id):
"""Shutdown domain (nicely).
"""
dom = int(id)
if dom <= 0:
return 0
eserver.inject('xend.domain.shutdown', id)
val = xc.domain_destroy(dom=dom, force=0)
self.refresh()
return val
def domain_halt(self, id):
"""Shutdown domain immediately.
"""
dom = int(id)
if dom <= 0:
return 0
eserver.inject('xend.domain.halt', id)
val = xc.domain_destroy(dom=dom, force=1)
self.refresh()
return val
def domain_migrate(self, id, dst):
"""Start domain migration.
"""
# Need a cancel too?
pass
def domain_save(self, id, dst, progress=0):
"""Save domain state to file, halt domain.
"""
dom = int(id)
self.domain_stop(id)
eserver.inject('xend.domain.save', id)
rc = xc.linux_save(dom=dom, state_file=dst, progress=progress)
if rc == 0:
self.domain_halt(id)
return rc
def domain_restore(self, src, config, progress=0):
"""Restore domain from file.
"""
dominfo = XendDomainInfo.dom_restore(dom, config)
self._add_domain(dominfo.id, dominfo)
return dominfo
def domain_device_add(self, id, info):
"""Add a device to a domain.
"""
pass
def domain_device_remove(self, id, dev):
"""Delete a device from a domain.
"""
pass
def domain_device_configure(self, id, dev, info):
"""Configure a domain device.
"""
pass
#============================================================================
# Backward compatibility stuff from here on.
def domain_pincpu(self, dom, cpu):
dom = int(dom)
return xc.domain_pincpu(dom, cpu)
def domain_cpu_bvt_set(self, dom, mcuadv, warp, warpl, warpu):
dom = int(dom)
return xc.bvtsched_domain_set(dom=dom, mcuadv=mcuadv,
warp=warp, warpl=warpl, warpu=warpu)
def domain_cpu_bvt_get(self, dom):
dom = int(dom)
return xc.bvtsched_domain_get(dom)
def domain_cpu_atropos_set(self, dom, period, slice, latency, xtratime):
dom = int(dom)
return xc.atropos_domain_set(dom, period, slice, latency, xtratime)
def domain_cpu_atropos_get(self, dom):
dom = int(dom)
return xc.atropos_domain_get(dom)
def domain_vif_ls(self, dom):
dominfo = self.domain_get(dom)
if not dominfo: return None
devs = dominfo.get_devices('vif')
return range(0, len(devs))
def domain_vif_get(self, dom, vif):
dominfo = self.domain_get(dom)
if not dominfo: return None
return dominfo.get_device_by_index(vif)
def domain_vif_stats(self, dom, vif):
dom = int(dom)
return xc.vif_stats_get(dom=dom, vif=vif)
def domain_vif_ip_add(self, dom, vif, ip):
dom = int(dom)
return xenctl.ip.setup_vfr_rules_for_vif(dom, vif, ip)
def domain_vif_scheduler_set(self, dom, vif, bytes, usecs):
dom = int(dom)
return xc.xc_vif_scheduler_set(dom=dom, vif=vif,
credit_bytes=bytes, credit_usecs=usecs)
def domain_vif_scheduler_get(self, dom, vif):
dom = int(dom)
return xc.vif_scheduler_get(dom=dom, vif=vif)
def domain_vbd_ls(self, dom):
dominfo = self.domain_get(dom)
if not dominfo: return []
devs = dominfo.get_devices('vbd')
return [ sxp.child_value(v, 'dev') for v in devs ]
def domain_vbd_get(self, dom, vbd):
dominfo = self.domain_get(dom)
if not dominfo: return None
devs = dominfo.get_devices('vbd')
for v in devs:
if sxp.child_value(v, 'dev') == vbd:
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_shadow_control(self, dom, op):
dom = int(dom)
return xc.shadow_control(dom, op)
#============================================================================
def instance():
global inst
try:
inst
except:
inst = XendDomain()
return inst
+44
View File
@@ -0,0 +1,44 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Handler for persistent domain configs.
"""
import sxp
import XendDB
import XendDomain
__all__ = [ "XendDomainConfig" ]
class XendDomainConfig:
dbpath = 'config'
def __init__(self):
self.db = XendDB.XendDB(self.dbpath)
def domain_config_ls(self, path):
return self.db.ls(path)
def domain_config_create(self, path, sxpr):
self.db.save(path, sxpr)
pass
def domain_config_delete(self, path):
self.db.delete(path)
def domain_config_instance(self, path):
"""Create a domain from a config.
"""
config = self.db.fetch(path)
xd = XendDomain.instance()
newdom = xd.domain_create(config)
return newdom
def instance():
global inst
try:
inst
except:
inst = XendDomainConfig()
return inst
+697
View File
@@ -0,0 +1,697 @@
#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Representation of a single domain.
Includes support for domain construction, using
open-ended configurations.
Author: Mike Wray <mike.wray@hpl.hp.com>
"""
import sys
import os
from twisted.internet import defer
import Xc; xc = Xc.new()
import xenctl.ip
import xenctl.vdisk
import sxp
import XendConsole
xendConsole = XendConsole.instance()
import server.SrvConsoleServer
xend = server.SrvConsoleServer.instance()
class VmError(ValueError):
"""Vm construction error."""
def __init__(self, value):
self.value = value
def __str__(self):
return self.value
class XendDomainInfo:
"""Virtual machine object."""
def __init__(self, config, dom, name, memory, image=None, console=None):
"""Construct a virtual machine object.
config configuration
dom domain id
name name
memory memory size (in MB)
image image object
"""
#todo: add info: runtime, state, ...
self.config = config
self.id = str(dom)
self.dom = dom
self.name = name
self.memory = memory
self.image = image
self.console = console
self.devices = {}
self.configs = []
self.info = None
#todo: state: running, suspended
self.state = 'running'
#todo: set to migrate info if migrating
self.migrate = None
def update(self, info):
"""Update with info from xc.domain_getinfo().
"""
self.info = info
def __str__(self):
s = "domain"
s += " id=" + self.id
s += " name=" + self.name
s += " memory=" + str(self.memory)
if self.console:
s += " console=" + self.console.id
if self.image:
s += " image=" + self.image
s += ""
return s
__repr__ = __str__
def sxpr(self):
sxpr = ['domain',
['id', self.id],
['name', self.name],
['memory', self.memory] ]
if self.info:
run = (self.info['running'] and 'r') or '-'
stop = (self.info['stopped'] and 's') or '-'
state = run + state
sxpr.append(['cpu', self.info['cpu']])
sxpr.append(['state', state])
sxpr.append(['cpu_time', self.info['cpu_time']/1e8])
if self.console:
sxpr.append(self.console.sxpr())
if self.config:
sxpr.append(['config', self.config])
return sxpr
def add_device(self, type, dev):
"""Add a device to a virtual machine.
dev device to add
"""
dl = self.devices.get(type, [])
dl.append(dev)
self.devices[type] = dl
def get_devices(self, type):
val = self.devices.get(type, [])
print 'get_devices', type; sxp.show(val); print
return val
def get_device_by_id(self, type, id):
"""Get the device with the given id.
id device id
returns device or None
"""
return sxp.child_with_id(self.get_devices(type), id)
def get_device_by_index(self, type, idx):
dl = self.get_devices(type)
if 0 <= idx < len(dl):
return dl[idx]
else:
return None
def add_config(self, val):
"""Add configuration data to a virtual machine.
val data to add
"""
self.configs.append(val)
def destroy(self):
if self.dom <= 0:
return 0
return xc.domain_destroy(dom=self.dom, force=1)
def show(self):
"""Print virtual machine info.
"""
print "[VM dom=%d name=%s memory=%d" % (self.dom, self.name, self.memory)
print "image:"
sxp.show(self.image)
print
for dl in self.devices:
for dev in dl:
print "device:"
sxp.show(dev)
print
for val in self.configs:
print "config:"
sxp.show(val)
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 make_disk(dom, uname, dev, mode, sharing):
"""Create a virtual disk device for a domain.
@returns Deferred
"""
segments = xenctl.vdisk.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)
ctrl = xend.blkif_create(dom)
def fn(ctrl):
return xend.blkif_dev_create(dom, vdev, mode, segment)
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.
@returns Deferred
"""
xend.netif_create(dom)
d = xend.netif_dev_create(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
IP_NONLOCAL_BIND = '/proc/sys/net/ipv4/ip_nonlocal_bind'
def get_ip_nonlocal_bind():
return int(open(IP_NONLOCAL_BIND, 'r').read()[0])
def set_ip_nonlocal_bind(v):
print >> open(IP_NONLOCAL_BIND, 'w'), str(v)
def link_local(ip):
return xenctl.ip.check_subnet(ip, '169.254.0.0', '255.255.0.0')
def arping(ip, gw):
cmd = '/usr/sbin/arping -A -b -I eth0 -c 1 -s %s %s' % (ip, gw)
print cmd
os.system(cmd)
gateway = xenctl.ip.get_current_ipgw() or '255.255.255.255'
nlb = get_ip_nonlocal_bind()
if not nlb: set_ip_nonlocal_bind(1)
try:
for ip in iplist:
if not link_local(ip):
arping(ip, gateway)
finally:
if not nlb: set_ip_nonlocal_bind(0)
def xen_domain_create(config, ostype, name, memory, kernel, ramdisk, cmdline, vifs_n):
if not os.path.isfile(kernel):
raise VmError('Kernel image does not exist: %s' % kernel)
if ramdisk and not os.path.isfile(ramdisk):
raise VMError('Kernel ramdisk does not exist: %s' % ramdisk)
cpu = int(sxp.child_value(config, 'cpu', '-1'))
dom = xc.domain_create(mem_kb= memory * 1024, name= name, cpu= cpu)
if dom <= 0:
raise VmError('Creating domain failed: name=%s memory=%d kernel=%s'
% (name, memory, kernel))
console = xendConsole.console_create(dom)
buildfn = getattr(xc, '%s_build' % ostype)
err = buildfn(dom = dom,
image = kernel,
control_evtchn = console.port2,
cmdline = cmdline,
ramdisk = ramdisk)
if err != 0:
raise VmError('Building domain failed: type=%s dom=%d err=%d'
% (ostype, dom, err))
vm = XendDomainInfo(config, dom, name, memory, kernel, console)
return vm
config_handlers = {}
def add_config_handler(name, h):
"""Add a handler for a config field.
name field name
h handler: fn(vm, config, field, index)
"""
config_handlers[name] = h
def get_config_handler(name):
"""Get a handler for a config field.
returns handler or None
"""
return config_handlers.get(name)
"""Table of handlers for virtual machine images.
Indexed by image type.
"""
image_handlers = {}
def add_image_handler(name, h):
"""Add a handler for an image type
name image type
h handler: fn(config, name, memory, image)
"""
image_handlers[name] = h
def get_image_handler(name):
"""Get the handler for an image type.
name image type
returns handler or None
"""
return image_handlers.get(name)
"""Table of handlers for devices.
Indexed by device type.
"""
device_handlers = {}
def add_device_handler(name, h):
"""Add a handler for a device type.
name device type
h handler: fn(vm, dev)
"""
device_handlers[name] = h
def get_device_handler(name):
"""Get the handler for a device type.
name device type
returns handler or None
"""
return device_handlers.get(name)
def vm_create(config):
"""Create a VM from a configuration.
If a vm has been partially created and there is an error it
is destroyed.
config configuration
returns Deferred
raises VmError for invalid configuration
"""
# todo - add support for scheduling params?
print 'vm_create>'
xenctl.vdisk.VBD_EXPERT_MODE = 0
vm = None
try:
name = sxp.child_value(config, 'name')
memory = int(sxp.child_value(config, 'memory', '128'))
image = sxp.child_value(config, 'image')
image_name = sxp.name(image)
image_handler = get_image_handler(image_name)
if image_handler is None:
raise VmError('unknown image type: ' + image_name)
vm = image_handler(config, name, memory, image)
deferred = vm_configure(vm, config)
except StandardError, ex:
# Catch errors, cleanup and re-raise.
if vm:
vm.destroy()
raise
def cbok(x):
print 'vm_create> cbok', x
return x
deferred.addCallback(cbok)
print 'vm_create<'
return deferred
def vm_restore(src, config, progress=0):
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)
return deferred
def dom_get(dom):
domlist = xc.domain_getinfo(dom=dom)
if domlist and dom == domlist[0]['dom']:
return domlist[0]
return None
def dom_configure(dom, config):
d = dom_get(dom)
if not d:
raise VMError("Domain not found: %d" % dom)
try:
name = d['name']
memory = d['memory']/1024
image = None
vm = VM(config, dom, name, memory, image)
deferred = vm_configure(vm, config)
except StandardError, ex:
if vm:
vm.destroy()
raise
return deferred
def append_deferred(dlist, v):
if isinstance(v, defer.Deferred):
dlist.append(v)
def vm_create_devices(vm, config):
"""Create the devices for a vm.
vm virtual machine
config configuration
returns Deferred
raises VmError for invalid devices
"""
print '>vm_create_devices'
dlist = []
devices = sxp.children(config, 'device')
index = {}
for d in devices:
dev = sxp.child0(d)
if dev is None:
raise VmError('invalid device')
dev_name = sxp.name(dev)
dev_index = index.get(dev_name, 0)
dev_handler = get_device_handler(dev_name)
if dev_handler is None:
raise VmError('unknown device type: ' + dev_name)
v = dev_handler(vm, dev, dev_index)
append_deferred(dlist, v)
index[dev_name] = dev_index + 1
deferred = defer.DeferredList(dlist, fireOnOneErrback=1)
print '<vm_create_devices'
return deferred
def vm_configure(vm, config):
"""Configure a vm.
vm virtual machine
config configuration
returns Deferred - calls callback with vm
"""
d = xend.blkif_create(vm.dom)
d.addCallback(_vm_configure1, vm, config)
return d
def _vm_configure1(val, vm, config):
d = vm_create_devices(vm, config)
print '_vm_configure1> made devices...'
def cbok(x):
print '_vm_configure1> cbok', x
return x
d.addCallback(cbok)
d.addCallback(_vm_configure2, vm, config)
print '_vm_configure1<'
return d
def _vm_configure2(val, vm, config):
print '>callback _vm_configure2...'
dlist = []
index = {}
for field in sxp.children(config):
field_name = sxp.name(field)
field_index = index.get(field_name, 0)
field_handler = get_config_handler(field_name)
# Ignore unknown fields. Warn?
if field_handler:
v = field_handler(vm, config, field, field_index)
append_deferred(dlist, v)
index[field_name] = field_index + 1
d = defer.DeferredList(dlist, fireOnOneErrback=1)
def cbok(results):
print '_vm_configure2> cbok', results
return vm
def cberr(err):
print '_vm_configure2> cberr', err
vm.destroy()
return err
d.addCallback(cbok)
d.addErrback(cberr)
print '<_vm_configure2'
return d
def config_devices(config, name):
"""Get a list of the 'device' nodes of a given type from a config.
config configuration
name device type
return list of device configs
"""
devices = []
for d in sxp.children(config, 'device'):
dev = sxp.child0(d)
if dev is None: continue
if name == sxp.name(dev):
devices.append(dev)
return devices
def vm_image_linux(config, name, memory, image):
"""Create a VM for a linux image.
name vm name
memory vm memory
image image config
returns vm
"""
kernel = sxp.child_value(image, "kernel")
cmdline = ""
ip = sxp.child_value(image, "ip", "dhcp")
if ip:
cmdline += " ip=" + ip
root = sxp.child_value(image, "root")
if root:
cmdline += " root=" + root
args = sxp.child_value(image, "args")
if args:
cmdline += " " + args
ramdisk = sxp.child_value(image, "ramdisk", '')
vifs = config_devices(config, "vif")
vm = xen_domain_create(config, "linux", name, memory, kernel,
ramdisk, cmdline, len(vifs))
return vm
def vm_image_netbsd(config, name, memory, image):
"""Create a VM for a bsd image.
name vm name
memory vm memory
image image config
returns vm
"""
#todo: Same as for linux. Is that right? If so can unify them.
kernel = sxp.child_value(image, "kernel")
cmdline = ""
ip = sxp.child_value(image, "ip", "dhcp")
if ip:
cmdline += "ip=" + ip
root = sxp.child_value(image, "root")
if root:
cmdline += "root=" + root
args = sxp.child_value(image, "args")
if args:
cmdline += " " + args
ramdisk = sxp.child_value(image, "ramdisk")
vifs = config_devices(config, "vif")
vm = xen_domain_create(config, "netbsd", name, memory, kernel,
ramdisk, cmdline, len(vifs))
return vm
def vm_dev_vif(vm, val, index):
"""Create a virtual network interface (vif).
vm virtual machine
val vif config
index vif index
"""
vif = index #todo
vmac = sxp.child_value(val, "mac")
defer = make_vif(vm.dom, vif, vmac)
def fn(id):
dev = val + ['vif', vif]
vm.add_device('vif', dev)
print 'vm_dev_vif> created', dev
return id
defer.addCallback(fn)
return defer
def vm_dev_vbd(vm, val, index):
"""Create a virtual block device (vbd).
vm virtual machine
val vbd config
index vbd index
"""
uname = sxp.child_value(val, 'uname')
if not uname:
raise VMError('vbd: Missing uname')
dev = sxp.child_value(val, 'dev')
if not dev:
raise VMError('vbd: Missing dev')
mode = sxp.child_value(val, 'mode', 'r')
sharing = sxp.child_value(val, 'sharing', 'rr')
defer = make_disk(vm.dom, uname, dev, mode, sharing)
def fn(vbd):
vm.add_device('vbd', val)
return vbd
defer.addCallback(fn)
return defer
def vm_dev_pci(vm, val, index):
bus = sxp.child_value(val, 'bus')
if not bus:
raise VMError('pci: Missing bus')
dev = sxp.child_value(val, 'dev')
if not dev:
raise VMError('pci: Missing dev')
func = sxp.child_value(val, 'func')
if not func:
raise VMError('pci: Missing func')
rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev, func=func, enable=1)
if rc < 0:
#todo non-fatal
raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
(bus, dev, func))
return rc
def vm_field_vfr(vm, config, val, index):
"""Handle a vfr field in a config.
vm virtual machine
config vm config
val vfr field
"""
# Get the rules and add them.
# (vfr (vif (id foo) (ip x.x.x.x)) ... )
list = sxp.children(val, 'vif')
for v in list:
id = sxp.child_value(v, 'id')
if id is None:
raise VmError('vfr: missing vif id')
id = int(id)
dev = vm.get_device_by_index('vif', id)
if not dev:
raise VmError('vfr: invalid vif id %d' % id)
vif = sxp.child_value(dev, 'vif')
ip = sxp.child_value(v, 'ip')
if not ip:
raise VmError('vfr: missing ip address')
#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)
def vnet_bridge(vnet, vmac, dom, idx):
"""Add the device for the vif to the bridge for its vnet.
"""
vif = "vif%d.%d" % (dom, idx)
try:
cmd = "(vif.conn (vif %s) (vnet %s) (vmac %s))" % (vif, vnet, vmac)
print "*** vnet_bridge>", cmd
out = file("/proc/vnet/policy", "wb")
out.write(cmd)
err = out.close()
print "vnet_bridge>", "err=", err
except IOError, ex:
print "vnet_bridge>", ex
def vm_field_vnet(vm, config, val, index):
"""Handle a vnet field in a config.
vm virtual machine
config vm config
val vnet field
index index
"""
# Get the vif children. For each vif look up the vif device
# with the given id and configure its vnet.
# (vnet (vif (id foo) (vnet 2) (mac x:x:x:x:x:x)) ... )
vif_vnets = sxp.children(val, 'vif')
for v in vif_vnets:
id = sxp.child_value(v, 'id')
if id is None:
raise VmError('vnet: missing vif id')
dev = vm.get_device_by_id('vif', id)
if not sxp.elementp(dev, 'vif'):
raise VmError('vnet: invalid vif id %s' % id)
vnet = sxp.child_value(v, 'vnet', 1)
mac = sxp.child_value(dev, 'mac')
vif = sxp.child_value(dev, 'vif')
vnet_bridge(vnet, mac, vm.dom, 0)
vm.add_config([ 'vif.vnet', ['id', id], ['vnet', vnet], ['mac', mac]])
# Register image handlers for linux and bsd.
add_image_handler('linux', vm_image_linux)
add_image_handler('netbsd', vm_image_netbsd)
# Register device handlers for vifs and vbds.
add_device_handler('vif', vm_dev_vif)
add_device_handler('vbd', vm_dev_vbd)
add_device_handler('pci', vm_dev_pci)
# Register config handlers for vfr and vnet.
add_config_handler('vfr', vm_field_vfr)
add_config_handler('vnet', vm_field_vnet)
+103
View File
@@ -0,0 +1,103 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
import sys
import socket
import sxp
import XendDB
import EventServer; eserver = EventServer.instance()
class XendMigrateInfo:
# states: begin, active, failed, succeeded?
def __init__(self, id, dom, dst):
self.id = id
self.state = 'begin'
self.src_host = socket.gethostname()
self.src_dom = dom
self.dst_host = dst
self.dst_dom = None
def set_state(self, state):
self.state = state
def get_state(self):
return self.state
def sxpr(self):
sxpr = ['migrate', ['id', self.id], ['state', self.state] ]
sxpr_src = ['src', ['host', self.src_host], ['domain', self.src_dom] ]
sxpr.append(sxpr_src)
sxpr_dst = ['dst', ['host', self.dst] ]
if self.dst_dom:
sxpr_dst.append(['domain', self.dst_dom])
sxpr.append(sxpr_dst)
return sxpr
class XendMigrate:
# Represents migration in progress.
# Use log for indications of begin/end/errors?
# Need logging of: domain create/halt, migrate begin/end/fail
# Log via event server?
dbpath = "migrate"
def __init__(self):
self.db = XendDB.XendDB(self.dbpath)
self.migrate = {}
self.migrate_db = self.db.fetchall("")
self.id = 0
def nextid(self):
self.id += 1
return "%d" % self.id
def sync(self):
self.db.saveall("", self.migrate_db)
def sync_migrate(self, id):
self.db.save(id, self.migrate_db[id])
def close(self):
pass
def _add_migrate(self, id, info):
self.migrate[id] = info
self.migrate_db[id] = info.sxpr()
self.sync_migrate(id)
#eserver.inject('xend.migrate.begin', info.sxpr())
def _delete_migrate(self, id):
#eserver.inject('xend.migrate.end', id)
del self.migrate[id]
del self.migrate_db[id]
self.db.delete(id)
def migrate_ls(self):
return self.migrate.keys()
def migrates(self):
return self.migrate.values()
def migrate_get(self, id):
return self.migrate.get(id)
def migrate_begin(self, dom, dst):
# Check dom for existence, not migrating already.
# Create migrate info, tell xend to migrate it?
# - or fork migrate command ourselves?
# Subscribe to migrate notifications (for updating).
id = self.nextid()
info = XenMigrateInfo(id, dom, dst)
self._add_migrate(id, info)
return id
def instance():
global inst
try:
inst
except:
inst = XendMigrate()
return inst
+55
View File
@@ -0,0 +1,55 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Handler for node operations.
Has some persistent state:
- logs
- notification urls
"""
import Xc
class XendNodeInfo:
"""Node information record.
"""
def __init__(self):
pass
class XendNode:
def __init__(self):
self.xc = Xc.new()
def shutdown(self):
return 0
def reboot(self):
return 0
def notify(self, uri):
return 0
def cpu_bvt_slice_set(self, slice):
ret = 0
#ret = self.xc.bvtsched_global_set(ctx_allow=slice)
return ret
def cpu_bvt_slice_get(self, slice):
ret = 0
#ret = self.xc.bvtsched_global_get()
return ret
def cpu_rrobin_slice_set(self, slice):
ret = 0
#ret = self.xc.rrobin_global_set(slice)
return ret
def instance():
global inst
try:
inst
except:
inst = XendNode()
return inst
+156
View File
@@ -0,0 +1,156 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Xend root class.
Creates the event server and handles configuration.
"""
import os
import os.path
import sys
import EventServer
# Initial create of the event server.
eserver = EventServer.instance()
import sxp
def reboots():
"""Get a list of system reboots from wtmp.
"""
out = os.popen('/usr/bin/last reboot', 'r')
list = [ x.strip() for x in out if x.startswith('reboot') ]
return list
def last_reboot():
"""Get the last known system reboot.
"""
l = reboots()
return (l and l[-1]) or None
class XendRoot:
"""Root of the management classes."""
lastboot_default = "/etc/xen/xend/lastboot"
"""Default path to the root of the database."""
dbroot_default = "/etc/xen/xend/xenmgr-db"
"""Default path to the config file."""
config_default = "/etc/xen/xenmgr-config.sxp"
"""Environment variable used to override config_default."""
config_var = "XEND_CONFIG"
def __init__(self):
self.rebooted = 0
self.last_reboot = None
self.dbroot = None
self.config_path = None
self.config = None
self.configure()
self.check_lastboot()
eserver.subscribe('xend.*', self.event_handler)
#eserver.subscribe('xend.domain.created', self.event_handler)
#eserver.subscribe('xend.domain.died', self.event_handler)
def start(self):
eserver.inject('xend.start', self.rebooted)
def event_handler(self, event, val):
print >> sys.stderr, "EVENT>", event, val
def read_lastboot(self):
try:
val = file(self.lastboot, 'rb').readlines()[0]
except StandardError, ex:
print 'warning: Error reading', self.lastboot, ex
val = None
return val
def write_lastboot(self, val):
if not val: return
try:
fdir = os.path.dirname(self.lastboot)
if not os.path.isdir(fdir):
os.makedirs(fdir)
out = file(self.lastboot, 'wb+')
out.write(val)
out.close()
except IOError, ex:
print 'warning: Error writing', self.lastboot, ex
pass
def check_lastboot(self):
"""Check if there has been a system reboot since we saved lastboot.
"""
last_val = self.read_lastboot()
this_val = last_reboot()
if this_val == last_val:
self.rebooted = 0
else:
self.rebooted = 1
self.write_lastboot(this_val)
self.last_reboot = this_val
def get_last_reboot(self):
return self.last_reboot
def get_rebooted(self):
return self.rebooted
def configure(self):
self.set_config()
self.dbroot = self.get_config_value("dbroot", self.dbroot_default)
self.lastboot = self.get_config_value("lastboot", self.lastboot_default)
def get_dbroot(self):
"""Get the path to the database root.
"""
return self.dbroot
def set_config(self):
"""If the config file exists, read it. If not, ignore it.
The config file is a sequence of sxp forms.
"""
self.config_path = os.getenv(self.config_var, self.config_default)
if os.path.exists(self.config_path):
fin = file(self.config_path, 'rb')
try:
config = sxp.parse(fin)
config.insert(0, 'config')
self.config = config
finally:
fin.close()
else:
self.config = ['config']
def get_config(self, name=None):
"""Get the configuration element with the given name, or
the whole configuration if no name is given.
name element name (optional)
returns config or none
"""
if name is None:
val = self.config
else:
val = sxp.child(self.config, name)
return val
def get_config_value(self, name, val=None):
"""Get the value of an atomic configuration element.
name element name
val default value (optional, defaults to None)
returns value
"""
return sxp.child_value(self.config, name, val=val)
def instance():
global inst
try:
inst
except:
inst = XendRoot()
return inst
+111
View File
@@ -0,0 +1,111 @@
# 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
+69
View File
@@ -0,0 +1,69 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Handler for vnet operations.
"""
import sxp
import XendDB
class XendVnet:
"""Index of all vnets. Singleton.
"""
dbpath = "vnet"
def __init__(self):
# Table of vnet info indexed by vnet id.
self.vnet = {}
self.db = XendDB.XendDB(self.dbpath)
self.vnet = self.db.fetchall("")
def vnet_ls(self):
"""List all vnets.
"""
return self.vnet.keys()
def vnets(self):
return self.vnet.values()
def vnet_get(self, id):
"""Get a vnet.
id vnet id
"""
return self.vnet.get(id)
def vnet_create(self, info):
"""Create a vnet.
info config
"""
self.vnet_configure(info)
def vnet_configure(self, info):
"""Configure a vnet.
id vnet id
info config
"""
# Need to configure for real.
# Only sync if succeeded - otherwise need to back out.
self.vnet[info.id] = info
self.db.save(info.id, info)
def vnet_delete(self, id):
"""Delete a vnet.
id vnet id
"""
# Need to delete for real. What if fails?
if id in self.vnet:
del self.vnet[id]
self.db.delete(id)
def instance():
global inst
try:
inst
except:
inst = XendVnet()
return inst
+1
View File
@@ -0,0 +1 @@
+165
View File
@@ -0,0 +1,165 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Encoding for arguments to HTTP calls.
Uses the url-encoding with MIME type 'application/x-www-form-urlencoded'
if the data does not include files. Otherwise it uses the encoding with
MIME type 'multipart/form-data'. See the HTML4 spec for details.
"""
import sys
import types
from StringIO import StringIO
import urllib
import httplib
import random
import md5
# Extract from HTML4 spec.
## The following example illustrates "multipart/form-data"
## encoding. Suppose we have the following form:
## <FORM action="http://server.com/cgi/handle"
## enctype="multipart/form-data"
## method="post">
## <P>
## What is your name? <INPUT type="text" name="submit-name"><BR>
## What files are you sending? <INPUT type="file" name="files"><BR>
## <INPUT type="submit" value="Send"> <INPUT type="reset">
## </FORM>
## If the user enters "Larry" in the text input, and selects the text
## file "file1.txt", the user agent might send back the following data:
## Content-Type: multipart/form-data; boundary=AaB03x
## --AaB03x
## Content-Disposition: form-data; name="submit-name"
## Larry
## --AaB03x
## Content-Disposition: form-data; name="files"; filename="file1.txt"
## Content-Type: text/plain
## ... contents of file1.txt ...
## --AaB03x--
## If the user selected a second (image) file "file2.gif", the user agent
## might construct the parts as follows:
## Content-Type: multipart/form-data; boundary=AaB03x
## --AaB03x
## Content-Disposition: form-data; name="submit-name"
## Larry
## --AaB03x
## Content-Disposition: form-data; name="files"
## Content-Type: multipart/mixed; boundary=BbC04y
## --BbC04y
## Content-Disposition: file; filename="file1.txt"
## Content-Type: text/plain
## ... contents of file1.txt ...
## --BbC04y
## Content-Disposition: file; filename="file2.gif"
## Content-Type: image/gif
## Content-Transfer-Encoding: binary
## ...contents of file2.gif...
## --BbC04y--
## --AaB03x--
__all__ = ['encode_data', 'encode_multipart', 'encode_form', 'mime_boundary' ]
def data_values(d):
if isinstance(d, types.DictType):
return d.items()
else:
return d
def encode_data(d):
"""Encode some data for HTTP transport.
The encoding used is stored in 'Content-Type' in the headers.
d data - sequence of tuples or dictionary
returns a 2-tuple of the headers and the encoded data
"""
val = ({}, None)
if d is None: return val
multipart = 0
for (k, v) in data_values(d):
if encode_isfile(v):
multipart = 1
break
if multipart:
val = encode_multipart(d)
else:
val = encode_form(d)
return val
def encode_isfile(v):
if isinstance(v, types.FileType):
return 1
if hasattr(v, 'readlines'):
return 1
return 0
def encode_multipart(d):
boundary = mime_boundary()
hdr = { 'Content-Type': 'multipart/form-data; boundary=' + boundary }
out = StringIO()
for (k,v) in data_values(d):
out.write('--')
out.write(boundary)
out.write('\r\n')
if encode_isfile(v):
out.write('Content-Disposition: form-data; name="')
out.write(k)
if hasattr(v, 'name'):
out.write('"; filename="')
out.write(v.name)
out.write('"\r\n')
out.write('Content-Type: application/octet-stream\r\n')
out.write('\r\n')
for l in v.readlines():
out.write(l)
else:
out.write('Content-Disposition: form-data; name="')
out.write(k)
out.write('"\r\n')
out.write('\r\n')
out.write(str(v))
out.write('\r\n')
out.write('--')
out.write(boundary)
out.write('--')
out.write('\r\n')
return (hdr, out.getvalue())
def mime_boundary():
random.seed()
m = md5.new()
for i in range(0, 10):
c = chr(random.randint(1, 255))
m.update(c)
b = m.hexdigest()
return b[0:16]
def encode_form(d):
hdr = { 'Content-Type': 'application/x-www-form-urlencoded' }
val = urllib.urlencode(d)
return (hdr, val)
def main():
#d = {'a': 1, 'b': 'x y', 'c': file('conf.sxp') }
#d = {'a': 1, 'b': 'x y' }
d = [ ('a', 1), ('b', 'x y'), ('c', file('conf.sxp')) ]
#d = [ ('a', 1), ('b', 'x y')]
v = encode_data(d)
print v[0]
sys.stdout.write(v[1])
print
if __name__ == "__main__":
main()
+137
View File
@@ -0,0 +1,137 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
import cgi
import os
import sys
import types
import StringIO
from twisted.internet import defer
from twisted.internet import reactor
from twisted.web import error
from twisted.web import resource
from twisted.web import server
from xenmgr import sxp
from xenmgr import PrettyPrint
def uri_pathlist(p):
"""Split a path into a list.
p path
return list of path elements
"""
l = []
for x in p.split('/'):
if x == '': continue
l.append(x)
return l
class SrvBase(resource.Resource):
"""Base class for services.
"""
def parse_form(self, req, method):
"""Parse the data for a request, GET using the URL, POST using encoded data.
Posts should use enctype='multipart/form-data' in the <form> tag,
rather than 'application/x-www-form-urlencoded'. Only 'multipart/form-data'
handles file upload.
req request
returns a cgi.FieldStorage instance
"""
env = {}
env['REQUEST_METHOD'] = method
if self.query:
env['QUERY_STRING'] = self.query
val = cgi.FieldStorage(fp=req.rfile, headers=req.headers, environ=env)
return val
def use_sxp(self, req):
"""Determine whether to send an SXP response to a request.
Uses SXP if there is no User-Agent, no Accept, or application/sxp is in Accept.
req request
returns 1 for SXP, 0 otherwise
"""
ok = 0
user_agent = req.getHeader('User-Agent')
accept = req.getHeader('Accept')
if (not user_agent) or (not accept) or (accept.find(sxp.mime_type) >= 0):
ok = 1
return ok
def get_op_method(self, op):
"""Get the method for an operation.
For operation 'foo' looks for 'op_foo'.
op operation name
returns method or None
"""
op_method_name = 'op_' + op
return getattr(self, op_method_name, None)
def perform(self, req):
"""General operation handler for posted operations.
For operation 'foo' looks for a method op_foo and calls
it with op_foo(op, req). Replies with code 500 if op_foo
is not found.
The method must return a list when req.use_sxp is true
and an HTML string otherwise (or list).
Methods may also return a Deferred (for incomplete processing).
req request
"""
op = req.args.get('op')
if op is None or len(op) != 1:
req.setResponseCode(404, "Invalid")
return ''
op = op[0]
op_method = self.get_op_method(op)
if op_method is None:
req.setResponseCode(501, "Not implemented")
req.setHeader("Content-Type", "text/plain")
req.write("Not implemented: " + op)
return ''
else:
val = op_method(op, req)
if isinstance(val, defer.Deferred):
val.addCallback(self._cb_perform, req, 1)
return server.NOT_DONE_YET
else:
self._cb_perform(val, req, 0)
return ''
def _cb_perform(self, val, req, dfr):
"""Callback to complete the request.
May be called from a Deferred.
"""
if isinstance(val, error.ErrorPage):
req.write(val.render(req))
elif self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
sxp.show(val, req)
else:
req.write('<html><head></head><body>')
self.print_path(req)
if isinstance(val, types.ListType):
req.write('<code><pre>')
PrettyPrint.prettyprint(val, out=req)
req.write('</pre></code>')
else:
req.write(str(val))
req.write('</body></html>')
if dfr:
req.finish()
def print_path(self, req):
"""Print the path with hyperlinks.
"""
pathlist = [x for x in req.prepath if x != '' ]
s = "/"
req.write('<h1><a href="/">/</a>')
for x in pathlist:
s += x + "/"
req.write(' <a href="%s">%s</a>/' % (s, x))
req.write("</h1>")
+42
View File
@@ -0,0 +1,42 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from xenmgr import sxp
from xenmgr import XendConsole
from SrvDir import SrvDir
class SrvConsole(SrvDir):
"""An individual console.
"""
def __init__(self, info):
SrvDir.__init__(self)
self.info = info
self.xc = XendConsole.instance()
def op_disconnect(self, op, req):
val = self.xc.console_disconnect(self.info.id)
return val
def render_POST(self, req):
return self.perform(req)
def render_GET(self, req):
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
sxp.show(self.info.sxpr(), out=req)
else:
req.write('<html><head></head><body>')
self.print_path(req)
#self.ls()
req.write('<p>%s</p>' % self.info)
req.write('<p><a href="%s">Connect to domain %d</a></p>'
% (self.info.uri(), self.info.dom2))
self.form(req)
req.write('</body></html>')
return ''
def form(self, req):
req.write('<form method="post" action="%s">' % req.prePathURL())
if self.info.connection():
req.write('<input type="submit" name="op" value="disconnect">')
req.write('</form>')
+58
View File
@@ -0,0 +1,58 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from SrvDir import SrvDir
from SrvConsole import SrvConsole
from xenmgr import XendConsole
from xenmgr import sxp
class SrvConsoleDir(SrvDir):
"""Console directory.
"""
def __init__(self):
SrvDir.__init__(self)
self.xconsole = XendConsole.instance()
def console(self, x):
val = None
try:
info = self.xconsole.console_get(x)
val = SrvConsole(info)
except KeyError:
pass
return val
def get(self, x):
v = SrvDir.get(self, x)
if v is not None:
return v
v = self.console(x)
return v
def render_GET(self, req):
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
self.ls_console(req, 1)
else:
req.write("<html><head></head><body>")
self.print_path(req)
self.ls(req)
self.ls_console(req)
#self.form(req.wfile)
req.write("</body></html>")
return ''
def ls_console(self, req, use_sxp=0):
url = req.prePathURL()
if not url.endswith('/'):
url += '/'
if use_sxp:
consoles = self.xconsole.console_ls()
sxp.show(consoles, out=req)
else:
consoles = self.xconsole.consoles()
consoles.sort(lambda x, y: cmp(x.id, y.id))
req.write('<ul>')
for c in consoles:
req.write('<li><a href="%s%s"> %s</a></li>' % (url, c.id, c))
req.write('</ul>')
+631
View File
@@ -0,0 +1,631 @@
###########################################################
## Xen controller daemon
## Copyright (c) 2004, K A Fraser (University of Cambridge)
## Copyright (C) 2004, Mike Wray <mike.wray@hp.com>
###########################################################
import os
import os.path
import signal
import sys
import socket
import pwd
import re
import StringIO
from twisted.internet import pollreactor
pollreactor.install()
from twisted.internet import reactor
from twisted.internet import protocol
from twisted.internet import abstract
from twisted.internet import defer
import xend.utils
from xenmgr import sxp
from xenmgr import PrettyPrint
from xenmgr import EventServer
eserver = EventServer.instance()
from xenmgr.server import SrvServer
import channel
import blkif
import netif
import console
from params import *
DEBUG = 1
class MgmtProtocol(protocol.DatagramProtocol):
"""Handler for the management socket (unix-domain).
"""
def __init__(self, daemon):
#protocol.DatagramProtocol.__init__(self)
self.daemon = daemon
def write(self, data, addr):
return self.transport.write(data, addr)
def datagramReceived(self, data, addr):
if DEBUG: print 'datagramReceived> addr=', addr, 'data=', data
io = StringIO.StringIO(data)
try:
vals = sxp.parse(io)
res = self.dispatch(vals[0])
self.send_result(addr, res)
except SystemExit:
raise
except:
if DEBUG:
raise
else:
self.send_error(addr)
def send_reply(self, addr, sxpr):
io = StringIO.StringIO()
sxp.show(sxpr, out=io)
io.seek(0)
self.write(io.getvalue(), addr)
def send_result(self, addr, res):
def fn(res, self=self, addr=addr):
self.send_reply(addr, ['ok', res])
if isinstance(res, defer.Deferred):
res.addCallback(fn)
else:
fn(res)
def send_error(self, addr):
(extype, exval) = sys.exc_info()[:2]
self.send_reply(addr, ['err',
['type', str(extype) ],
['value', str(exval) ] ] )
def opname(self, name):
"""Get the name of the method for an operation.
"""
return 'op_' + name.replace('.', '_')
def operror(self, name, v):
"""Default operation handler - signals an error.
"""
raise NotImplementedError('Invalid operation: ' +name)
def dispatch(self, req):
"""Dispatch a request to its handler.
"""
op_name = sxp.name(req)
op_method_name = self.opname(op_name)
op_method = getattr(self, op_method_name, self.operror)
return op_method(op_name, req)
def op_console_create(self, name, req):
"""Create a new control interface - console for a domain.
"""
print name, req
dom = sxp.child_value(req, 'domain')
if not dom: raise ValueError('Missing domain')
dom = int(dom)
console_port = sxp.child_value(req, 'console_port')
if console_port:
console_port = int(console_port)
resp = self.daemon.console_create(dom, console_port)
print name, resp
return resp
def op_consoles(self, name, req):
"""Get a list of the consoles.
"""
return self.daemon.consoles()
def op_console_disconnect(self, name, req):
id = sxp.child_value(req, 'id')
if not id:
raise ValueError('Missing console id')
id = int(id)
console = self.daemon.get_console(id)
if not console:
raise ValueError('Invalid console id')
if console.conn:
console.conn.loseConnection()
return ['ok']
def op_blkifs(self, name, req):
pass
def op_blkif_devs(self, name, req):
pass
def op_blkif_create(self, name, req):
pass
def op_blkif_dev_create(self, name, req):
pass
def op_netifs(self, name, req):
pass
def op_netif_devs(self, name, req):
pass
def op_netif_create(self, name, req):
pass
def op_netif_dev_create(self, name, req):
pass
class NotifierProtocol(protocol.Protocol):
"""Asynchronous handler for i/o on the notifier (event channel).
"""
def __init__(self, channelFactory):
self.channelFactory = channelFactory
def notificationReceived(self, idx, type):
#print 'NotifierProtocol>notificationReceived>', idx, type
channel = self.channelFactory.getChannel(idx)
if not channel:
return
#print 'NotifierProtocol>notificationReceived> channel', channel
channel.notificationReceived(type)
def connectionLost(self, reason=None):
pass
def doStart(self):
pass
def doStop(self):
pass
def startProtocol(self):
pass
def stopProtocol(self):
pass
class NotifierPort(abstract.FileDescriptor):
"""Transport class for the event channel.
"""
def __init__(self, daemon, notifier, proto, reactor=None):
assert isinstance(proto, NotifierProtocol)
abstract.FileDescriptor.__init__(self, reactor)
self.daemon = daemon
self.notifier = notifier
self.protocol = proto
def startListening(self):
self._bindNotifier()
self._connectToProtocol()
def stopListening(self):
if self.connected:
result = self.d = defer.Deferred()
else:
result = None
self.loseConnection()
return result
def fileno(self):
return self.notifier.fileno()
def _bindNotifier(self):
self.connected = 1
def _connectToProtocol(self):
self.protocol.makeConnection(self)
self.startReading()
def loseConnection(self):
if self.connected:
self.stopReading()
self.disconnecting = 1
reactor.callLater(0, self.connectionLost)
def connectionLost(self, reason=None):
abstract.FileDescriptor.connectionLost(self, reason)
if hasattr(self, 'protocol'):
self.protocol.doStop()
self.connected = 0
#self.notifier.close() # Not implemented.
os.close(self.fileno())
del self.notifier
if hasattr(self, 'd'):
self.d.callback(None)
del self.d
def doRead(self):
#print 'NotifierPort>doRead>', self
count = 0
while 1:
#print 'NotifierPort>doRead>', count
notification = self.notifier.read()
if not notification:
break
(idx, type) = notification
self.protocol.notificationReceived(idx, type)
self.notifier.unmask(idx)
count += 1
#print 'NotifierPort>doRead<'
class EventProtocol(protocol.Protocol):
"""Asynchronous handler for a connected event socket.
"""
def __init__(self, daemon):
#protocol.Protocol.__init__(self)
self.daemon = daemon
# Event queue.
self.queue = []
# Subscribed events.
self.events = []
self.parser = sxp.Parser()
self.pretty = 0
# For debugging subscribe to everything and make output pretty.
self.subscribe(['*'])
self.pretty = 1
def dataReceived(self, data):
try:
self.parser.input(data)
if self.parser.ready():
val = self.parser.get_val()
res = self.dispatch(val)
self.send_result(res)
if self.parser.at_eof():
self.loseConnection()
except SystemExit:
raise
except:
if DEBUG:
raise
else:
self.send_error()
def connectionLost(self, reason=None):
self.unsubscribe()
def send_reply(self, sxpr):
io = StringIO.StringIO()
if self.pretty:
PrettyPrint.prettyprint(sxpr, out=io)
else:
sxp.show(sxpr, out=io)
print >> io
io.seek(0)
return self.transport.write(io.getvalue())
def send_result(self, res):
return self.send_reply(['ok', res])
def send_error(self):
(extype, exval) = sys.exc_info()[:2]
return self.send_reply(['err',
['type', str(extype)],
['value', str(exval)]])
def send_event(self, val):
return self.send_reply(['event', val[0], val[1]])
def unsubscribe(self):
for event in self.events:
eserver.unsubscribe(event, self.queue_event)
def subscribe(self, events):
self.unsubscribe()
for event in events:
eserver.subscribe(event, self.queue_event)
self.events = events
def queue_event(self, name, v):
# Despite the name we dont' queue the event here.
# We send it because the transport will queue it.
self.send_event([name, v])
def opname(self, name):
return 'op_' + name.replace('.', '_')
def operror(self, name, req):
raise NotImplementedError('Invalid operation: ' +name)
def dispatch(self, req):
op_name = sxp.name(req)
op_method_name = self.opname(op_name)
op_method = getattr(self, op_method_name, self.operror)
return op_method(op_name, req)
def op_help(self, name, req):
def nameop(x):
if x.startswith('op_'):
return x[3:].replace('_', '.')
else:
return x
l = [ nameop(k) for k in dir(self) if k.startswith('op_') ]
return l
def op_quit(self, name, req):
self.loseConnection()
def op_exit(self, name, req):
sys.exit(0)
def op_pretty(self, name, req):
self.pretty = 1
return ['ok']
def op_console_disconnect(self, name, req):
id = sxp.child_value(req, 'id')
if not id:
raise ValueError('Missing console id')
self.daemon.console_disconnect(id)
return ['ok']
def op_info(self, name, req):
val = self.daemon.consoles()
return val
def op_sys_subscribe(self, name, v):
# (sys.subscribe event*)
# Subscribe to the events:
self.subscribe(v[1:])
return ['ok']
def op_sys_inject(self, name, v):
# (sys.inject event)
event = v[1]
eserver.inject(sxp.name(event), event)
return ['ok']
class EventFactory(protocol.Factory):
"""Asynchronous handler for the event server socket.
"""
protocol = EventProtocol
service = None
def __init__(self, daemon):
#protocol.Factory.__init__(self)
self.daemon = daemon
def buildProtocol(self, addr):
proto = self.protocol(self.daemon)
proto.factory = self
return proto
class Daemon:
"""The xend daemon.
"""
def __init__(self):
self.shutdown = 0
def daemon_pids(self):
pids = []
pidex = '(?P<pid>\d+)'
pythonex = '(?P<python>\S*python\S*)'
cmdex = '(?P<cmd>.*)'
procre = re.compile('^\s*' + pidex + '\s*' + pythonex + '\s*' + cmdex + '$')
xendre = re.compile('^/usr/sbin/xend\s*(start|restart)\s*.*$')
procs = os.popen('ps -e -o pid,args 2>/dev/null')
for proc in procs:
pm = procre.match(proc)
if not pm: continue
xm = xendre.match(pm.group('cmd'))
if not xm: continue
#print 'pid=', pm.group('pid'), 'cmd=', pm.group('cmd')
pids.append(int(pm.group('pid')))
return pids
def new_cleanup(self, kill=0):
err = 0
pids = self.daemon_pids()
if kill:
for pid in pids:
print "Killing daemon pid=%d" % pid
os.kill(pid, signal.SIGHUP)
elif pids:
err = 1
print "Daemon already running: ", pids
return err
def cleanup(self, kill=False):
# No cleanup to do if PID_FILE is empty.
if not os.path.isfile(PID_FILE) or not os.path.getsize(PID_FILE):
return 0
# Read the pid of the previous invocation and search active process list.
pid = open(PID_FILE, 'r').read()
lines = os.popen('ps ' + pid + ' 2>/dev/null').readlines()
for line in lines:
if re.search('^ *' + pid + '.+xend', line):
if not kill:
print "Daemon is already running (pid %d)" % int(pid)
return 1
# Old daemon is still active: terminate it.
os.kill(int(pid), 1)
# Delete the stale PID_FILE.
os.remove(PID_FILE)
return 0
def install_child_reaper(self):
#signal.signal(signal.SIGCHLD, self.onSIGCHLD)
# Ensure that zombie children are automatically reaped.
xend.utils.autoreap()
def onSIGCHLD(self, signum, frame):
code = 1
while code > 0:
code = os.waitpid(-1, os.WNOHANG)
def start(self):
if self.cleanup(kill=False):
return 1
# Detach from TTY.
if not DEBUG:
os.setsid()
if self.set_user():
return 1
self.install_child_reaper()
# Fork -- parent writes PID_FILE and exits.
pid = os.fork()
if pid:
# Parent
pidfile = open(PID_FILE, 'w')
pidfile.write(str(pid))
pidfile.close()
return 0
# Child
logfile = self.open_logfile()
self.redirect_output(logfile)
self.run()
return 0
def open_logfile(self):
if not os.path.exists(CONTROL_DIR):
os.makedirs(CONTROL_DIR)
# Open log file. Truncate it if non-empty, and request line buffering.
if os.path.isfile(LOG_FILE):
os.rename(LOG_FILE, LOG_FILE+'.old')
logfile = open(LOG_FILE, 'w+', 1)
return logfile
def set_user(self):
# Set the UID.
try:
os.setuid(pwd.getpwnam(USER)[2])
return 0
except KeyError, error:
print "Error: no such user '%s'" % USER
return 1
def redirect_output(self, logfile):
if DEBUG: return
# Close down standard file handles
try:
os.close(0) # stdin
os.close(1) # stdout
os.close(2) # stderr
except:
pass
# Redirect output to log file.
sys.stdout = sys.stderr = logfile
def stop(self):
return self.cleanup(kill=True)
def run(self):
self.createFactories()
self.listenMgmt()
self.listenEvent()
self.listenNotifier()
SrvServer.create()
reactor.run()
def createFactories(self):
self.channelF = channel.channelFactory()
self.blkifCF = blkif.BlkifControllerFactory()
self.netifCF = netif.NetifControllerFactory()
self.consoleCF = console.ConsoleControllerFactory()
def listenMgmt(self):
protocol = MgmtProtocol(self)
s = os.path.join(CONTROL_DIR, MGMT_SOCK)
if os.path.exists(s):
os.unlink(s)
return reactor.listenUNIXDatagram(s, protocol)
def listenEvent(self):
protocol = EventFactory(self)
return reactor.listenTCP(EVENT_PORT, protocol)
def listenNotifier(self):
protocol = NotifierProtocol(self.channelF)
p = NotifierPort(self, self.channelF.notifier, protocol, reactor)
p.startListening()
return p
def exit(self):
reactor.diconnectAll()
sys.exit(0)
def blkif_create(self, dom):
"""Create a block device interface controller.
Returns Deferred
"""
d = self.blkifCF.createInstance(dom)
return d
def blkif_dev_create(self, dom, vdev, mode, segment):
"""Create a block device.
Returns Deferred
"""
ctrl = self.blkifCF.getInstanceByDom(dom)
if not ctrl:
raise ValueError('No blkif controller: %d' % dom)
print 'blkif_dev_create>', dom, vdev, mode, segment
d = ctrl.attach_device(vdev, mode, segment)
return d
def netif_create(self, dom):
"""Create a network interface controller.
"""
return self.netifCF.createInstance(dom)
def netif_dev_create(self, dom, vif, vmac):
"""Create a network device.
todo
"""
ctrl = self.netifCF.getInstanceByDom(dom)
if not ctrl:
raise ValueError('No netif controller: %d' % dom)
d = ctrl.attach_device(vif, vmac)
return d
def console_create(self, dom, console_port=None):
"""Create a console for a domain.
"""
console = self.consoleCF.getInstanceByDom(dom)
if console is None:
console = self.consoleCF.createInstance(dom, console_port)
return console.sxpr()
def consoles(self):
return [ c.sxpr() for c in self.consoleCF.getInstances() ]
def get_console(self, id):
return self.consoleCF.getInstance(id)
def get_domain_console(self, dom):
return self.consoleCF.getInstanceByDom(dom)
def console_disconnect(self, id):
"""Disconnect any connected console client.
"""
console = self.get_console(id)
if not console:
raise ValueError('Invalid console id')
if console.conn:
console.conn.loseConnection()
def instance():
global inst
try:
inst
except:
inst = Daemon()
return inst
+9
View File
@@ -0,0 +1,9 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from SrvDir import SrvDir
class SrvDeviceDir(SrvDir):
"""Device directory.
"""
pass
+91
View File
@@ -0,0 +1,91 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from twisted.web import error
from xenmgr import sxp
from SrvBase import SrvBase
class SrvConstructor:
"""Delayed constructor for sub-servers.
Does not import the sub-server class or create the object until needed.
"""
def __init__(self, klass):
"""Create a constructor. It is assumed that the class
should be imported as 'import klass from klass'.
klass name of its class
"""
self.klass = klass
self.obj = None
def getobj(self):
"""Get the sub-server object, importing its class and instantiating it if
necessary.
"""
if not self.obj:
exec 'from %s import %s' % (self.klass, self.klass)
klassobj = eval(self.klass)
self.obj = klassobj()
return self.obj
class SrvDir(SrvBase):
"""Base class for directory servlets.
"""
isLeaf = False
def __init__(self):
SrvBase.__init__(self)
self.table = {}
self.order = []
def getChild(self, x, req):
if x == '': return self
val = self.get(x)
if val is None:
return error.NoResource('Not found')
else:
return val
def get(self, x):
val = self.table.get(x)
if val is not None:
val = val.getobj()
return val
def add(self, x, xclass = None):
if xclass is None:
xclass = 'SrvDir'
self.table[x] = SrvConstructor(xclass)
self.order.append(x)
def render_GET(self, req):
if self.use_sxp(req):
req.setHeader("Content-type", sxp.mime_type)
self.ls(req, 1)
else:
req.write('<html><head></head><body>')
self.print_path(req)
self.ls(req)
self.form(req)
req.write('</body></html>')
return ''
def ls(self, req, use_sxp=0):
url = req.prePathURL()
if not url.endswith('/'):
url += '/'
if use_sxp:
req.write('(ls ')
for k in self.order:
req.write(' ' + k)
req.write(')')
else:
req.write('<ul>')
for k in self.order:
v = self.get(k)
req.write('<li><a href="%s%s">%s</a></li>'
% (url, k, k))
req.write('</ul>')
def form(self, req):
pass
+202
View File
@@ -0,0 +1,202 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from xenmgr import sxp
from xenmgr import XendDomain
from xenmgr import XendConsole
from xenmgr import PrettyPrint
from xenmgr.Args import FormFn
from SrvDir import SrvDir
class SrvDomain(SrvDir):
"""Service managing a single domain.
"""
def __init__(self, dom):
SrvDir.__init__(self)
self.dom = dom
self.xd = XendDomain.instance()
self.xconsole = XendConsole.instance()
def op_start(self, op, req):
val = self.xd.domain_start(self.dom.id)
return val
def op_stop(self, op, req):
val = self.xd.domain_stop(self.dom.id)
return val
def op_shutdown(self, op, req):
val = self.xd.domain_shutdown(self.dom.id)
req.setResponseCode(202)
req.setHeader("Location", "%s/.." % req.prePathURL())
return val
def op_halt(self, op, req):
val = self.xd.domain_halt(self.dom.id)
req.setHeader("Location", "%s/.." % req.prePathURL())
return val
def op_save(self, op, req):
fn = FormFn(self.xd.domain_save,
[['dom', 'int'],
['dst', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_restore(self, op, req):
fn = FormFn(self.xd.domain_restore,
[['dom', 'int'],
['src', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_migrate(self, op, req):
fn = FormFn(self.xd.domain_migrate,
[['dom', 'int'],
['destination', 'str']])
val = fn(req.args, {'dom': self.dom.id})
val = 0 # Some migrate id.
req.setResponseCode(202)
#req.send_header("Location", "%s/.." % self.path) # Some migrate url.
return val
def op_pincpu(self, op, req):
fn = FormFn(self.xd.domain_migrate,
[['dom', 'int'],
['cpu', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_cpu_bvt_set(self, op, req):
fn = FormFn(self.xd.domain_cpu_bvt_set,
[['dom', 'int'],
['mcuadv', 'int'],
['warp', 'int'],
['warpl', 'int'],
['warpu', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_cpu_atropos_set(self, op, req):
fn = FormFn(self.xd.domain_cpu_atropos_set,
[['dom', 'int'],
['period', 'int'],
['slice', 'int'],
['latency', 'int'],
['xtratime', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vifs(self, op, req):
return self.xd.domain_vif_ls(self.dom.id)
def op_vif(self, op, req):
fn = FormFn(self.xd.domain_vif_get,
[['dom', 'int'],
['vif', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vif_stats(self, op, req):
#todo
fn = FormFn(self.xd.domain_vif_stats,
[['dom', 'int'],
['vif', 'int']])
#val = fn(req.args, {'dom': self.dom.id})
val = 999
#return val
return val
def op_vif_ip_add(self, op, req):
fn = FormFn(self.xd.domain_vif_ip_add,
[['dom', 'int'],
['vif', 'int'],
['ip', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vif_scheduler_set(self, op, req):
fn = FormFn(self.xd.domain_vif_scheduler_set,
[['dom', 'int'],
['vif', 'int'],
['bytes', 'int'],
['usecs', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vif_scheduler_get(self, op, req):
fn = FormFn(self.xd.domain_vif_scheduler_set,
[['dom', 'int'],
['vif', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vbds(self, op, req):
return self.xd.domain_vbd_ls(self.dom.id)
def op_vbd(self, op, req):
fn = FormFn(self.xd.domain_vbd_get,
[['dom', 'int'],
['vbd', 'int']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vbd_add(self, op, req):
fn = FormFn(self.xd.domain_vbd_add,
[['dom', 'int'],
['uname', 'str'],
['dev', 'str'],
['mode', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def op_vbd_remove(self, op, req):
fn = FormFn(self.xd.domain_vbd_remove,
[['dom', 'int'],
['dev', 'str']])
val = fn(req.args, {'dom': self.dom.id})
return val
def render_POST(self, req):
return self.perform(req)
def render_GET(self, req):
op = req.args.get('op')
if op and op[0] in ['vifs', 'vif', 'vif_stats', 'vbds', 'vbd']:
return self.perform(req)
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
sxp.show(self.dom.sxpr(), out=req)
else:
req.write('<html><head></head><body>')
self.print_path(req)
#self.ls()
req.write('<p>%s</p>' % self.dom)
if self.dom.console:
cinfo = self.dom.console
cid = cinfo.id
#todo: Local xref: need to know server prefix.
req.write('<p><a href="/xend/console/%s">Console %s</a></p>'
% (cid, cid))
req.write('<p><a href="%s">Connect to console</a></p>'
% cinfo.uri())
if self.dom.config:
req.write("<code><pre>")
PrettyPrint.prettyprint(self.dom.config, out=req)
req.write("</pre></code>")
req.write('<a href="%s?op=vif_stats&vif=0">vif 0 stats</a>'
% req.prePathURL())
self.form(req)
req.write('</body></html>')
return ''
def form(self, req):
req.write('<form method="post" action="%s">' % req.prePathURL())
req.write('<input type="submit" name="op" value="start">')
req.write('<input type="submit" name="op" value="stop">')
req.write('<input type="submit" name="op" value="shutdown">')
req.write('<input type="submit" name="op" value="halt">')
req.write('<br><input type="submit" name="op" value="migrate">')
req.write('To: <input type="text" name="destination">')
req.write('</form>')
+130
View File
@@ -0,0 +1,130 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from StringIO import StringIO
from twisted.protocols import http
from twisted.web import error
from xenmgr import sxp
from xenmgr import XendDomain
from SrvDir import SrvDir
from SrvDomain import SrvDomain
class SrvDomainDir(SrvDir):
"""Service that manages the domain directory.
"""
def __init__(self):
SrvDir.__init__(self)
self.xd = XendDomain.instance()
def domain(self, x):
val = None
try:
dom = self.xd.domain_get(x)
val = SrvDomain(dom)
except KeyError:
pass
return val
def get(self, x):
v = SrvDir.get(self, x)
if v is not None:
return v
v = self.domain(x)
return v
def op_create(self, op, req):
ok = 0
try:
configstring = req.args.get('config')[0]
print 'config:', configstring
pin = sxp.Parser()
pin.input(configstring)
pin.input_eof()
config = pin.get_val()
ok = 1
except Exception, ex:
print ex
if not ok:
req.setResponseCode(http.BAD_REQUEST, "Invalid configuration")
return "Invalid configuration"
return error.ErrorPage(http.BAD_REQUEST,
"Invalid",
"Invalid configuration")
try:
deferred = self.xd.domain_create(config)
deferred.addCallback(self._cb_op_create, configstring, req)
return deferred
except Exception, ex:
raise
#return ['err', str(ex) ]
#req.setResponseCode(http.BAD_REQUEST, "Error creating domain")
#return str(ex)
#return error.ErrorPage(http.BAD_REQUEST,
# "Error creating domain",
# str(ex))
def _cb_op_create(self, dominfo, configstring, req):
"""Callback to handle deferred domain creation.
"""
dom = dominfo.id
domurl = "%s/%s" % (req.prePathURL(), dom)
req.setResponseCode(201, "created")
req.setHeader("Location", domurl)
if self.use_sxp(req):
return dominfo.sxpr()
else:
out = StringIO()
print >> out, ('<p> Created <a href="%s">Domain %s</a></p>'
% (domurl, dom))
print >> out, '<p><pre>'
print >> out, configstring
print >> out, '</pre></p>'
val = out.getvalue()
out.close()
return val
def render_POST(self, req):
return self.perform(req)
def render_GET(self, req):
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
self.ls_domain(req, 1)
else:
req.write("<html><head></head><body>")
self.print_path(req)
self.ls(req)
self.ls_domain(req)
self.form(req)
req.write("</body></html>")
return ''
def ls_domain(self, req, use_sxp=0):
url = req.prePathURL()
if not url.endswith('/'):
url += '/'
if use_sxp:
domains = self.xd.domain_ls()
sxp.show(domains, out=req)
else:
domains = self.xd.domains()
domains.sort(lambda x, y: cmp(x.id, y.id))
req.write('<ul>')
for d in domains:
req.write('<li><a href="%s%s"> Domain %s</a>'
% (url, d.id, d.id))
req.write('name=%s' % d.name)
req.write('memory=%d'% d.memory)
req.write('</li>')
req.write('</ul>')
def form(self, req):
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
% req.prePathURL())
req.write('<button type="submit" name="op" value="create">Create Domain</button>')
req.write('Config <input type="file" name="config"><br>')
req.write('</form>')
+41
View File
@@ -0,0 +1,41 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from xenmgr import sxp
from xenmgr import EventServer
from SrvDir import SrvDir
class SrvEventDir(SrvDir):
"""Event directory.
"""
def __init__(self):
SrvDir.__init__(self)
self.eserver = EventServer.instance()
def op_inject(self, op, req):
eventstring = req.args.get('event')
pin = sxp.Parser()
pin.input(eventstring)
pin.input_eof()
sxpr = pin.get_val()
self.eserver.inject(sxp.name(sxpr), sxpr)
if req.use_sxp:
sxp.name(sxpr)
else:
return '<code>' + eventstring + '</code>'
def render_POST(self, req):
return self.perform(req)
def form(self, req):
action = req.prePathURL()
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
% action)
req.write('<button type="submit" name="op" value="inject">Inject</button>')
req.write('Event <input type="text" name="event" size="40"><br>')
req.write('</form>')
req.write('<form method="post" action="%s" enctype="multipart/form-data">'
% action)
req.write('<button type="submit" name="op" value="inject">Inject</button>')
req.write('Event file<input type="file" name="event"><br>')
req.write('</form>')
+59
View File
@@ -0,0 +1,59 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
import os
from SrvDir import SrvDir
from xenmgr import sxp
from xenmgr import XendNode
class SrvNode(SrvDir):
"""Information about the node.
"""
def __init__(self):
SrvDir.__init__(self)
self.xn = XendNode.instance()
def op_shutdown(self, op, req):
val = self.xn.shutdown()
return val
def op_reboot(self, op, req):
val = self.xn.reboot()
return val
def op_cpu_rrobin_slice_set(self, op, req):
fn = FormFn(self.xn.cpu_rrobin_slice_set,
[['slice', 'int']])
val = fn(req.args, {})
return val
def op_cpu_bvt_slice_set(self, op, req):
fn = FormFn(self.xn.cpu_bvt_slice_set,
[['slice', 'int']])
val = fn(req.args, {})
return val
def render_POST(self, req):
return self.perform(req)
def render_GET(self, req):
if self.use_sxp(req):
req.setHeader("Content-Type", sxp.mime_type)
sxp.show(['node'] + self.info(), out=req)
else:
req.write('<html><head></head><body>')
self.print_path(req)
req.write('<ul>')
for d in self.info():
req.write('<li> %10s: %s' % (d[0], 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]]
+31
View File
@@ -0,0 +1,31 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from xenmgr import XendRoot
xroot = XendRoot.instance()
from SrvDir import SrvDir
class SrvRoot(SrvDir):
"""The root of the xend server.
"""
"""Server sub-components. Each entry is (name, class), where
'name' is the entry name and 'class' is the name of its class.
"""
#todo Get this list from the XendRoot config.
subdirs = [
('node', 'SrvNode' ),
('domain', 'SrvDomainDir' ),
('console', 'SrvConsoleDir' ),
('event', 'SrvEventDir' ),
('vdisk', 'SrvVdiskDir' ),
('device', 'SrvDeviceDir' ),
('vnet', 'SrvVnetDir' ),
]
def __init__(self):
SrvDir.__init__(self)
for (name, klass) in self.subdirs:
self.add(name, klass)
for (name, klass) in self.subdirs:
self.get(name)
xroot.start()
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/python2
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Example xend HTTP and console server.
Can be accessed from a browser or from a program.
Do 'python SrvServer.py' to run the server.
Then point a web browser at http://localhost:8000/xend and follow the links.
Most are stubs, except /domain which has a list of domains and a 'create domain'
button.
You can also access the server from a program.
Do 'python XendClient.py' to run a few test operations.
The data served differs depending on the client (as defined by User-Agent
and Accept in the HTTP headers). If the client is a browser, data
is returned in HTML, with interactive forms. If the client is a program,
data is returned in SXP format, with no forms.
The server serves to the world by default. To restrict it to the local host
change 'interface' in main().
Mike Wray <mike.wray@hp.com>
"""
# todo Support security settings etc. in the config file.
# todo Support command-line args.
from twisted.web import server
from twisted.web import resource
from twisted.internet import reactor
from xenmgr import XendRoot
xroot = XendRoot.instance()
from SrvRoot import SrvRoot
def create(port=None, interface=None):
if port is None: port = 8000
if interface is None: interface = ''
root = resource.Resource()
xend = SrvRoot()
root.putChild('xend', xend)
site = server.Site(root)
reactor.listenTCP(port, site, interface=interface)
def main(port=None, interface=None):
create(port, interface)
reactor.run()
if __name__ == '__main__':
main()
+12
View File
@@ -0,0 +1,12 @@
# 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()
+28
View File
@@ -0,0 +1,28 @@
# 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
+9
View File
@@ -0,0 +1,9 @@
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
from SrvDir import SrvDir
class SrvVnetDir(SrvDir):
"""Vnet directory.
"""
pass
+1
View File
@@ -0,0 +1 @@
+232
View File
@@ -0,0 +1,232 @@
import channel
import controller
from messages import *
class BlkifControllerFactory(controller.ControllerFactory):
"""Factory for creating block device interface controllers.
Also handles the 'back-end' channel to dom0.
"""
# todo: add support for setting dom controlling blkifs (don't assume 0).
# todo: add support for 'recovery'.
def __init__(self):
controller.ControllerFactory.__init__(self)
self.majorTypes = [ CMSG_BLKIF_BE ]
self.subTypes = {
CMSG_BLKIF_BE_CREATE : self.recv_be_create,
CMSG_BLKIF_BE_CONNECT : self.recv_be_connect,
CMSG_BLKIF_BE_VBD_CREATE : self.recv_be_vbd_create,
CMSG_BLKIF_BE_VBD_GROW : self.recv_be_vbd_grow,
CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
}
self.attached = 1
self.registerChannel()
def createInstance(self, dom):
d = self.addDeferred()
blkif = self.getInstanceByDom(dom)
if blkif:
self.callDeferred(blkif)
else:
blkif = BlkifController(self, dom)
self.addInstance(blkif)
blkif.send_be_create()
return d
def setControlDomain(self, dom):
if self.channel:
self.deregisterChannel()
self.attached = 0
self.dom = dom
self.registerChannel()
#
#if xend.blkif.be_port:
# xend.blkif.recovery = True
#xend.blkif.be_port = xend.main.port_from_dom(dom)
def recv_be_create(self, msg, req):
#print 'recv_be_create>'
val = unpackMsg('blkif_be_create_t', msg)
blkif = self.getInstanceByDom(val['domid'])
self.callDeferred(blkif)
def recv_be_connect(self, msg, req):
#print 'recv_be_create>'
val = unpackMsg('blkif_be_connect_t', msg)
blkif = self.getInstanceByDom(val['domid'])
if blkif:
blkif.send_fe_interface_status_changed()
else:
pass
def recv_be_vbd_create(self, msg, req):
#print 'recv_be_vbd_create>'
val = unpackMsg('blkif_be_vbd_create_t', msg)
blkif = self.getInstanceByDom(val['domid'])
if blkif:
blkif.send_be_vbd_grow(val['vdevice'])
else:
pass
def recv_be_vbd_grow(self, msg, req):
#print 'recv_be_vbd_grow>'
val = unpackMsg('blkif_be_vbd_grow_t', msg)
# Check status?
if self.attached:
self.callDeferred(0)
else:
self.reattach_device(val['domid'], val['vdevice'])
def reattach_device(self, dom, vdev):
blkif = self.getInstanceByDom(dom)
if blkif:
blkif.reattach_device(vdev)
attached = 1
for blkif in self.getInstances():
if not blkif.attached:
attached = 0
break
self.attached = attached
if self.attached:
self.reattached()
def reattached(self):
for blkif in self.getInstances():
blkif.reattached()
def recv_be_driver_status_changed(self, msg, req):
val = unpackMsg('blkif_be_driver_status_changed_t'. msg)
status = val['status']
if status == BLKIF_DRIVER_STATUS_UP and not self.attached:
for blkif in self.getInstances():
blkif.detach()
class BlkDev:
"""Info record for a block device.
"""
def __init__(self, vdev, mode, segment):
self.vdev = vdev
self.mode = mode
self.device = segment['device']
self.start_sector = segment['start_sector']
self.nr_sectors = segment['nr_sectors']
self.attached = 1
def readonly(self):
return 'w' not in self.mode
class BlkifController(controller.Controller):
"""Block device interface controller. Handles all block devices
for a domain.
"""
def __init__(self, factory, dom):
#print 'BlkifController> dom=', dom
controller.Controller.__init__(self, factory, dom)
self.devices = {}
self.majorTypes = [ CMSG_BLKIF_FE ]
self.subTypes = {
CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED:
self.recv_fe_driver_status_changed,
CMSG_BLKIF_FE_INTERFACE_CONNECT :
self.recv_fe_interface_connect,
}
self.attached = 1
self.registerChannel()
#print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx
def attach_device(self, vdev, mode, segment):
"""Attach a device to the specified interface.
"""
#print 'BlkifController>attach_device>', self.dom, vdev, mode, segment
if vdev in self.devices: return -1
dev = BlkDev(vdev, mode, segment)
self.devices[vdev] = dev
self.send_be_vbd_create(vdev)
return self.factory.addDeferred()
def detach(self):
self.attached = 0
for dev in self.devices.values():
dev.attached = 0
self.send_be_vbd_create(vdev)
def reattach_device(self, vdev):
dev = self.devices[vdev]
dev.attached = 1
attached = 1
for dev in self.devices.values():
if not dev.attached:
attached = 0
break
self.attached = attached
return self.attached
def reattached(self):
msg = packMsg('blkif_fe_interface_status_changed_t',
{ 'handle' : 0,
'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED})
self.writeRequest(msg)
def recv_fe_driver_status_changed(self, msg, req):
msg = packMsg('blkif_fe_interface_status_changed_t',
{ 'handle' : 0,
'status' : BLKIF_INTERFACE_STATUS_DISCONNECTED,
'evtchn' : 0 })
self.writeRequest(msg)
def recv_fe_interface_connect(self, msg, req):
val = unpackMsg('blkif_fe_interface_connect_t', msg)
self.evtchn = channel.eventChannel(0, self.dom)
msg = packMsg('blkif_be_connect_t',
{ 'domid' : self.dom,
'blkif_handle' : val['handle'],
'evtchn' : self.evtchn['port1'],
'shmem_frame' : val['shmem_frame'] })
self.factory.writeRequest(msg)
pass
#def recv_fe_interface_status_changed(self, msg, req):
# (hnd, status, chan) = unpackMsg('blkif_fe_interface_status_changed_t', msg)
# print 'recv_fe_interface_status_changed>', hnd, status, chan
# pass
def send_fe_interface_status_changed(self):
msg = packMsg('blkif_fe_interface_status_changed_t',
{ 'handle' : 0,
'status' : BLKIF_INTERFACE_STATUS_CONNECTED,
'evtchn' : self.evtchn['port2'] })
self.writeRequest(msg)
def send_be_create(self):
msg = packMsg('blkif_be_create_t',
{ 'domid' : self.dom,
'blkif_handle' : 0 })
self.factory.writeRequest(msg)
def send_be_vbd_create(self, vdev):
dev = self.devices[vdev]
msg = packMsg('blkif_be_vbd_create_t',
{ 'domid' : self.dom,
'blkif_handle' : 0,
'vdevice' : dev.vdev,
'readonly' : dev.readonly() })
self.factory.writeRequest(msg)
def send_be_vbd_grow(self, vdev):
dev = self.devices[vdev]
msg = packMsg('blkif_be_vbd_grow_t',
{ 'domid' : self.dom,
'blkif_handle' : 0,
'vdevice' : dev.vdev,
'extent.device' : dev.device,
'extent.sector_start' : dev.start_sector,
'extent.sector_length' : dev.nr_sectors })
self.factory.writeRequest(msg)
+259
View File
@@ -0,0 +1,259 @@
import Xc; xc = Xc.new()
import xend.utils
from messages import msgTypeName
def eventChannel(dom1, dom2):
return xc.evtchn_bind_interdomain(dom1=dom1, dom2=dom2)
class ChannelFactory:
"""Factory for creating channels.
Maintains a table of channels.
"""
channels = {}
def __init__(self):
self.notifier = xend.utils.notifier()
def addChannel(self, channel):
idx = channel.idx
self.channels[idx] = channel
self.notifier.bind(idx)
# Try to wake it up
#self.notifier.unmask(idx)
#channel.notify()
def getChannel(self, idx):
return self.channels.get(idx)
def delChannel(self, idx):
if idx in self.channels:
del self.channels[idx]
self.notifier.unbind(idx)
def domChannel(self, dom):
for chan in self.channels.values():
if chan.dom == dom:
return chan
chan = Channel(self, dom)
self.addChannel(chan)
return chan
def channelClosed(self, channel):
self.delChannel(channel.idx)
def createPort(self, dom):
return xend.utils.port(dom)
def channelFactory():
global inst
try:
inst
except:
inst = ChannelFactory()
return inst
class Channel:
"""A control channel to a domain. Messages for the domain device controllers
are multiplexed over the channel (console, block devs, net devs).
"""
def __init__(self, factory, dom):
self.factory = factory
self.dom = dom
self.port = self.factory.createPort(dom)
self.idx = self.port.local_port
self.devs = []
self.devs_by_type = {}
self.closed = 0
self.queue = []
def getIndex(self):
return self.idx
def getLocalPort(self):
return self.port.local_port
def getRemotePort(self):
return self.port.remote_port
def close(self):
for d in self.devs:
d.lostChannel()
self.factory.channelClosed(self)
del self.devs
del self.devs_by_type
def registerDevice(self, types, dev):
"""Register a device controller.
@param types message types the controller handles
@param dev device controller
"""
self.devs.append(dev)
for ty in types:
self.devs_by_type[ty] = dev
def unregisterDevice(self, dev):
"""Remove the registration for a device controller.
@param dev device controller
"""
self.devs.remove(dev)
types = [ ty for (ty, d) in self.devs_by_type.items()
if d == dev ]
for ty in types:
del devs_by_type[ty]
def getDevice(self, type):
"""Get the device controller handling a message type.
@param type message type
@returns controller or None
"""
return self.devs_by_type.get(type)
def getMessageType(self, msg):
hdr = msg.get_header()
return (hdr['type'], hdr.get('subtype'))
def __repr__(self):
return ('<Channel dom=%d ports=%d:%d>'
% (self.dom,
self.port.local_port,
self.port.remote_port))
def notificationReceived(self, type):
#print 'notificationReceived> type=', type, self
if self.closed: return
if type == self.factory.notifier.EXCEPTION:
print 'notificationReceived> EXCEPTION'
info = xc.evtchn_status(self.idx)
if info['status'] == 'unbound':
print 'notificationReceived> EXCEPTION closing...'
self.close()
return
work = 0
work += self.handleRequests()
work += self.handleResponses()
work += self.handleWrites()
if work:
self.notify()
#print 'notificationReceived<', work
def notify(self):
#print 'notify>', self
self.port.notify()
def handleRequests(self):
#print 'handleRequests>'
work = 0
while 1:
#print 'handleRequests>', work
msg = self.readRequest()
#print 'handleRequests> msg=', msg
if not msg: break
self.requestReceived(msg)
work += 1
#print 'handleRequests<', work
return work
def requestReceived(self, msg):
(ty, subty) = self.getMessageType(msg)
#print 'requestReceived>', ty, subty, self
#todo: Must respond before writing any more messages.
#todo: Should automate this (respond on write)
self.port.write_response(msg)
dev = self.getDevice(ty)
if dev:
dev.requestReceived(msg, ty, subty)
else:
print ("requestReceived> No device: Message type %s %d:%d"
% (msgTypeName(ty, subty), ty, subty)), self
def handleResponses(self):
#print 'handleResponses>', self
work = 0
while 1:
#print 'handleResponses>', work
msg = self.readResponse()
#print 'handleResponses> msg=', msg
if not msg: break
self.responseReceived(msg)
work += 1
#print 'handleResponses<', work
return work
def responseReceived(self, msg):
(ty, subty) = self.getMessageType(msg)
#print 'responseReceived>', ty, subty
dev = self.getDevice(ty)
if dev:
dev.responseReceived(msg, ty, subty)
else:
print ("responseReceived> No device: Message type %d:%d"
% (msgTypeName(ty, subty), ty, subty)), self
def handleWrites(self):
#print 'handleWrites>', self
work = 0
# Pull data from producers.
#print 'handleWrites> pull...'
for dev in self.devs:
work += dev.produceRequests()
# Flush the queue.
#print 'handleWrites> flush...'
while self.queue and self.port.space_to_write_request():
msg = self.queue.pop(0)
self.port.write_request(msg)
work += 1
#print 'handleWrites<', work
return work
def writeRequest(self, msg, notify=1):
#print 'writeRequest>', self
if self.closed:
val = -1
elif self.writeReady():
self.port.write_request(msg)
if notify: self.notify()
val = 1
else:
self.queue.append(msg)
val = 0
#print 'writeRequest<', val
return val
def writeResponse(self, msg):
#print 'writeResponse>', self
if self.closed: return -1
self.port.write_response(msg)
return 1
def writeReady(self):
if self.closed or self.queue: return 0
return self.port.space_to_write_request()
def readRequest(self):
#print 'readRequest>', self
if self.closed:
#print 'readRequest> closed'
return None
if self.port.request_to_read():
val = self.port.read_request()
else:
val = None
#print 'readRequest< ', val
return val
def readResponse(self):
#print 'readResponse>', self
if self.closed:
#print 'readResponse> closed'
return None
if self.port.response_to_read():
val = self.port.read_response()
else:
val = None
#print 'readResponse<', val
return val
+220
View File
@@ -0,0 +1,220 @@
from twisted.internet import reactor
from twisted.internet import protocol
from twisted.protocols import telnet
import xend.utils
from xenmgr import EventServer
eserver = EventServer.instance()
import controller
from messages import *
from params import *
"""Telnet binary option."""
TRANSMIT_BINARY = '0'
WILL = chr(251)
IAC = chr(255)
class ConsoleProtocol(protocol.Protocol):
"""Asynchronous handler for a console TCP socket.
"""
def __init__(self, controller, idx):
self.controller = controller
self.idx = idx
self.addr = None
self.binary = 0
def connectionMade(self):
peer = self.transport.getPeer()
self.addr = (peer.host, peer.port)
if self.controller.connect(self.addr, self):
self.transport.write("Cannot connect to console %d on domain %d\n"
% (self.idx, self.controller.dom))
self.loseConnection()
return
else:
self.transport.write("Connected to console %d on domain %d\n"
% (self.idx, self.controller.dom))
self.setTelnetTransmitBinary()
eserver.inject('xend.console.connect',
[self.idx, self.addr[0], self.addr[1]])
def setTelnetTransmitBinary(self):
"""Send the sequence to set the telnet TRANSMIT-BINARY option.
"""
self.write(IAC + WILL + TRANSMIT_BINARY)
def dataReceived(self, data):
if self.controller.handleInput(self, data):
self.loseConnection()
def write(self, data):
#if not self.connected: return -1
self.transport.write(data)
return len(data)
def connectionLost(self, reason=None):
eserver.inject('xend.console.disconnect',
[self.idx, self.addr[0], self.addr[1]])
self.controller.disconnect()
def loseConnection(self):
self.transport.loseConnection()
class ConsoleFactory(protocol.ServerFactory):
"""Asynchronous handler for a console server socket.
"""
protocol = ConsoleProtocol
def __init__(self, controller, idx):
#protocol.ServerFactory.__init__(self)
self.controller = controller
self.idx = idx
def buildProtocol(self, addr):
proto = self.protocol(self.controller, self.idx)
proto.factory = self
return proto
class ConsoleControllerFactory(controller.ControllerFactory):
"""Factory for creating console controllers.
"""
def createInstance(self, dom, console_port=None):
if console_port is None:
console_port = CONSOLE_PORT_BASE + dom
console = ConsoleController(self, dom, console_port)
self.addInstance(console)
eserver.inject('xend.console.create',
[console.idx, console.dom, console.console_port])
return console
def consoleClosed(self, console):
eserver.inject('xend.console.close', console.idx)
self.delInstance(console)
class ConsoleController(controller.Controller):
"""Console controller for a domain.
Does not poll for i/o itself, but relies on the notifier to post console
output and the connected TCP sockets to post console input.
"""
def __init__(self, factory, dom, console_port):
#print 'ConsoleController> dom=', dom
controller.Controller.__init__(self, factory, dom)
self.majorTypes = [ CMSG_CONSOLE ]
self.status = "new"
self.addr = None
self.conn = None
self.rbuf = xend.utils.buffer()
self.wbuf = xend.utils.buffer()
self.console_port = console_port
self.registerChannel()
self.listener = None
self.listen()
#print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx
def sxpr(self):
val =['console',
['id', self.idx ],
['domain', self.dom ],
['local_port', self.channel.getLocalPort() ],
['remote_port', self.channel.getRemotePort() ],
['console_port', self.console_port ] ]
if self.addr:
val.append(['connected', self.addr[0], self.addr[1]])
return val
def ready(self):
return not (self.closed() or self.rbuf.empty())
def closed(self):
return self.status == 'closed'
def connected(self):
return self.status == 'connected'
def close(self):
self.status = "closed"
self.listener.stopListening()
self.deregisterChannel()
self.lostChannel()
def listen(self):
"""Listen for TCP connections to the console port..
"""
if self.closed(): return
self.status = "listening"
if self.listener:
#self.listener.startListening()
pass
else:
f = ConsoleFactory(self, self.idx)
self.listener = reactor.listenTCP(self.console_port, f)
def connect(self, addr, conn):
if self.closed(): return -1
if self.connected(): return -1
self.addr = addr
self.conn = conn
self.status = "connected"
self.handleOutput()
return 0
def disconnect(self):
self.addr = None
self.conn = None
self.listen()
def requestReceived(self, msg, type, subtype):
#print '***Console', self.dom, msg.get_payload()
self.rbuf.write(msg.get_payload())
self.handleOutput()
def responseReceived(self, msg, type, subtype):
pass
def produceRequests(self):
# Send as much pending console data as there is room for.
work = 0
while not self.wbuf.empty() and self.channel.writeReady():
msg = xend.utils.message(CMSG_CONSOLE, 0, 0)
msg.append_payload(self.wbuf.read(msg.MAX_PAYLOAD))
work += self.channel.writeRequest(msg, notify=0)
return work
def handleInput(self, conn, data):
"""Handle some external input aimed at the console.
Called from a TCP connection (conn).
"""
if self.closed(): return -1
if conn != self.conn: return 0
self.wbuf.write(data)
if self.produceRequests():
self.channel.notify()
return 0
def handleOutput(self):
"""Handle buffered output from the console.
Sends it to the connected console (if any).
"""
if self.closed():
#print 'Console>handleOutput> closed'
return -1
if not self.conn:
#print 'Console>handleOutput> not connected'
return 0
while not self.rbuf.empty():
try:
#print 'Console>handleOutput> writing...'
bytes = self.conn.write(self.rbuf.peek())
if bytes > 0:
self.rbuf.discard(bytes)
except socket.error, error:
pass
#print 'Console>handleOutput<'
return 0
+133
View File
@@ -0,0 +1,133 @@
from twisted.internet import defer
import channel
from messages import msgTypeName
class CtrlMsgRcvr:
"""Abstract class for things that deal with a control interface to a domain.
"""
def __init__(self):
self.channelFactory = channel.channelFactory()
self.majorTypes = [ ]
self.subTypes = {}
self.dom = None
self.channel = None
self.idx = None
def requestReceived(self, msg, type, subtype):
method = self.subTypes.get(subtype)
if method:
method(msg, 1)
else:
print ('requestReceived> No handler: Message type %s %d:%d'
% (msgTypeName(type, subtype), type, subtype)), self
def responseReceived(self, msg, type, subtype):
method = self.subTypes.get(subtype)
if method:
method(msg, 0)
else:
print ('responseReceived> No handler: Message type %s %d:%d'
% (msgTypeName(type, subtype), type, subtype)), self
def lostChannel(self):
pass
def registerChannel(self):
self.channel = self.channelFactory.domChannel(self.dom)
#print 'registerChannel> channel=', self.channel, self
self.idx = self.channel.getIndex()
#print 'registerChannel> idx=', self.idx
if self.majorTypes:
self.channel.registerDevice(self.majorTypes, self)
def deregisterChannel(self):
if self.channel:
self.channel.deregisterDevice(self)
del self.channel
def produceRequests(self):
return 0
def writeRequest(self, msg):
if self.channel:
self.channel.writeRequest(msg)
else:
print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self
def writeResponse(self, msg):
if self.channel:
self.channel.writeResponse(msg)
else:
print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
class ControllerFactory(CtrlMsgRcvr):
"""Abstract class for factories creating controllers.
Maintains a table of instances.
"""
def __init__(self):
CtrlMsgRcvr.__init__(self)
self.instances = {}
self.dlist = []
self.dom = 0
def addInstance(self, instance):
self.instances[instance.idx] = instance
def getInstance(self, idx):
return self.instances.get(idx)
def getInstances(self):
return self.instances.values()
def getInstanceByDom(self, dom):
for inst in self.instances.values():
if inst.dom == dom:
return inst
return None
def delInstance(self, instance):
if instance in self.instances:
del self.instances[instance.idx]
def createInstance(self, dom):
raise NotImplementedError()
def instanceClosed(self, instance):
self.delInstance(instance)
def addDeferred(self):
d = defer.Deferred()
self.dlist.append(d)
return d
def callDeferred(self, *args):
if self.dlist:
d = self.dlist.pop(0)
d.callback(*args)
def errDeferred(self, *args):
if self.dlist:
d = self.dlist.pop(0)
d.errback(*args)
class Controller(CtrlMsgRcvr):
"""Abstract class for a device controller attached to a domain.
"""
def __init__(self, factory, dom):
CtrlMsgRcvr.__init__(self)
self.factory = factory
self.dom = dom
self.channel = None
self.idx = None
def close(self):
self.deregisterChannel()
self.lostChannel(self)
def lostChannel(self):
self.factory.instanceClosed(self)
+269
View File
@@ -0,0 +1,269 @@
import struct
class Struct:
maxDepth = 10
base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ]
sizes = {'B': 1,
'H': 2,
'I': 4,
'L': 4,
'Q': 8,
'c': 1,
'h': 2,
'i': 4,
'l': 4,
'q': 8,
'x': 1,
}
formats = {
'int8' : 'B',
'int16' : 'H',
'int32' : 'I',
'int64' : 'Q',
'u8' : 'B',
'u16' : 'H',
'u32' : 'I',
'u64' : 'Q'
}
def typedef(self, name, val):
self.formats[name] = val
def struct(self, name, *f):
self.typedef(name, StructInfo(self, f))
def getType(self, name):
return self.formats[name]
def format(self, ty):
d = 0
f = ty
while d < self.maxDepth:
d += 1
f = self.formats[f]
if isinstance(f, StructInfo):
return f.format()
if f in self.base:
return f
return -1
def alignedformat(self, ty):
fmt = self.format(ty)
#print 'alignedformat> %s |%s|' %(ty, fmt)
afmt = self.align(fmt)
#print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt)
return afmt
def align(self, fmt):
n1 = 0
afmt = ''
for a in fmt:
n2 = self.getSize(a)
m = n1 % n2
if m:
d = (n2 - m)
afmt += 'x' * d
n1 += d
afmt += a
n1 += n2
return afmt
def fmtsize(self, fmt):
s = 0
for f in fmt:
s += self.getSize(f)
return s
def getSize(self, f):
return self.sizes[f]
def pack(self, ty, data):
return self.getType(ty).pack(data)
def unpack(self, ty, data):
return self.getType(ty).unpack(data)
def show(self):
l = self.formats.keys()
l.sort()
for v in l:
print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v))
class StructInfo:
def __init__(self, s, f):
self.fmt = None
self.structs = s
self.fields = f
def alignedformat(self):
if self.afmt: return self.afmt
self.afmt = self.structs.align(self.format())
return self.afmt
def format(self):
if self.fmt: return self.fmt
fmt = ""
for (ty, name) in self.fields:
fmt += self.formatString(ty)
self.fmt = fmt
return fmt
def formatString(self, ty):
if ty in self.fields:
ty = self.fields[ty]
return self.structs.format(ty)
def pack(self, *args):
return struct.pack(self.alignedformat(), *args)
def unpack(self, data):
return struct.unpack(self.alignedformat(), data)
types = Struct()
types.typedef('short' , 'h')
types.typedef('int' , 'i')
types.typedef('long' , 'l')
types.typedef('unsigned short', 'H')
types.typedef('unsigned int' , 'I')
types.typedef('unsigned long' , 'L')
types.typedef('domid_t' , 'u64')
types.typedef('blkif_vdev_t' , 'u16')
types.typedef('blkif_pdev_t' , 'u16')
types.typedef('blkif_sector_t', 'u64')
types.struct('u8[6]',
('u8', 'a1'),
('u8', 'a2'),
('u8', 'a3'),
('u8', 'a4'),
('u8', 'a5'),
('u8', 'a6'))
types.struct('blkif_fe_interface_status_changed_t',
('unsigned int', 'handle'),
('unsigned int', 'status'),
('unsigned int', 'evtchn'))
types.struct('blkif_fe_driver_status_changed_t',
('unsigned int', 'status'),
('unsigned int', 'nr_interfaces'))
types.struct('blkif_fe_interface_connect_t',
('unsigned int' , 'handle'),
('unsigned long', 'shmem_frame'))
types.struct('blkif_fe_interface_disconnect_t',
('unsigned int', 'handle'))
types.struct('blkif_extent_t',
('blkif_pdev_t' , 'device'),
('blkif_sector_t', 'sector_start'),
('blkif_sector_t', 'sector_length'))
types.struct('blkif_be_create_t',
('domid_t' , 'domid'),
('unsigned int', 'blkif_handle'),
('unsigned int', 'status'))
types.struct('blkif_be_destroy_t',
('domid_t' , 'domid'),
('unsigned int', 'blkif_handle'),
('unsigned int', 'status'))
types.struct('blkif_be_connect_t',
('domid_t' , 'domid'),
('unsigned int' , 'blkif_handle'),
('unsigned int' , 'evtchn'),
('unsigned long', 'shmem_frame'),
('unsigned int' , 'status'))
types.struct('blkif_be_disconnect_t',
('domid_t' , 'domid'),
('unsigned int', 'blkif_handle'),
('unsigned int', 'status'))
types.struct('blkif_be_vbd_create_t',
('domid_t' , 'domid'), #Q
('unsigned int', 'blkif_handle'), #I
('blkif_vdev_t', 'vdevice'), #H
('int' , 'readonly'), #i
('unsigned int', 'status')) #I
types.struct('blkif_be_vbd_destroy_t',
('domid_t' , 'domid'),
('unsigned int', 'blkif_handle'),
('blkif_vdev_t', 'vdevice'),
('unsigned int', 'status'))
types.struct('blkif_be_vbd_grow_t',
('domid_t' , 'domid'), #Q
('unsigned int' , 'blkif_handle'), #I
('blkif_vdev_t' , 'vdevice'), #H
('blkif_extent_t', 'extent'), #HQQ
('unsigned int' , 'status')) #I
types.struct('blkif_be_vbd_shrink_t',
('domid_t' , 'domid'),
('unsigned int', 'blkif_handle'),
('blkif_vdev_t', 'vdevice'),
('unsigned int', 'status'))
types.struct('blkif_be_driver_status_changed_t',
('unsigned int', 'status'),
('unsigned int', 'nr_interfaces'))
types.struct('netif_fe_interface_status_changed_t',
('unsigned int', 'handle'),
('unsigned int', 'status'),
('unsigned int', 'evtchn'),
('u8[6]', 'mac'))
types.struct('netif_fe_driver_status_changed_t',
('unsigned int', 'status'),
('unsigned int', 'nr_interfaces'))
types.struct('netif_fe_interface_connect_t',
('unsigned int', 'handle'),
('unsigned long', 'tx_shmem_frame'),
('unsigned long', 'rx_shmem_frame'))
types.struct('netif_fe_interface_disconnect_t',
('unsigned int', 'handle'))
types.struct('netif_be_create_t',
('domid_t' , 'domid'),
('unsigned int', 'netif_handle'),
('u8[6]' , 'mac'),
('unsigned int', 'status'))
types.struct('netif_be_destroy_t',
('domid_t' , 'domid'),
('unsigned int', 'netif_handle'),
('unsigned int', 'status'))
types.struct('netif_be_connect_t',
('domid_t' , 'domid'),
('unsigned int' , 'netif_handle'),
('unsigned int' , 'evtchn'),
('unsigned long', 'tx_shmem_frame'),
('unsigned long', 'rx_shmem_frame'),
('unsigned int' , 'status'))
types.struct('netif_be_disconnect_t',
('domid_t' , 'domid'),
('unsigned int', 'netif_handle'),
('unsigned int', 'status'))
types.struct('netif_be_driver_status_changed_t',
('unsigned int', 'status'),
('unsigned int', 'nr_interfaces'))
if 1 or __name__ == "__main__":
types.show()
+186
View File
@@ -0,0 +1,186 @@
import struct
import xend.utils
""" All message formats.
Added to incrementally for the various message types.
See below.
"""
msg_formats = {}
#============================================================================
# Console message types.
#============================================================================
CMSG_CONSOLE = 0
console_formats = { 'console_data': (CMSG_CONSOLE, 0, "?") }
msg_formats.update(console_formats)
#============================================================================
# Block interface message types.
#============================================================================
CMSG_BLKIF_BE = 1
CMSG_BLKIF_FE = 2
CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED = 0
CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED = 32
CMSG_BLKIF_FE_INTERFACE_CONNECT = 33
CMSG_BLKIF_FE_INTERFACE_DISCONNECT = 34
CMSG_BLKIF_BE_CREATE = 0
CMSG_BLKIF_BE_DESTROY = 1
CMSG_BLKIF_BE_CONNECT = 2
CMSG_BLKIF_BE_DISCONNECT = 3
CMSG_BLKIF_BE_VBD_CREATE = 4
CMSG_BLKIF_BE_VBD_DESTROY = 5
CMSG_BLKIF_BE_VBD_GROW = 6
CMSG_BLKIF_BE_VBD_SHRINK = 7
CMSG_BLKIF_BE_DRIVER_STATUS_CHANGED = 32
BLKIF_DRIVER_STATUS_DOWN = 0
BLKIF_DRIVER_STATUS_UP = 1
BLKIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
BLKIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
BLKIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
BLKIF_BE_STATUS_OKAY = 0
BLKIF_BE_STATUS_ERROR = 1
BLKIF_BE_STATUS_INTERFACE_EXISTS = 2
BLKIF_BE_STATUS_INTERFACE_NOT_FOUND = 3
BLKIF_BE_STATUS_INTERFACE_CONNECTED = 4
BLKIF_BE_STATUS_VBD_EXISTS = 5
BLKIF_BE_STATUS_VBD_NOT_FOUND = 6
BLKIF_BE_STATUS_OUT_OF_MEMORY = 7
BLKIF_BE_STATUS_EXTENT_NOT_FOUND = 8
BLKIF_BE_STATUS_MAPPING_ERROR = 9
blkif_formats = {
'blkif_be_connect_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CONNECT, "QIILI"),
'blkif_be_create_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_CREATE, "QII"),
'blkif_be_destroy_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_DESTROY, "QII"),
'blkif_be_vbd_create_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_CREATE, "QIHII"),
'blkif_be_vbd_grow_t':
(CMSG_BLKIF_BE, CMSG_BLKIF_BE_VBD_GROW , "QIHHHQQI"),
'blkif_fe_interface_status_changed_t':
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_STATUS_CHANGED, "III"),
'blkif_fe_driver_status_changed_t':
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_DRIVER_STATUS_CHANGED, "?"),
'blkif_fe_interface_connect_t':
(CMSG_BLKIF_FE, CMSG_BLKIF_FE_INTERFACE_CONNECT, "IL"),
}
msg_formats.update(blkif_formats)
#============================================================================
# Network interface message types.
#============================================================================
CMSG_NETIF_BE = 3
CMSG_NETIF_FE = 4
CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED = 0
CMSG_NETIF_FE_DRIVER_STATUS_CHANGED = 32
CMSG_NETIF_FE_INTERFACE_CONNECT = 33
CMSG_NETIF_FE_INTERFACE_DISCONNECT = 34
CMSG_NETIF_BE_CREATE = 0
CMSG_NETIF_BE_DESTROY = 1
CMSG_NETIF_BE_CONNECT = 2
CMSG_NETIF_BE_DISCONNECT = 3
CMSG_NETIF_BE_DRIVER_STATUS_CHANGED = 32
NETIF_INTERFACE_STATUS_DESTROYED = 0 #/* Interface doesn't exist. */
NETIF_INTERFACE_STATUS_DISCONNECTED = 1 #/* Exists but is disconnected. */
NETIF_INTERFACE_STATUS_CONNECTED = 2 #/* Exists and is connected. */
NETIF_DRIVER_STATUS_DOWN = 0
NETIF_DRIVER_STATUS_UP = 1
netif_formats = {
'netif_be_connect_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_CONNECT, "QIILLI"),
'netif_be_create_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_CREATE, "QIBBBBBBBBI"),
'netif_be_destroy_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_DESTROY, "QII"),
'netif_be_disconnect_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_DISCONNECT, "QII"),
'netif_be_driver_status_changed_t':
(CMSG_NETIF_BE, CMSG_NETIF_BE_DRIVER_STATUS_CHANGED, "QII"),
'netif_fe_driver_status_changed_t':
(CMSG_NETIF_FE, CMSG_NETIF_FE_DRIVER_STATUS_CHANGED, "II"),
'netif_fe_interface_connect_t':
(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_CONNECT, "ILL"),
'netif_fe_interface_status_changed_t':
(CMSG_NETIF_FE, CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, "IIIBBBBBBBB"),
}
msg_formats.update(netif_formats)
#============================================================================
class Msg:
pass
def packMsg(ty, params):
print '>packMsg', ty, params
(major, minor, packing) = msg_formats[ty]
args = {}
for (k, v) in params.items():
if k == 'mac':
for i in range(0, 6):
args['mac[%d]' % i] = v[i]
else:
args[k] = v
for (k, v) in args.items():
print 'packMsg>', k, v, type(v)
msgid = 0
msg = xend.utils.message(major, minor, msgid, args)
return msg
def unpackMsg(ty, msg):
args = msg.get_payload()
mac = [0, 0, 0, 0, 0, 0]
macs = []
for (k, v) in args.items():
if k.startswith('mac['):
macs += k
i = int(k[4:5])
mac[i] = v
else:
pass
if macs:
args['mac'] = mac
for k in macs:
del args[k]
print '<unpackMsg', ty, args
return args
def msgTypeName(ty, subty):
for (name, info) in msg_formats.items():
if info[0] == ty and info[1] == subty:
return name
return None
+205
View File
@@ -0,0 +1,205 @@
import random
import channel
import controller
from messages import *
class NetifControllerFactory(controller.ControllerFactory):
"""Factory for creating network interface controllers.
Also handles the 'back-end' channel to dom0.
"""
# todo: add support for setting dom controlling blkifs (don't assume 0).
# todo: add support for 'recovery'.
def __init__(self):
controller.ControllerFactory.__init__(self)
self.majorTypes = [ CMSG_NETIF_BE ]
self.subTypes = {
CMSG_NETIF_BE_CREATE : self.recv_be_create,
CMSG_NETIF_BE_CONNECT: self.recv_be_connect,
CMSG_NETIF_BE_DRIVER_STATUS_CHANGED: self.recv_be_driver_status_changed,
}
self.attached = 1
self.registerChannel()
def createInstance(self, dom):
#print 'netif>createInstance> dom=', dom
netif = self.getInstanceByDom(dom)
if netif is None:
netif = NetifController(self, dom)
self.addInstance(netif)
return netif
def setControlDomain(self, dom):
self.deregisterChannel()
self.attached = 0
self.dom = dom
self.registerChannel()
#
#if xend.netif.be_port.remote_dom != 0:
# xend.netif.recovery = True
# xend.netif.be_port = xend.main.port_from_dom(dom)
#
pass
def recv_be_create(self, msg, req):
self.callDeferred(0)
def recv_be_connect(self, msg, req):
val = unpackMsg('netif_be_connect_t', msg)
dom = val['domid']
vif = val['netif_handle']
netif = self.getInstanceByDom(dom)
if netif:
netif.send_interface_connected(vif)
else:
print "recv_be_connect> unknown vif=", vif
pass
def recv_be_driver_status_changed(self, msg, req):
val = unpackMsg('netif_be_driver_status_changed_t', msg)
status = val['status']
if status == NETIF_DRIVER_STATUS_UP and not self.attached:
for netif in self.getInstances():
netif.reattach_devices()
self.attached = 1
## pl = msg.get_payload()
## status = pl['status']
## if status == NETIF_DRIVER_STATUS_UP:
## if xend.netif.recovery:
## print "New netif backend now UP, notifying guests:"
## for netif_key in interface.list.keys():
## netif = interface.list[netif_key]
## netif.create()
## print " Notifying %d" % netif.dom
## msg = xend.utils.message(
## CMSG_NETIF_FE,
## CMSG_NETIF_FE_INTERFACE_STATUS_CHANGED, 0,
## { 'handle' : 0, 'status' : 1 })
## netif.ctrlif_tx_req(xend.main.port_from_dom(netif.dom),msg)
## print "Done notifying guests"
## recovery = False
class NetDev:
"""Info record for a network device.
"""
def __init__(self, vif, mac):
self.vif = vif
self.mac = mac
self.evtchn = None
class NetifController(controller.Controller):
"""Network interface controller. Handles all network devices for a domain.
"""
def __init__(self, factory, dom):
#print 'NetifController> dom=', dom
controller.Controller.__init__(self, factory, dom)
self.devices = {}
self.majorTypes = [ CMSG_NETIF_FE ]
self.subTypes = {
CMSG_NETIF_FE_DRIVER_STATUS_CHANGED:
self.recv_fe_driver_status_changed,
CMSG_NETIF_FE_INTERFACE_CONNECT :
self.recv_fe_interface_connect,
}
self.registerChannel()
#print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx
def randomMAC(self):
# VIFs get a random MAC address with a "special" vendor id.
#
# NB. The vendor is currently an "obsolete" one that used to belong
# to DEC (AA-00-00). Using it is probably a bit rude :-)
#
# NB2. The first bit of the first random octet is set to zero for
# all dynamic MAC addresses. This may allow us to manually specify
# MAC addresses for some VIFs with no fear of clashes.
mac = [ 0xaa, 0x00, 0x00,
random.randint(0x00, 0x7f),
random.randint(0x00, 0xff),
random.randint(0x00, 0xff) ]
return mac
def attach_device(self, vif, vmac):
if vmac is None:
mac = self.randomMAC()
else:
mac = [ int(x, 16) for x in vmac.split(':') ]
if len(mac) != 6: raise ValueError("invalid mac")
#print "attach_device>", "vif=", vif, "mac=", mac
self.devices[vif] = NetDev(vif, mac)
d = self.factory.addDeferred()
self.send_be_create(vif)
return d
def reattach_devices(self):
d = self.factory.addDeferred()
self.send_be_create(vif)
self.attach_fe_devices(0)
def attach_fe_devices(self):
for dev in self.devices.values():
msg = packMsg('netif_fe_interface_status_changed_t',
{ 'handle' : dev.vif,
'status' : NETIF_INTERFACE_STATUS_DISCONNECTED,
'evtchn' : 0,
'mac' : dev.mac })
self.writeRequest(msg)
def recv_fe_driver_status_changed(self, msg, req):
if not req: return
msg = packMsg('netif_fe_driver_status_changed_t',
{ 'status' : NETIF_DRIVER_STATUS_UP,
'nr_interfaces' : len(self.devices) })
self.writeRequest(msg)
self.attach_fe_devices()
def recv_fe_interface_connect(self, msg, req):
val = unpackMsg('netif_fe_interface_connect_t', msg)
dev = self.devices[val['handle']]
dev.evtchn = channel.eventChannel(0, self.dom)
msg = packMsg('netif_be_connect_t',
{ 'domid' : self.dom,
'netif_handle' : dev.vif,
'evtchn' : dev.evtchn['port1'],
'tx_shmem_frame' : val['tx_shmem_frame'],
'rx_shmem_frame' : val['rx_shmem_frame'] })
self.factory.writeRequest(msg)
#def recv_fe_interface_status_changed(self):
# print 'recv_fe_interface_status_changed>'
# pass
def send_interface_connected(self, vif):
dev = self.devices[vif]
msg = packMsg('netif_fe_interface_status_changed_t',
{ 'handle' : dev.vif,
'status' : NETIF_INTERFACE_STATUS_CONNECTED,
'evtchn' : dev.evtchn['port2'],
'mac' : dev.mac })
self.writeRequest(msg)
def send_be_create(self, vif):
dev = self.devices[vif]
msg = packMsg('netif_be_create_t',
{ 'domid' : self.dom,
'netif_handle' : dev.vif,
'mac' : dev.mac })
self.factory.writeRequest(msg)
def send_be_destroy(self, vif):
print 'send_be_destroy>', 'dom=', self.dom, 'vif=', vif
dev = self.devices[vif]
del self.devices[vif]
msg = packMsg('netif_be_destroy_t',
{ 'domid' : self.dom,
'netif_handle' : vif })
self.factory.writeRequest(msg)
+10
View File
@@ -0,0 +1,10 @@
# The following parameters could be placed in a configuration file.
PID_FILE = '/var/run/xend.pid'
LOG_FILE = '/var/log/xend.log'
USER = 'root'
CONTROL_DIR = '/var/run/xend'
MGMT_SOCK = 'xenmgrsock' # relative to CONTROL_DIR
EVENT_PORT = 8001
CONSOLE_PORT_BASE = 9600
+557
View File
@@ -0,0 +1,557 @@
#!/usr/bin/python2
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""
Input-driven parsing for s-expression (sxp) format.
Create a parser: pin = Parser();
Then call pin.input(buf) with your input.
Call pin.input_eof() when done.
Use pin.read() to see if a value has been parsed, pin.get_val()
to get a parsed value. You can call ready and get_val at any time -
you don't have to wait until after calling input_eof.
"""
from __future__ import generators
import sys
import types
import errno
import string
__all__ = [
"mime_type",
"ParseError",
"Parser",
"atomp",
"show",
"show_xml",
"elementp",
"name",
"attributes",
"attribute",
"children",
"child",
"child_at",
"child0",
"child1",
"child2",
"child3",
"child4",
"child_value",
"has_id",
"with_id",
"child_with_id",
"elements",
"parse",
]
mime_type = "application/sxp"
escapes = {
'a': '\a',
'b': '\b',
't': '\t',
'n': '\n',
'v': '\v',
'f': '\f',
'r': '\r',
'\\': '\\',
'\'': '\'',
'\"': '\"'}
k_list_open = "("
k_list_close = ")"
k_attr_open = "@"
k_eval = "!"
escapes_rev = {}
for k in escapes:
escapes_rev[escapes[k]] = k
class ParseError(StandardError):
def __init__(self, parser, value):
self.parser = parser
self.value = value
def __str__(self):
return self.value
class ParserState:
def __init__(self, fn, parent=None):
self.parent = parent
self.buf = ''
self.val = []
self.delim = None
self.fn = fn
def push(self, fn):
return ParserState(fn, parent=self)
class Parser:
def __init__(self):
self.error = sys.stderr
self.reset()
def reset(self):
self.val = []
self.eof = 0
self.err = 0
self.line_no = 0
self.char_no = 0
self.state = None
def push_state(self, fn):
self.state = self.state.push(fn)
def pop_state(self):
val = self.state
self.state = self.state.parent
if self.state and self.state.fn == self.state_start:
# Return to start state - produce the value.
self.val += self.state.val
self.state.val = []
return val
def in_class(self, c, s):
return s.find(c) >= 0
def in_space_class(self, c):
return self.in_class(c, ' \t\n\v\f\r')
def is_separator(self, c):
return self.in_class(c, '{}()<>[]!;')
def in_comment_class(self, c):
return self.in_class(c, '#')
def in_string_quote_class(self, c):
return self.in_class(c, '"\'')
def in_printable_class(self, c):
return self.in_class(c, string.printable)
def set_error_stream(self, error):
self.error = error
def has_error(self):
return self.err > 0
def at_eof(self):
return self.eof
def input_eof(self):
self.eof = 1
self.input_char(-1)
def input(self, buf):
if not buf or len(buf) == 0:
self.input_eof()
else:
for c in buf:
self.input_char(c)
def input_char(self, c):
if self.at_eof():
pass
elif c == '\n':
self.line_no += 1
self.char_no = 0
else:
self.char_no += 1
if self.state is None:
self.begin_start(None)
self.state.fn(c)
def ready(self):
return len(self.val) > 0
def get_val(self):
v = self.val[0]
self.val = self.val[1:]
return v
def get_all(self):
return self.val
def begin_start(self, c):
self.state = ParserState(self.state_start)
def end_start(self):
self.val += self.state.val
self.pop_state()
def state_start(self, c):
if self.at_eof():
self.end_start()
elif self.in_space_class(c):
pass
elif self.in_comment_class(c):
self.begin_comment(c)
elif c == k_list_open:
self.begin_list(c)
elif c == k_list_close:
raise ParseError(self, "syntax error: "+c)
elif self.in_string_quote_class(c):
self.begin_string(c)
elif self.in_printable_class(c):
self.begin_atom(c)
elif c == chr(4):
# ctrl-D, EOT: end-of-text.
self.input_eof()
else:
raise ParseError(self, "invalid character: code %d" % ord(c))
def begin_comment(self, c):
self.push_state(self.state_comment)
self.state.buf += c
def end_comment(self):
self.pop_state()
def state_comment(self, c):
if c == '\n' or self.at_eof():
self.end_comment()
else:
self.state.buf += c
def begin_string(self, c):
self.push_state(self.state_string)
self.state.delim = c
def end_string(self):
val = self.state.buf
self.state.parent.val.append(val)
self.pop_state()
def state_string(self, c):
if self.at_eof():
raise ParseError(self, "unexpected EOF")
elif c == self.state.delim:
self.end_string()
elif c == '\\':
self.push_state(self.state_escape)
else:
self.state.buf += c
def state_escape(self, c):
if self.at_eof():
raise ParseError(self, "unexpected EOF")
d = escapes.get(c)
if d:
self.state.parent.buf += d
self.pop_state()
elif c == 'x':
self.state.fn = self.state_hex
self.state.val = 0
else:
self.state.fn = self.state_octal
self.state.val = 0
self.input_char(c)
def state_octal(self, c):
def octaldigit(c):
self.state.val *= 8
self.state.val += ord(c) - ord('0')
self.state.buf += c
if self.state.val < 0 or self.state.val > 0xff:
raise ParseError(self, "invalid octal escape: out of range " + self.state.buf)
if len(self.state.buf) == 3:
octaldone()
def octaldone():
d = chr(self.state.val)
self.state.parent.buf += d
self.pop_state()
if self.at_eof():
raise ParseError(self, "unexpected EOF")
elif '0' <= c <= '7':
octaldigit(c)
elif len(self.buf):
octaldone()
self.input_char(c)
def state_hex(self, c):
def hexdone():
d = chr(self.state.val)
self.state.parent.buf += d
self.pop_state()
def hexdigit(c, d):
self.state.val *= 16
self.state.val += ord(c) - ord(d)
self.state.buf += c
if self.state.val < 0 or self.state.val > 0xff:
raise ParseError(self, "invalid hex escape: out of range " + self.state.buf)
if len(self.state.buf) == 2:
hexdone()
if self.at_eof():
raise ParseError(self, "unexpected EOF")
elif '0' <= c <= '9':
hexdigit(c, '0')
elif 'A' <= c <= 'F':
hexdigit(c, 'A')
elif 'a' <= c <= 'f':
hexdigit(c, 'a')
elif len(buf):
hexdone()
self.input_char(c)
def begin_atom(self, c):
self.push_state(self.state_atom)
self.state.buf = c
def end_atom(self):
val = self.state.buf
self.state.parent.val.append(val)
self.pop_state()
def state_atom(self, c):
if self.at_eof():
self.end_atom()
elif (self.is_separator(c) or
self.in_space_class(c) or
self.in_comment_class(c)):
self.end_atom()
self.input_char(c)
else:
self.state.buf += c
def begin_list(self, c):
self.push_state(self.state_list)
def end_list(self):
val = self.state.val
self.state.parent.val.append(val)
self.pop_state()
def state_list(self, c):
if self.at_eof():
raise ParseError(self, "unexpected EOF")
elif c == k_list_close:
self.end_list()
else:
self.state_start(c)
def atomp(sxpr):
if sxpr.isalnum() or sxpr == '@':
return 1
for c in sxpr:
if c in string.whitespace: return 0
if c in '"\'\\(){}[]<>$#&%^': return 0
if c in string.ascii_letters: continue
if c in string.digits: continue
if c in '.-_:/~': continue
return 0
return 1
def show(sxpr, out=sys.stdout):
if isinstance(sxpr, types.ListType):
out.write(k_list_open)
i = 0
for x in sxpr:
if i: out.write(' ')
show(x, out)
i += 1
out.write(k_list_close)
elif isinstance(sxpr, types.StringType) and atomp(sxpr):
out.write(sxpr)
else:
#out.write("'" + str(sxpr) + "'")
out.write(repr(str(sxpr)))
def show_xml(sxpr, out=sys.stdout):
if isinstance(sxpr, types.ListType):
element = name(sxpr)
out.write('<%s' % element)
for attr in attributes(sxpr):
out.write(' %s=%s' % (attr[0], attr[1]))
out.write('>')
i = 0
for x in children(sxpr):
if i: out.write(' ')
show_xml(x, out)
i += 1
out.write('</%s>' % element)
elif isinstance(sxpr, types.StringType) and atomp(sxpr):
out.write(sxpr)
else:
out.write(str(sxpr))
def elementp(sxpr, elt=None):
return (isinstance(sxpr, types.ListType)
and len(sxpr)
and (None == elt or sxpr[0] == elt))
def name(sxpr):
val = None
if isinstance(sxpr, types.StringType):
val = sxpr
elif isinstance(sxpr, types.ListType) and len(sxpr):
val = sxpr[0]
return val
def attributes(sxpr):
val = []
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
attr = sxpr[1]
if elementp(attr, k_attr_open):
val = attr[1:]
return val
def attribute(sxpr, key, val=None):
for x in attributes(sxpr):
if x[0] == key:
val = x[1]
break
return val
def children(sxpr, elt=None):
val = []
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
i = 1
x = sxpr[i]
if elementp(x, k_attr_open):
i += 1
val = sxpr[i : ]
if elt:
def iselt(x):
return elementp(x, elt)
val = filter(iselt, val)
return val
def child(sxpr, elt, val=None):
for x in children(sxpr):
if elementp(x, elt):
val = x
break
return val
def child_at(sxpr, index, val=None):
kids = children(sxpr)
if len(kids) > index:
val = kids[index]
return val
def child0(sxpr, val=None):
return child_at(sxpr, 0, val)
def child1(sxpr, val=None):
return child_at(sxpr, 1, val)
def child2(sxpr, val=None):
return child_at(sxpr, 2, val)
def child3(sxpr, val=None):
return child_at(sxpr, 3, val)
def child4(sxpr, val=None):
return child_at(sxpr, 4, val)
def child_value(sxpr, elt, val=None):
kid = child(sxpr, elt)
if kid:
val = child_at(kid, 0, val)
return val
def has_id(sxpr, id):
"""Test if an s-expression has a given id.
"""
return attribute(sxpr, 'id') == id
def with_id(sxpr, id, val=None):
"""Find the first s-expression with a given id, at any depth.
sxpr s-exp or list
id id
val value if not found (default None)
return s-exp or val
"""
if isinstance(sxpr, types.ListType):
for n in sxpr:
if has_id(n, id):
val = n
break
v = with_id(n, id)
if v is None: continue
val = v
break
return val
def child_with_id(sxpr, id, val=None):
"""Find the first child with a given id.
sxpr s-exp or list
id id
val value if not found (default None)
return s-exp or val
"""
if isinstance(sxpr, types.ListType):
for n in sxpr:
if has_id(n, id):
val = n
break
return val
def elements(sxpr, ctxt=None):
"""Generate elements (at any depth).
Visit elements in pre-order.
Values generated are (node, context)
The context is None if there is no parent, otherwise
(index, parent, context) where index is the node's index w.r.t its parent,
and context is the parent's context.
sxpr s-exp
returns generator
"""
yield (sxpr, ctxt)
i = 0
for n in children(sxpr):
if isinstance(n, types.ListType):
# Calling elements() recursively does not generate recursively,
# it just returns a generator object. So we must iterate over it.
for v in elements(n, (i, sxpr, ctxt)):
yield v
i += 1
def parse(io):
"""Completely parse all input from 'io'.
io input file object
returns list of values, None if incomplete
raises ParseError on parse error
"""
pin = Parser()
while 1:
buf = io.readline()
pin.input(buf)
if len(buf) == 0:
break
if pin.ready():
val = pin.get_all()
else:
val = None
return val
if __name__ == '__main__':
print ">main"
pin = Parser()
while 1:
buf = sys.stdin.read(1024)
#buf = sys.stdin.readline()
pin.input(buf)
while pin.ready():
val = pin.get_val()
print
print '****** val=', val
if len(buf) == 0:
break
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
#============================================================================
# Move the IP address from eth0 onto the Xen bridge (nbe-br).
# Works best if the bridge control utils (brctl) have been installed.
#============================================================================
import os
import os.path
import re
import sys
from getopt import getopt
CMD_IFCONFIG = '/sbin/ifconfig'
CMD_ROUTE = '/sbin/route'
CMD_BRCTL = '/usr/local/sbin/brctl'
def routes():
"""Return a list of the routes.
"""
fin = os.popen(CMD_ROUTE + ' -n', 'r')
routes = []
for x in fin:
if x.startswith('Kernel'): continue
if x.startswith('Destination'): continue
x = x.strip()
y = x.split()
z = { 'destination': y[0],
'gateway' : y[1],
'mask' : y[2],
'flags' : y[3],
'metric' : y[4],
'ref' : y[5],
'use' : y[6],
'interface' : y[7] }
routes.append(z)
return routes
def cmd(p, s):
"""Print and execute command 'p' with args 's'.
"""
global opts
c = p + ' ' + s
if opts.verbose: print c
if not opts.dryrun:
os.system(c)
def ifconfig(interface):
"""Return the ip config for an interface,
"""
fin = os.popen(CMD_IFCONFIG + ' %s' % interface, 'r')
inetre = re.compile('\s*inet\s*addr:(?P<address>\S*)\s*Bcast:(?P<broadcast>\S*)\s*Mask:(?P<mask>\S*)')
info = None
for x in fin:
m = inetre.match(x)
if not m: continue
info = m.groupdict()
info['interface'] = interface
break
return info
def reconfigure(interface, bridge):
"""Reconfigure an interface to be attached to a bridge, and give the bridge
the IP address etc. from interface. Move the default route to the interface
to the bridge.
"""
intf_info = ifconfig(interface)
if not intf_info:
print 'Interface not found:', interface
return
#bridge_info = ifconfig(bridge)
#if not bridge_info:
# print 'Bridge not found:', bridge
# return
route_info = routes()
intf_info['bridge'] = bridge
intf_info['gateway'] = None
for r in route_info:
if (r['destination'] == '0.0.0.0' and
'G' in r['flags'] and
r['interface'] == interface):
intf_info['gateway'] = r['gateway']
if not intf_info['gateway']:
print 'Gateway not found: ', interface
return
cmd(CMD_IFCONFIG, '%(interface)s 0.0.0.0' % intf_info)
cmd(CMD_IFCONFIG, '%(bridge)s %(address)s netmask %(mask)s broadcast %(broadcast)s up' % intf_info)
cmd(CMD_ROUTE, 'add default gateway %(gateway)s dev %(bridge)s' % intf_info)
if os.path.exists(CMD_BRCTL):
cmd(CMD_BRCTL, 'addif %(bridge)s %(interface)s' % intf_info)
defaults = {
'interface': 'eth0',
'bridge' : 'nbe-br',
'verbose' : 1,
'dryrun' : 0,
}
short_options = 'hvqni:b:'
long_options = ['help', 'verbose', 'quiet', 'interface=', 'bridge=']
def usage():
print """Usage:
%s [options]
Reconfigure routing so that <bridge> has the IP address from
<interface>. This lets IP carry on working when <interface>
is attached to <bridge> for virtual networking.
If brctl is available, <interface> is added to <bridge>,
so this can be run before any domains have been created.
""" % sys.argv[0]
print """
-i, --interface <interface> interface, default %(interface)s.
-b, --bridge <bridge> bridge, default %(bridge)s.
-v, --verbose Print commands.
-q, --quiet Don't print commands.
-n, --dry-run Don't execute commands.
-h, --help Print this help.
""" % defaults
sys.exit(1)
class Opts:
def __init__(self, defaults):
for (k, v) in defaults.items():
setattr(self, k, v)
pass
def main():
global opts
opts = Opts(defaults)
(options, args) = getopt(sys.argv[1:], short_options, long_options)
if args: usage()
for k, v in options:
if k in ['-h', '--help']:
usage()
elif k in ['-i', '--interface']:
opts.interface = v
elif k in ['-b', '--bridge']:
opts.bridge = v
elif k in ['-q', '--quiet']:
opts.verbose = 0
elif k in ['-v', '--verbose']:
opts.verbose = 1
elif k in ['-n', '--dry-run']:
opts.dryrun = 1
reconfigure(opts.interface, opts.bridge)
if __name__ == '__main__':
main()
+14
View File
@@ -0,0 +1,14 @@
from distutils.core import setup, Extension
PACKAGE = 'xenmgr'
VERSION = '1.0'
setup(name = PACKAGE,
version = VERSION,
description = 'Xen Management API',
author = 'Mike Wray',
author_email = 'mike.wray@hp.com',
packages = [ PACKAGE, PACKAGE + '.server' ],
package_dir = { PACKAGE: 'lib' },
)
+40
View File
@@ -0,0 +1,40 @@
#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Xen management daemon. Lives in /usr/sbin.
Provides console server and HTTP management api.
Run:
xend start
The daemon is stopped with:
xend stop
Unfortunately restarting it upsets the channel to dom0 and
domain management stops working - needs a reboot to fix.
"""
import os
import sys
from xenmgr.server import SrvConsoleServer
def main():
daemon = SrvConsoleServer.instance()
if not sys.argv[1:]:
print 'usage: %s {start|stop|restart}' % sys.argv[0]
elif os.fork():
pid, status = os.wait()
return status >> 8
elif sys.argv[1] == 'start':
return daemon.start()
elif sys.argv[1] == 'stop':
return daemon.stop()
elif sys.argv[1] == 'restart':
return daemon.stop() or daemon.start()
else:
print 'not an option:', sys.argv[1]
return 1
if __name__ == '__main__':
sys.exit(main())
+10
View File
@@ -0,0 +1,10 @@
#!/usr/bin/python
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
"""Xen management daemon. Lives in /usr/sbin.
Run after running xend to provide http access to the management API.
NO LONGER NEEDED.
"""
from xenmgr.server import SrvServer
SrvServer.main()
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.