mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
bitkeeper revision 1.1045 (40ec1aa7Tw6ojoQiO9Pu3Cz1YCaofA)
Merge boulderdash.cl.cam.ac.uk:/auto/groups/xeno/BK/xeno.bk into boulderdash.cl.cam.ac.uk:/auto/anfs/scratch/boulderdash/gm281/xeno-clone/xeno.bk
This commit is contained in:
commit
ab0d0fa800
128 files changed
+5811
-3725
No files matched your search
+42
-11
@@ -3,7 +3,7 @@ import re
|
||||
import socket
|
||||
import struct
|
||||
|
||||
def readlines(fd):
|
||||
def _readlines(fd):
|
||||
"""Version of readlines safe against EINTR.
|
||||
"""
|
||||
import errno
|
||||
@@ -21,7 +21,7 @@ def readlines(fd):
|
||||
lines.append(line)
|
||||
return lines
|
||||
|
||||
def readline(fd):
|
||||
def _readline(fd):
|
||||
"""Version of readline safe against EINTR.
|
||||
"""
|
||||
while 1:
|
||||
@@ -42,11 +42,14 @@ as it may have been moved onto the bridge.
|
||||
NBE_BRIDGE = 'nbe-br'
|
||||
|
||||
def get_current_ipaddr(dev='eth0'):
|
||||
"""Return a string containing the primary IP address for the given
|
||||
network interface (default 'eth0').
|
||||
"""Get the primary IP address for the given network interface.
|
||||
|
||||
dev network interface (default eth0)
|
||||
|
||||
returns interface address as a string
|
||||
"""
|
||||
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
|
||||
lines = readlines(fd)
|
||||
lines = _readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^\s+inet addr:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
|
||||
line )
|
||||
@@ -57,11 +60,14 @@ def get_current_ipaddr(dev='eth0'):
|
||||
return None
|
||||
|
||||
def get_current_ipmask(dev='eth0'):
|
||||
"""Return a string containing the primary IP netmask for the given
|
||||
network interface (default 'eth0').
|
||||
"""Get the primary IP netmask for a network interface.
|
||||
|
||||
dev network interface (default eth0)
|
||||
|
||||
returns interface netmask as a string
|
||||
"""
|
||||
fd = os.popen( '/sbin/ifconfig ' + dev + ' 2>/dev/null' )
|
||||
lines = readlines(fd)
|
||||
lines = _readlines(fd)
|
||||
for line in lines:
|
||||
m = re.search( '^.+Mask:([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+).*',
|
||||
line )
|
||||
@@ -72,11 +78,14 @@ def get_current_ipmask(dev='eth0'):
|
||||
return None
|
||||
|
||||
def get_current_ipgw(dev='eth0'):
|
||||
"""Return a string containing the IP gateway for the given
|
||||
network interface (default 'eth0').
|
||||
"""Get the IP gateway for a network interface.
|
||||
|
||||
dev network interface (default eth0)
|
||||
|
||||
returns gateway address as a string
|
||||
"""
|
||||
fd = os.popen( '/sbin/route -n' )
|
||||
lines = readlines(fd)
|
||||
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 )
|
||||
@@ -88,24 +97,46 @@ def get_current_ipgw(dev='eth0'):
|
||||
|
||||
def inet_aton(addr):
|
||||
"""Convert an IP addr in IPv4 dot notation into an int.
|
||||
|
||||
addr IP address as a string
|
||||
|
||||
returns integer
|
||||
"""
|
||||
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.
|
||||
|
||||
n IP address
|
||||
|
||||
returns string
|
||||
"""
|
||||
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.
|
||||
|
||||
addr IP address
|
||||
offset offset to add
|
||||
|
||||
returns new address
|
||||
"""
|
||||
n = inet_aton(addr)
|
||||
n += offset
|
||||
return inet_ntoa(n)
|
||||
|
||||
def check_subnet( ip, network, netmask ):
|
||||
"""Check if an IP address is in the subnet defined by
|
||||
a network address and mask'.
|
||||
|
||||
ip IP adress
|
||||
network network address
|
||||
netmask network mask
|
||||
|
||||
returns 1 if it is in the subnet, 0 if not
|
||||
"""
|
||||
n_ip = inet_aton(ip)
|
||||
n_net = inet_aton(network)
|
||||
n_mask = inet_aton(netmask)
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import types
|
||||
import sxp
|
||||
|
||||
class ArgError(StandardError):
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
"""Client API for the HTTP interface on xend.
|
||||
Callable as a script - see main().
|
||||
|
||||
This API is the 'control-plane' for xend.
|
||||
The 'data-plane' is done separately. For example, consoles
|
||||
are accessed via sockets on xend, but the list of consoles
|
||||
is accessible via this API.
|
||||
"""
|
||||
import sys
|
||||
import httplib
|
||||
@@ -27,6 +32,8 @@ class Foo(httplib.HTTPResponse):
|
||||
|
||||
|
||||
def sxprio(sxpr):
|
||||
"""Convert an sxpr to a string.
|
||||
"""
|
||||
io = StringIO()
|
||||
sxp.show(sxpr, out=io)
|
||||
print >> io
|
||||
@@ -80,6 +87,12 @@ def eventurl(location, root, id=''):
|
||||
return urljoin(location, root, 'event/', id)
|
||||
|
||||
def xend_request(url, method, data=None):
|
||||
"""Make a request to xend.
|
||||
|
||||
url xend request url
|
||||
method http method: POST or GET
|
||||
data request argument data (dict)
|
||||
"""
|
||||
urlinfo = urlparse.urlparse(url)
|
||||
(uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo
|
||||
if DEBUG: print url, urlinfo
|
||||
@@ -122,20 +135,37 @@ def xend_request(url, method, data=None):
|
||||
return val
|
||||
|
||||
def xend_get(url, args=None):
|
||||
"""Make a xend request using GET.
|
||||
Requests using GET are 'safe' and may be repeated without
|
||||
nasty side-effects.
|
||||
"""
|
||||
return xend_request(url, "GET", args)
|
||||
|
||||
def xend_call(url, data):
|
||||
"""Make xend request using POST.
|
||||
Requests using POST potentially cause side-effects and should
|
||||
not be repeated unless it really is wanted to do the side
|
||||
effect again.
|
||||
"""
|
||||
return xend_request(url, "POST", data)
|
||||
|
||||
class Xend:
|
||||
|
||||
"""Default location of the xend server."""
|
||||
SRV_DEFAULT = "localhost:8000"
|
||||
|
||||
"""Default path to the xend root on the server."""
|
||||
ROOT_DEFAULT = "/xend/"
|
||||
|
||||
def __init__(self, srv=None, root=None):
|
||||
self.bind(srv, root)
|
||||
|
||||
def bind(self, srv=None, root=None):
|
||||
"""Bind to a given server.
|
||||
|
||||
srv server location (host:port)
|
||||
root server xend root path
|
||||
"""
|
||||
if srv is None: srv = self.SRV_DEFAULT
|
||||
if root is None: root = self.ROOT_DEFAULT
|
||||
if not root.endswith('/'): root += '/'
|
||||
@@ -200,9 +230,10 @@ class Xend:
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'pause'})
|
||||
|
||||
def xend_domain_shutdown(self, id):
|
||||
def xend_domain_shutdown(self, id, reason):
|
||||
return xend_call(self.domainurl(id),
|
||||
{'op' : 'shutdown'})
|
||||
{'op' : 'shutdown',
|
||||
'reason' : reason })
|
||||
|
||||
def xend_domain_destroy(self, id):
|
||||
return xend_call(self.domainurl(id),
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
Needs to be persistent for one uptime.
|
||||
"""
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
from twisted.internet import reactor
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
|
||||
@@ -28,10 +31,19 @@ __all__ = [ "XendDomain" ]
|
||||
class XendDomain:
|
||||
"""Index of all domains. Singleton.
|
||||
"""
|
||||
|
||||
|
||||
"""Path to domain database."""
|
||||
dbpath = "domain"
|
||||
|
||||
"""Table of domain info indexed by domain id."""
|
||||
domain = {}
|
||||
|
||||
"""Table of configs for domain restart, indexed by domain id."""
|
||||
restarts = {}
|
||||
|
||||
"""Table of delayed calls."""
|
||||
schedule = {}
|
||||
|
||||
def __init__(self):
|
||||
self.xconsole = XendConsole.instance()
|
||||
# Table of domain info indexed by domain id.
|
||||
@@ -49,6 +61,53 @@ class XendDomain:
|
||||
print 'XendDomain> virq', val
|
||||
self.reap()
|
||||
|
||||
def schedule_later(self, _delay, _name, _fn, *args):
|
||||
"""Schedule a function to be called later (if not already scheduled).
|
||||
|
||||
_delay delay in seconds
|
||||
_name schedule name
|
||||
_fn function
|
||||
args arguments
|
||||
"""
|
||||
if self.schedule.get(_name): return
|
||||
self.schedule[_name] = reactor.callLater(_delay, _fn, *args)
|
||||
|
||||
def schedule_cancel(self, name):
|
||||
"""Cancel a scheduled function call.
|
||||
|
||||
name schedule name to cancel
|
||||
"""
|
||||
callid = self.schedule.get(name)
|
||||
if not callid:
|
||||
return
|
||||
if callid.active():
|
||||
callid.cancel()
|
||||
del self.schedule[name]
|
||||
|
||||
def reap_schedule(self, delay=0):
|
||||
"""Schedule reap to be called later.
|
||||
|
||||
delay delay in seconds
|
||||
"""
|
||||
self.schedule_later(delay, 'reap', self.reap)
|
||||
|
||||
def reap_cancel(self):
|
||||
"""Cancel any scheduled reap.
|
||||
"""
|
||||
self.schedule_cancel('reap')
|
||||
|
||||
def refresh_schedule(self, delay=0):
|
||||
"""Schedule refresh to be called later.
|
||||
|
||||
delay delay in seconds
|
||||
"""
|
||||
self.schedule_later(delay, 'refresh', self.refresh)
|
||||
|
||||
def refresh_cancel(self):
|
||||
"""Cancel any scheduled refresh.
|
||||
"""
|
||||
self.schedule_cancel('refresh')
|
||||
|
||||
def rm_all(self):
|
||||
"""Remove all domain info. Used after reboot.
|
||||
"""
|
||||
@@ -145,6 +204,7 @@ class XendDomain:
|
||||
"""Look for domains that have crashed or stopped.
|
||||
Tidy them up.
|
||||
"""
|
||||
self.reap_cancel()
|
||||
print 'XendDomain>reap>'
|
||||
domlist = xc.domain_getinfo()
|
||||
casualties = []
|
||||
@@ -158,12 +218,14 @@ class XendDomain:
|
||||
for d in casualties:
|
||||
id = str(d['dom'])
|
||||
print 'XendDomain>reap> died id=', id, d
|
||||
self.domain_destroy(id, refresh=0)
|
||||
self.final_domain_destroy(id)
|
||||
print 'XendDomain>reap<'
|
||||
|
||||
def refresh(self):
|
||||
"""Refresh domain list from Xen.
|
||||
"""
|
||||
self.refresh_cancel()
|
||||
print 'XendDomain>refresh>'
|
||||
domlist = xc.domain_getinfo()
|
||||
# Index the domlist by id.
|
||||
# Add entries for any domains we don't know about.
|
||||
@@ -184,7 +246,7 @@ class XendDomain:
|
||||
d.update(dominfo)
|
||||
else:
|
||||
self._delete_domain(d.id)
|
||||
self.reap()
|
||||
self.reap_schedule(1)
|
||||
|
||||
def refresh_domain(self, id):
|
||||
"""Refresh information for a single domain.
|
||||
@@ -267,21 +329,70 @@ class XendDomain:
|
||||
"""Shutdown domain (nicely).
|
||||
|
||||
id domain id
|
||||
reason shutdown type: poweroff, reboot, halt
|
||||
reason shutdown type: poweroff, reboot, suspend, halt
|
||||
"""
|
||||
dom = int(id)
|
||||
if dom <= 0:
|
||||
return 0
|
||||
self.domain_restart_schedule(id, reason)
|
||||
eserver.inject('xend.domain.shutdown', [id, reason])
|
||||
if reason == 'halt':
|
||||
reason = 'poweroff'
|
||||
val = xend.domain_shutdown(dom, reason)
|
||||
self.refresh()
|
||||
self.refresh_schedule()
|
||||
return val
|
||||
|
||||
def domain_destroy(self, id, refresh=1):
|
||||
"""Terminate domain immediately.
|
||||
|
||||
def domain_restart_schedule(self, id, reason):
|
||||
"""Schedule a restart for a domain if it needs one.
|
||||
|
||||
id domain id
|
||||
reason shutdown reason
|
||||
"""
|
||||
if id in self.restarts:
|
||||
# Don't schedule if already there.
|
||||
return
|
||||
restart = 0
|
||||
if reason in ['poweroff', 'reboot']:
|
||||
dominfo = self.domain.get(id)
|
||||
if dominfo and (dominfo.autorestart or reason == 'reboot'):
|
||||
restart = 1
|
||||
# Clear autorestart flag to avoid multiple restarts.
|
||||
dominfo.autorestart = 0
|
||||
|
||||
if restart:
|
||||
self.restarts[id] = dominfo.config
|
||||
|
||||
def domain_restart_cancel(self, id):
|
||||
"""Cancel any restart scheduled for a domain.
|
||||
|
||||
id domain id
|
||||
"""
|
||||
dominfo = self.domain.get(id)
|
||||
if dominfo:
|
||||
dominfo.autorestart = 0
|
||||
if id in self.restarts:
|
||||
del self.restarts[id]
|
||||
|
||||
def domain_restarts(self):
|
||||
"""Execute any scheduled domain restarts for domains that have gone.
|
||||
"""
|
||||
for id in self.restarts.keys():
|
||||
if id in self.domain:
|
||||
# Don't execute restart for domains still running.
|
||||
continue
|
||||
config = self.restarts[id]
|
||||
# Remove it from the restarts.
|
||||
del self.restarts[id]
|
||||
try:
|
||||
self.domain_create(config)
|
||||
except:
|
||||
print >>sys.stderr, "XendDomain> Exception restarting domain"
|
||||
traceback.print_exc(sys.stderr)
|
||||
|
||||
def final_domain_destroy(self, id):
|
||||
"""Final destruction of a domain..
|
||||
|
||||
id domain id
|
||||
refresh send a domain destroy event if true
|
||||
"""
|
||||
dom = int(id)
|
||||
if dom <= 0:
|
||||
@@ -292,15 +403,26 @@ class XendDomain:
|
||||
val = dominfo.destroy()
|
||||
else:
|
||||
val = xc.domain_destroy(dom=dom)
|
||||
if refresh: self.refresh()
|
||||
return val
|
||||
|
||||
def domain_destroy(self, id):
|
||||
"""Terminate domain immediately.
|
||||
Camcels any restart for the domain.
|
||||
|
||||
id domain id
|
||||
"""
|
||||
self.domain_restart_cancel(id)
|
||||
val = self.final_domain_destroy(id)
|
||||
self.refresh_schedule()
|
||||
return val
|
||||
|
||||
def domain_migrate(self, id, dst):
|
||||
"""Start domain migration.
|
||||
|
||||
id domain id
|
||||
"""
|
||||
# Need a cancel too?
|
||||
# Don't forget to cancel restart for it.
|
||||
pass
|
||||
|
||||
def domain_save(self, id, dst, progress=0):
|
||||
|
||||
@@ -9,11 +9,13 @@ Author: Mike Wray <mike.wray@hpl.hp.com>
|
||||
"""
|
||||
|
||||
import string
|
||||
import types
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
import xen.util.ip
|
||||
@@ -78,8 +80,13 @@ def lookup_raw_partn(partition):
|
||||
|
||||
if not re.match( '/dev/', partition ):
|
||||
partition = '/dev/' + partition
|
||||
|
||||
drive = re.split( '[0-9]', partition )[0]
|
||||
|
||||
"""Try and match non-standard scsi raid arraysa
|
||||
"""
|
||||
if re.match( '/dev/cciss/c[0-9]+d[0-9]+p[0-9]+', partition ):
|
||||
drive = re.split( 'p[0-9]+', partition )[0]
|
||||
else:
|
||||
drive = re.split( '[0-9]', partition )[0]
|
||||
|
||||
if drive == partition:
|
||||
fd = os.popen( '/sbin/sfdisk -s ' + drive + ' 2>/dev/null' )
|
||||
@@ -283,7 +290,7 @@ def vm_restore(src, progress=0):
|
||||
d = restorefn(state_file=src, progress=progress)
|
||||
dom = int(d['dom'])
|
||||
if dom < 0:
|
||||
raise VMError('restore failed')
|
||||
raise VmError('restore failed')
|
||||
vmconfig = sxp.from_string(d['vmconfig'])
|
||||
vm.config = sxp.child_value(vmconfig, 'config')
|
||||
deferred = vm.dom_configure(dom)
|
||||
@@ -356,6 +363,8 @@ class XendDomainInfo:
|
||||
self.state = self.STATE_OK
|
||||
#todo: set to migrate info if migrating
|
||||
self.migrate = None
|
||||
#Whether to auto-restart
|
||||
self.autorestart = 0
|
||||
|
||||
def setdom(self, dom):
|
||||
self.dom = int(dom)
|
||||
@@ -412,6 +421,8 @@ class XendDomainInfo:
|
||||
try:
|
||||
self.name = sxp.child_value(config, 'name')
|
||||
self.memory = int(sxp.child_value(config, 'memory', '128'))
|
||||
if sxp.child(config, 'autorestart', None):
|
||||
self.autorestart = 1
|
||||
self.configure_backends()
|
||||
image = sxp.child_value(config, 'image')
|
||||
image_name = sxp.name(image)
|
||||
@@ -420,14 +431,18 @@ class XendDomainInfo:
|
||||
raise VmError('unknown image type: ' + image_name)
|
||||
image_handler(self, image)
|
||||
deferred = self.configure()
|
||||
def cbok(x):
|
||||
print 'vm_create> cbok', x
|
||||
return x
|
||||
def cberr(err):
|
||||
self.destroy()
|
||||
return err
|
||||
deferred.addCallback(cbok)
|
||||
deferred.addErrback(cberr)
|
||||
except StandardError, ex:
|
||||
# Catch errors, cleanup and re-raise.
|
||||
self.destroy()
|
||||
raise
|
||||
def cbok(x):
|
||||
print 'vm_create> cbok', x
|
||||
return x
|
||||
deferred.addCallback(cbok)
|
||||
print 'vm_create<'
|
||||
return deferred
|
||||
|
||||
@@ -503,6 +518,9 @@ class XendDomainInfo:
|
||||
devices have been released.
|
||||
"""
|
||||
if self.dom is None: return 0
|
||||
chan = xend.getDomChannel(self.dom)
|
||||
if chan:
|
||||
chan.close()
|
||||
return xc.domain_destroy(dom=self.dom)
|
||||
|
||||
def cleanup(self):
|
||||
@@ -611,7 +629,7 @@ class XendDomainInfo:
|
||||
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)
|
||||
raise VmError('Kernel ramdisk does not exist: %s' % ramdisk)
|
||||
print 'create-domain> init_domain...'
|
||||
self.init_domain()
|
||||
print 'create_domain>', 'dom=', self.dom
|
||||
@@ -691,12 +709,16 @@ class XendDomainInfo:
|
||||
"""
|
||||
d = dom_get(dom)
|
||||
if not d:
|
||||
raise VMError("Domain not found: %d" % dom)
|
||||
raise VmError("Domain not found: %d" % dom)
|
||||
try:
|
||||
self.setdom(dom)
|
||||
self.name = d['name']
|
||||
self.memory = d['memory']/1024
|
||||
deferred = self.configure()
|
||||
def cberr(err):
|
||||
self.destroy()
|
||||
return err
|
||||
deferred.addErrback(cberr)
|
||||
except StandardError, ex:
|
||||
self.destroy()
|
||||
raise
|
||||
@@ -807,10 +829,10 @@ def vm_dev_vbd(vm, val, index):
|
||||
vdev = index
|
||||
uname = sxp.child_value(val, 'uname')
|
||||
if not uname:
|
||||
raise VMError('vbd: Missing uname')
|
||||
raise VmError('vbd: Missing uname')
|
||||
dev = sxp.child_value(val, 'dev')
|
||||
if not dev:
|
||||
raise VMError('vbd: Missing dev')
|
||||
raise VmError('vbd: Missing dev')
|
||||
mode = sxp.child_value(val, 'mode', 'r')
|
||||
defer = make_disk(vm.dom, uname, dev, mode, vm.recreate)
|
||||
def fn(vbd):
|
||||
@@ -821,7 +843,7 @@ def vm_dev_vbd(vm, val, index):
|
||||
return defer
|
||||
|
||||
def parse_pci(val):
|
||||
if isinstance(val, StringType):
|
||||
if isinstance(val, types.StringType):
|
||||
radix = 10
|
||||
if val.startswith('0x') or val.startswith('0X'):
|
||||
radix = 16
|
||||
@@ -833,24 +855,24 @@ def parse_pci(val):
|
||||
def vm_dev_pci(vm, val, index):
|
||||
bus = sxp.child_value(val, 'bus')
|
||||
if not bus:
|
||||
raise VMError('pci: Missing bus')
|
||||
raise VmError('pci: Missing bus')
|
||||
dev = sxp.child_value(val, 'dev')
|
||||
if not dev:
|
||||
raise VMError('pci: Missing dev')
|
||||
raise VmError('pci: Missing dev')
|
||||
func = sxp.child_value(val, 'func')
|
||||
if not func:
|
||||
raise VMError('pci: Missing func')
|
||||
raise VmError('pci: Missing func')
|
||||
try:
|
||||
bus = parse_pci(bus)
|
||||
dev = parse_pci(dev)
|
||||
func = parse_pci(func)
|
||||
except:
|
||||
raise VMError('pci: invalid parameter')
|
||||
raise VmError('pci: invalid parameter')
|
||||
rc = xc.physdev_pci_access_modify(dom=vm.dom, bus=bus, dev=dev,
|
||||
func=func, enable=1)
|
||||
if rc < 0:
|
||||
#todo non-fatal
|
||||
raise VMError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
|
||||
raise VmError('pci: Failed to configure device: bus=%s dev=%s func=%s' %
|
||||
(bus, dev, func))
|
||||
return rc
|
||||
|
||||
|
||||
@@ -2,22 +2,125 @@
|
||||
|
||||
import sys
|
||||
import socket
|
||||
import time
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
from twisted.internet.protocol import Protocol
|
||||
from twisted.internet.protocol import ClientFactory
|
||||
|
||||
import sxp
|
||||
import XendDB
|
||||
import EventServer; eserver = EventServer.instance()
|
||||
|
||||
from xen.xend.packing import SxpPacker, SxpUnpacker
|
||||
from xen.xend import XendDomain
|
||||
xd = XendDomain.instance()
|
||||
|
||||
|
||||
XFRD_PORT = 8002
|
||||
|
||||
XFR_PROTO_MAJOR = 1
|
||||
XFR_PROTO_MINOR = 0
|
||||
|
||||
class Migrate(Protocol):
|
||||
|
||||
def __init__(self, minfo):
|
||||
self.packer = None
|
||||
self.unpacker = None
|
||||
self.minfo = minfo
|
||||
|
||||
def connectionMade(self):
|
||||
self.packer = SxpPacker(self.transport)
|
||||
self.unpacker = SxpPacker()
|
||||
# Send hello.
|
||||
self.packer.pack(['xfr.hello', XFR_PROTO_MAJOR, XFR_PROTO_MINOR])
|
||||
# Send migrate.
|
||||
vmconfig = self.minfo.vmconfig()
|
||||
if not vmconfig:
|
||||
self.loseConnection()
|
||||
return
|
||||
self.packer.pack(['xfr.migrate',
|
||||
self.minfo.src_dom,
|
||||
vmconfig,
|
||||
self.minfo.dst_host,
|
||||
self.minfo.dst_port])
|
||||
|
||||
def connectionLost(self, reason):
|
||||
self.minfo.closed(reason)
|
||||
|
||||
def dataReceived(self, data):
|
||||
try:
|
||||
self.unpacker.reset(data)
|
||||
val = self.unpacker.unpack()
|
||||
print 'dataReceived>', 'val=', val
|
||||
op = val[0]
|
||||
op.replace('.', '_')
|
||||
if op.startwith('xfr_'):
|
||||
fn = getattr(self, op, self.unknown)
|
||||
else:
|
||||
fn = self.unknown
|
||||
fn(val)
|
||||
except Exception, ex:
|
||||
print 'dataReceived>', ex
|
||||
pass
|
||||
|
||||
def unknown(self, val):
|
||||
print 'unknown>', val
|
||||
|
||||
def xfr_progress(self, val):
|
||||
print 'xfr_progress>', val
|
||||
|
||||
def xfr_error(self, val):
|
||||
# If we get an error with non-zero code the migrate failed.
|
||||
# An error with code zero indicates hello success.
|
||||
err = int(val[1])
|
||||
if not err: return
|
||||
self.minfo.error(err);
|
||||
self.loseConnection()
|
||||
|
||||
def xfr_ok(self, val):
|
||||
# An ok indicates migrate completed successfully, and contains
|
||||
# the new domain id on the remote system.
|
||||
dom = int(val[1])
|
||||
self.minfo.ok(dom)
|
||||
self.loseConnection()
|
||||
|
||||
class MigrateClientFactory(ClientFactory):
|
||||
|
||||
def __init__(self, minfo):
|
||||
ClientFactory.__init__(self)
|
||||
self.minfo = minfo
|
||||
|
||||
def startedConnecting(self, connector):
|
||||
print 'Started to connect', 'self=', self, 'connector=', connector
|
||||
|
||||
def buildProtocol(self, addr):
|
||||
print 'buildProtocol>', addr
|
||||
return Migrate(self.minfo)
|
||||
|
||||
def clientConnectionLost(self, connector, reason):
|
||||
print 'clientConnectionLost>', 'connector=', connector, 'reason=', reason
|
||||
|
||||
def clientConnectionFailed(self, connector, reason):
|
||||
print 'clientConnectionFailed>', 'connector=', connector, 'reason=', reason
|
||||
|
||||
|
||||
class XendMigrateInfo:
|
||||
|
||||
# states: begin, active, failed, succeeded?
|
||||
|
||||
def __init__(self, id, dom, dst):
|
||||
def __init__(self, id, dom, host, port):
|
||||
self.id = id
|
||||
self.state = 'begin'
|
||||
self.src_host = socket.gethostname()
|
||||
self.src_dom = dom
|
||||
self.dst_host = dst
|
||||
self.dst_port = port
|
||||
self.dst_dom = None
|
||||
self.start = 0
|
||||
self.deferred = defer.Deferred()
|
||||
|
||||
def set_state(self, state):
|
||||
self.state = state
|
||||
@@ -29,12 +132,33 @@ class XendMigrateInfo:
|
||||
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] ]
|
||||
sxpr_dst = ['dst', ['host', self.dst_host] ]
|
||||
if self.dst_dom:
|
||||
sxpr_dst.append(['domain', self.dst_dom])
|
||||
sxpr.append(sxpr_dst)
|
||||
return sxpr
|
||||
|
||||
|
||||
def vmconfig(self):
|
||||
dominfo = xd.domain_get(self.dom)
|
||||
if dominfo:
|
||||
val = return sxp.to_string(dominfo)
|
||||
else:
|
||||
val = None
|
||||
return None
|
||||
|
||||
def error(self, err):
|
||||
self.state = 'error'
|
||||
|
||||
def ok(self, dom):
|
||||
self.state = 'ok'
|
||||
self.dst_dom = dom
|
||||
|
||||
def close(self):
|
||||
if self.state =='ok':
|
||||
eserver.inject('xend.migrate.ok', self.sxpr())
|
||||
else:
|
||||
self.state = 'error'
|
||||
eserver.inject('xend.migrate.error', self.sxpr())
|
||||
|
||||
class XendMigrate:
|
||||
# Represents migration in progress.
|
||||
@@ -84,15 +208,15 @@ class XendMigrate:
|
||||
def migrate_get(self, id):
|
||||
return self.migrate.get(id)
|
||||
|
||||
def migrate_begin(self, dom, dst):
|
||||
def migrate_begin(self, dom, host):
|
||||
# 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)
|
||||
info = XenMigrateInfo(id, dom, host, XFRD_PORT)
|
||||
self._add_migrate(id, info)
|
||||
return id
|
||||
mcf = MigrateClientFactory(info)
|
||||
reactor.connectTCP('localhost', XFRD_PORT, mcf)
|
||||
return info.deferred
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
|
||||
@@ -0,0 +1,329 @@
|
||||
|
||||
# XDR-style packer/unpacker for sxpr.
|
||||
#
|
||||
# string -> [STRING] [len:u16] <len bytes>
|
||||
# atom -> [ATOM] [len:u16] <len bytes>
|
||||
# int -> [UINT] [value]
|
||||
# list -> [LIST] {1 elt}* 0
|
||||
# null -> [NULL]
|
||||
# none -> [NONE]
|
||||
# bool -> [BOOL] { 0:u8 | 1:u8 }
|
||||
#
|
||||
# types packed as u16.
|
||||
#
|
||||
# So (a b c) -> [LIST] 1 a 1 b 1 c 0
|
||||
# () -> [LIST] 0
|
||||
|
||||
import struct
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO as _StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO as _StringIO
|
||||
|
||||
import types
|
||||
|
||||
class Error(Exception):
|
||||
|
||||
def __init__(self, msg):
|
||||
self.msg = msg
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self.msg)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.msg)
|
||||
|
||||
|
||||
class ConversionError(Error):
|
||||
pass
|
||||
|
||||
BOOL_SIZE = 1
|
||||
BOOL_FMT = '>B'
|
||||
|
||||
BYTE_SIZE = 1
|
||||
BYTE_FMT = '>b'
|
||||
UBYTE_FMT = '>B'
|
||||
|
||||
SHORT_SIZE = 2
|
||||
SHORT_FMT = '>h'
|
||||
USHORT_FMT = '>H'
|
||||
|
||||
INT_SIZE = 4
|
||||
INT_FMT = '>l'
|
||||
UINT_FMT = '>L'
|
||||
|
||||
NONE_CODE = 0
|
||||
NULL_CODE = 1
|
||||
INT_CODE = 2
|
||||
STRING_CODE = 3
|
||||
ATOM_CODE = 4
|
||||
BOOL_CODE = 5
|
||||
LIST_CODE = 10
|
||||
|
||||
class Packer:
|
||||
|
||||
def __init__(self, io=None):
|
||||
self.reset(io=io)
|
||||
|
||||
def reset(self, io=None):
|
||||
if io is None:
|
||||
io = _StringIO()
|
||||
self.io = io
|
||||
|
||||
def get_buffer(self):
|
||||
return self.io.getvalue()
|
||||
|
||||
def get_io(self):
|
||||
return self.io
|
||||
|
||||
def struct_pack(self, fmt, x):
|
||||
try:
|
||||
self.io.write(struct.pack(fmt, x))
|
||||
except struct.error, msg:
|
||||
raise ConversionError, msg
|
||||
|
||||
def pack_none(self):
|
||||
pass
|
||||
|
||||
def pack_bool(self, x):
|
||||
# { '1' | '0' }
|
||||
print 'bool>', x
|
||||
if x:
|
||||
self.io.write('\1')
|
||||
else:
|
||||
self.io.write('\0')
|
||||
|
||||
def pack_byte(self, x):
|
||||
self.struct_pack(BYTE_FMT, x & 0xff)
|
||||
|
||||
def pack_char(self, x):
|
||||
print 'char>', x
|
||||
self.io.write(x)
|
||||
|
||||
def pack_ubyte(self, x):
|
||||
print 'ubyte>', x
|
||||
self.struct_pack(UBYTE_FMT, x & 0xff)
|
||||
|
||||
def pack_ushort(self, x):
|
||||
print 'ushort>', x
|
||||
self.struct_pack(USHORT_FMT, x & 0xffff)
|
||||
|
||||
def pack_short(self, x):
|
||||
print 'short>', x
|
||||
self.struct_pack(SHORT_FMT, x & 0xffff)
|
||||
|
||||
def pack_uint(self, x):
|
||||
print 'uint>', x
|
||||
self.struct_pack(UINT_FMT, x)
|
||||
|
||||
def pack_int(self, x):
|
||||
print 'int>', x
|
||||
self.struct_pack(INT_FMT, x)
|
||||
|
||||
def pack_uhyper(self, x):
|
||||
print 'uhyper>', x
|
||||
self.pack_uint(x>>32 & 0xffffffffL)
|
||||
self.pack_uint(x & 0xffffffffL)
|
||||
|
||||
pack_hyper = pack_uhyper
|
||||
|
||||
def pack_fstring(self, n, x):
|
||||
print 'fstring>', x
|
||||
self.io.write(x)
|
||||
|
||||
pack_fopaque = pack_fstring
|
||||
|
||||
def pack_string(self, x):
|
||||
print 'string>', x
|
||||
n = len(x)
|
||||
self.pack_ushort(n)
|
||||
self.pack_fstring(n, x)
|
||||
|
||||
pack_opaque = pack_string
|
||||
pack_bytes = pack_string
|
||||
|
||||
def pack_list(self, x, pack_item):
|
||||
print 'list>', x
|
||||
# { '1' <item> }* '0'
|
||||
for item in x:
|
||||
self.pack_bool(1)
|
||||
pack_item(item)
|
||||
self.pack_bool(0)
|
||||
|
||||
def pack_farray(self, x, pack_item):
|
||||
# <item>*
|
||||
# Can pass n and check length - but is it worth it?
|
||||
print 'farray>', list
|
||||
for item in x:
|
||||
pack_item(item)
|
||||
|
||||
def pack_array(self, x, pack_item):
|
||||
# n <item>*n
|
||||
print 'array>', x
|
||||
self.pack_uint(len(x))
|
||||
self.pack_farray(x, pack_item)
|
||||
|
||||
class Unpacker:
|
||||
|
||||
def __init__(self, data):
|
||||
self.reset(data)
|
||||
|
||||
def reset(self, data):
|
||||
if isinstance(data, types.StringType):
|
||||
data = _StringIO(data)
|
||||
self.io = data
|
||||
|
||||
def get_bytes(self, n):
|
||||
if n < 0:
|
||||
raise ConversionError('negative byte count')
|
||||
data = self.io.read(n)
|
||||
return data
|
||||
|
||||
def struct_unpack(self, fmt, n):
|
||||
data = self.get_bytes(n)
|
||||
try:
|
||||
return struct.unpack(fmt, data)[0]
|
||||
except struct.error, msg:
|
||||
raise ConversionError, msg
|
||||
|
||||
def unpack_none(self):
|
||||
return None
|
||||
|
||||
def unpack_bool(self):
|
||||
return self.struct_unpack(BOOL_FMT, BOOL_SIZE)
|
||||
|
||||
def unpack_char(self):
|
||||
return self.get_bytes(1)[0]
|
||||
|
||||
def unpack_byte(self):
|
||||
return self.struct_unpack(BYTE_FMT, BYTE_SIZE)
|
||||
|
||||
def unpack_ubyte(self):
|
||||
return self.struct_unpack(UBYTE_FMT, BYTE_SIZE)
|
||||
|
||||
def unpack_ushort(self):
|
||||
return self.struct_unpack(USHORT_FMT, SHORT_SIZE)
|
||||
|
||||
def unpack_short(self):
|
||||
return self.struct_unpack(SHORT_FMT, SHORT_SIZE)
|
||||
|
||||
def unpack_uint(self):
|
||||
x = self.struct_unpack(UINT_FMT, UINT_SIZE)
|
||||
try:
|
||||
return int(x)
|
||||
except OverflowError:
|
||||
return x
|
||||
|
||||
def unpack_int(self):
|
||||
return self.struct_unpack(INT_FMT, INT_SIZE)
|
||||
|
||||
def unpack_uhyper(self):
|
||||
hi = self.unpack_uint()
|
||||
lo = self.unpack_uint()
|
||||
return long(hi)<<32 | lo
|
||||
|
||||
def unpack_hyper(self):
|
||||
x = self.unpack_uhyper()
|
||||
if x >= 0x8000000000000000L:
|
||||
x = x - 0x10000000000000000L
|
||||
return x
|
||||
|
||||
def unpack_fstring(self, n):
|
||||
return self.get_bytes(n)
|
||||
|
||||
unpack_fopaque = unpack_fstring
|
||||
|
||||
def unpack_string(self):
|
||||
n = self.unpack_ushort()
|
||||
return self.unpack_fstring(n)
|
||||
|
||||
unpack_opaque = unpack_string
|
||||
unpack_bytes = unpack_string
|
||||
|
||||
def unpack_list(self, unpack_item):
|
||||
list = []
|
||||
while self.unpack_bool():
|
||||
list.append(unpack_item())
|
||||
return list
|
||||
|
||||
def unpack_farray(self, n, unpack_item):
|
||||
list = []
|
||||
for i in range(n):
|
||||
list.append(unpack_item())
|
||||
return list
|
||||
|
||||
def unpack_array(self, unpack_item):
|
||||
n = self.unpack_ushort()
|
||||
return self.unpack_farray(n, unpack_item)
|
||||
|
||||
class SxpPacker(Packer):
|
||||
|
||||
pack_code = Packer.pack_ushort
|
||||
|
||||
def pack(self, x):
|
||||
if isinstance(x, types.NoneType):
|
||||
self.pack_code(NONE_CODE)
|
||||
self.pack_none()
|
||||
elif isinstance(x, types.IntType):
|
||||
self.pack_code(INT_CODE)
|
||||
self.pack_int(x)
|
||||
elif isinstance(x, types.StringType):
|
||||
self.pack_code(STRING_CODE)
|
||||
self.pack_string(x)
|
||||
elif isinstance(x, types.ListType):
|
||||
self.pack_code(LIST_CODE)
|
||||
self.pack_list(x, self.pack)
|
||||
else:
|
||||
raise Error('invalid type ' + str(type(x)))
|
||||
|
||||
class SxpUnpacker(Unpacker):
|
||||
|
||||
unpack_code = Unpacker.unpack_ushort
|
||||
|
||||
def unpack(self):
|
||||
code = self.unpack_code()
|
||||
if code == NONE_CODE:
|
||||
val = self.unpack_none()
|
||||
elif code == INT_CODE:
|
||||
val = self.unpack_int()
|
||||
elif code == BOOL_CODE:
|
||||
val = self.unpack_bool()
|
||||
elif code == STRING_CODE:
|
||||
val = self.unpack_string()
|
||||
elif code == ATOM_CODE:
|
||||
val = self.unpack_string()
|
||||
elif code == LIST_CODE:
|
||||
val = self.unpack_list(self.unpack)
|
||||
else:
|
||||
raise Error('invalid code ' + str(code))
|
||||
return val
|
||||
|
||||
def main():
|
||||
d = "['vfarm', ['@', ['name', 'vfarm1']], ['memory', 1024], ['image', 'splinux'], ['args', 'root=/dev/nfs ip=dhcp'], [ 1, -1, 1000000]]"
|
||||
print"> len=", len(d), "d=", d
|
||||
obj = ['vfarm', ['@', ['name', 'vfarm1']],
|
||||
['memory', 1024],
|
||||
['image', 'splinux'],
|
||||
['args', 'root=/dev/nfs ip=dhcp'],
|
||||
[ 1, -1, 1000000] ]
|
||||
print "> obj=", obj
|
||||
pack = SxpPacker()
|
||||
pack.pack(obj)
|
||||
data = pack.get_buffer()
|
||||
print "> len=", len(data), "data=", data
|
||||
unpack = SxpUnpacker(data)
|
||||
obj_unpack = unpack.unpack()
|
||||
print "> obj=", obj_unpack
|
||||
#obj = [100,101,102, 999.00234, { 'a': 1, 'b': 2 } ]
|
||||
#pack.reset()
|
||||
#pack.pack_item(obj)
|
||||
#data = pack.get_buffer()
|
||||
#print "> obj=", obj
|
||||
#print "> len=", len(data), "data=", data
|
||||
#unpack.reset(data)
|
||||
#obj_unpack = unpack.unpack_item()
|
||||
#print "> obj=", obj_unpack
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -8,6 +8,7 @@ import types
|
||||
import StringIO
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
from twisted.internet import reactor
|
||||
from twisted.web import error
|
||||
from twisted.web import resource
|
||||
|
||||
@@ -14,6 +14,7 @@ import socket
|
||||
import pwd
|
||||
import re
|
||||
import StringIO
|
||||
import traceback
|
||||
|
||||
from twisted.internet import pollreactor
|
||||
pollreactor.install()
|
||||
@@ -22,6 +23,7 @@ from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
from twisted.internet import abstract
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
|
||||
from xen.lowlevel import xu
|
||||
|
||||
@@ -392,6 +394,12 @@ class EventProtocol(protocol.Protocol):
|
||||
eserver.inject(sxp.name(event), event)
|
||||
return ['ok']
|
||||
|
||||
def op_traceon(self, name, v):
|
||||
self.daemon.tracing(1)
|
||||
|
||||
def op_traceoff(self, name, v):
|
||||
self.daemon.tracing(0)
|
||||
|
||||
|
||||
class EventFactory(protocol.Factory):
|
||||
"""Asynchronous handler for the event server socket.
|
||||
@@ -424,6 +432,7 @@ class Daemon:
|
||||
"""
|
||||
def __init__(self):
|
||||
self.shutdown = 0
|
||||
self.traceon = 0
|
||||
|
||||
def daemon_pids(self):
|
||||
pids = []
|
||||
@@ -482,7 +491,7 @@ class Daemon:
|
||||
while code > 0:
|
||||
code = os.waitpid(-1, os.WNOHANG)
|
||||
|
||||
def start(self,trace=0):
|
||||
def start(self, trace=0):
|
||||
if self.cleanup(kill=False):
|
||||
return 1
|
||||
|
||||
@@ -506,7 +515,21 @@ class Daemon:
|
||||
# Child
|
||||
logfile = self.open_logfile()
|
||||
self.redirect_output(logfile)
|
||||
if trace:
|
||||
|
||||
self.tracing(trace)
|
||||
|
||||
self.run()
|
||||
return 0
|
||||
|
||||
def tracing(self, traceon):
|
||||
"""Turn tracing on or off.
|
||||
|
||||
traceon tracing flag
|
||||
"""
|
||||
if traceon == self.traceon:
|
||||
return
|
||||
self.traceon = traceon
|
||||
if traceon:
|
||||
self.tracefile = open('/var/log/xend.trace', 'w+', 1)
|
||||
self.traceindent = 0
|
||||
sys.settrace(self.trace)
|
||||
@@ -514,26 +537,35 @@ class Daemon:
|
||||
threading.settrace(self.trace) # Only in Python >= 2.3
|
||||
except:
|
||||
pass
|
||||
self.run()
|
||||
return 0
|
||||
|
||||
def print_trace(self,str):
|
||||
def print_trace(self, str):
|
||||
for i in range(self.traceindent):
|
||||
self.tracefile.write(" ")
|
||||
ch = " "
|
||||
if (i % 5):
|
||||
ch = ' '
|
||||
else:
|
||||
ch = '|'
|
||||
self.tracefile.write(ch)
|
||||
self.tracefile.write(str)
|
||||
|
||||
def trace(self, frame, event, arg):
|
||||
if not self.traceon:
|
||||
print >>self.tracefile
|
||||
print >>self.tracefile, '-' * 20, 'TRACE OFF', '-' * 20
|
||||
self.tracefile.close()
|
||||
self.tracefile = None
|
||||
return None
|
||||
if event == 'call':
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
m = re.search('.*xenmgr/(.*)', code.co_filename)
|
||||
m = re.search('.*xend/(.*)', filename)
|
||||
if not m:
|
||||
return None
|
||||
modulename = m.group(1)
|
||||
if re.search('sxp.py', modulename):
|
||||
return None
|
||||
self.traceindent += 1
|
||||
self.print_trace("++++ %s:%s\n"
|
||||
self.print_trace("> %s:%s\n"
|
||||
% (modulename, code.co_name))
|
||||
elif event == 'line':
|
||||
filename = frame.f_code.co_filename
|
||||
@@ -543,15 +575,18 @@ class Daemon:
|
||||
elif event == 'return':
|
||||
code = frame.f_code
|
||||
filename = code.co_filename
|
||||
m = re.search('.*xenmgr/(.*)', code.co_filename)
|
||||
m = re.search('.*xend/(.*)', filename)
|
||||
if not m:
|
||||
return None
|
||||
modulename = m.group(1)
|
||||
self.print_trace("---- %s:%s\n"
|
||||
self.print_trace("< %s:%s\n"
|
||||
% (modulename, code.co_name))
|
||||
self.traceindent -= 1
|
||||
elif event == 'exception':
|
||||
pass
|
||||
self.print_trace("! Exception:\n")
|
||||
(ex, val, tb) = arg
|
||||
traceback.print_exception(ex, val, tb, 10, self.tracefile)
|
||||
#del tb
|
||||
return self.trace
|
||||
|
||||
def open_logfile(self):
|
||||
@@ -629,6 +664,15 @@ class Daemon:
|
||||
reactor.diconnectAll()
|
||||
sys.exit(0)
|
||||
|
||||
def getDomChannel(self, dom):
|
||||
"""Get the channel to a domain.
|
||||
|
||||
dom domain
|
||||
|
||||
returns channel (or None)
|
||||
"""
|
||||
return self.channelF.getDomChannel(dom)
|
||||
|
||||
def blkif_set_control_domain(self, dom, recreate=0):
|
||||
"""Set the block device backend control domain.
|
||||
"""
|
||||
|
||||
@@ -27,7 +27,11 @@ class SrvDomain(SrvDir):
|
||||
return val
|
||||
|
||||
def op_shutdown(self, op, req):
|
||||
val = self.xd.domain_shutdown(self.dom.id)
|
||||
#val = self.xd.domain_shutdown(self.dom.id)
|
||||
fn = FormFn(self.xd.domain_shutdown,
|
||||
[['dom', 'int'],
|
||||
['reason', 'str']])
|
||||
val = fn(req.args, {'dom': self.dom.id})
|
||||
req.setResponseCode(202)
|
||||
req.setHeader("Location", "%s/.." % req.prePathURL())
|
||||
return val
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import traceback
|
||||
from StringIO import StringIO
|
||||
|
||||
from twisted.protocols import http
|
||||
@@ -24,6 +25,7 @@ class SrvDomainDir(SrvDir):
|
||||
val = None
|
||||
try:
|
||||
dom = self.xd.domain_get(x)
|
||||
if not dom: raise KeyError('No such domain')
|
||||
val = SrvDomain(dom)
|
||||
except KeyError, ex:
|
||||
print 'SrvDomainDir>', ex
|
||||
@@ -38,6 +40,9 @@ class SrvDomainDir(SrvDir):
|
||||
return v
|
||||
|
||||
def op_create(self, op, req):
|
||||
"""Create a domain.
|
||||
Expects the domain config in request parameter 'config' in SXP format.
|
||||
"""
|
||||
ok = 0
|
||||
try:
|
||||
configstring = req.args.get('config')[0]
|
||||
@@ -48,7 +53,8 @@ class SrvDomainDir(SrvDir):
|
||||
config = pin.get_val()
|
||||
ok = 1
|
||||
except Exception, ex:
|
||||
print 'op_create>', ex
|
||||
print 'op_create> Exception in config', ex
|
||||
traceback.print_exc()
|
||||
if not ok:
|
||||
req.setResponseCode(http.BAD_REQUEST, "Invalid configuration")
|
||||
return "Invalid configuration"
|
||||
@@ -57,19 +63,20 @@ class SrvDomainDir(SrvDir):
|
||||
"Invalid configuration")
|
||||
try:
|
||||
deferred = self.xd.domain_create(config)
|
||||
deferred.addCallback(self._cb_op_create, configstring, req)
|
||||
deferred.addCallback(self._op_create_cb, configstring, req)
|
||||
deferred.addErrback(self._op_create_err, req)
|
||||
return deferred
|
||||
except Exception, ex:
|
||||
raise
|
||||
#return ['err', str(ex) ]
|
||||
#req.setResponseCode(http.BAD_REQUEST, "Error creating domain")
|
||||
#return str(ex)
|
||||
print 'op_create> Exception creating domain:'
|
||||
traceback.print_exc()
|
||||
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):
|
||||
def _op_create_cb(self, dominfo, configstring, req):
|
||||
"""Callback to handle deferred domain creation.
|
||||
"""
|
||||
dom = dominfo.id
|
||||
@@ -89,7 +96,16 @@ class SrvDomainDir(SrvDir):
|
||||
out.close()
|
||||
return val
|
||||
|
||||
def _op_create_err(self, err, req):
|
||||
"""Callback to handle errors in deferred domain creation.
|
||||
"""
|
||||
print 'op_create> Deferred Exception creating domain:', err
|
||||
req.setResponseCode(http.BAD_REQUEST, "Error creating domain")
|
||||
return str(err)
|
||||
|
||||
def op_restore(self, op, req):
|
||||
"""Restore a domain from file.
|
||||
"""
|
||||
fn = FormFn(self.xd.domain_restore,
|
||||
[['file', 'str']])
|
||||
val = fn(req.args)
|
||||
@@ -131,6 +147,8 @@ class SrvDomainDir(SrvDir):
|
||||
req.write('</ul>')
|
||||
|
||||
def form(self, req):
|
||||
"""Generate the form(s) for domain dir operations.
|
||||
"""
|
||||
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>')
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
@@ -18,53 +21,78 @@ class BlkifControllerFactory(controller.ControllerFactory):
|
||||
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, recreate=0):
|
||||
d = self.addDeferred()
|
||||
"""Create a block device controller for a domain.
|
||||
|
||||
dom domain
|
||||
recreate if true it's a recreate (after xend restart)
|
||||
"""
|
||||
d = defer.Deferred()
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
if blkif:
|
||||
self.callDeferred(blkif)
|
||||
d.callback(blkif)
|
||||
else:
|
||||
blkif = BlkifController(self, dom)
|
||||
self.addInstance(blkif)
|
||||
if recreate:
|
||||
self.callDeferred(blkif)
|
||||
d.callback(blkif)
|
||||
else:
|
||||
blkif.send_be_create()
|
||||
d1 = defer.Deferred()
|
||||
d1.addCallback(self.respond_be_create, d)
|
||||
d1.addErrback(d.errback)
|
||||
blkif.send_be_create(response=d1)
|
||||
return d
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the block devices for a domain.
|
||||
|
||||
dom domain
|
||||
|
||||
returns devices
|
||||
"""
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
return (blkif and blkif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vdev):
|
||||
"""Get a block device from a domain.
|
||||
|
||||
dom domain
|
||||
vdev device index
|
||||
|
||||
returns device
|
||||
"""
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
return (blkif and blkif.getDevice(vdev)) or None
|
||||
|
||||
def setControlDomain(self, dom, recreate=0):
|
||||
"""Set the back-end block device controller domain.
|
||||
|
||||
dom domain
|
||||
recreate if true it's a recreate (after xend restart)
|
||||
"""
|
||||
if self.dom == dom: return
|
||||
self.deregisterChannel()
|
||||
if not recreate:
|
||||
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 getControlDomain(self):
|
||||
"""Get the back-end block device controller domain.
|
||||
"""
|
||||
return self.dom
|
||||
|
||||
def reattachDevice(self, dom, vdev):
|
||||
"""Reattach a device (on changing control domain).
|
||||
|
||||
dom domain
|
||||
vdev device index
|
||||
"""
|
||||
blkif = self.getInstanceByDom(dom)
|
||||
if blkif:
|
||||
blkif.reattachDevice(vdev)
|
||||
@@ -83,17 +111,20 @@ class BlkifControllerFactory(controller.ControllerFactory):
|
||||
return attached
|
||||
|
||||
def reattached(self):
|
||||
"""Notify all block interface we have been reattached
|
||||
(after changing control domain).
|
||||
"""
|
||||
for blkif in self.getInstances():
|
||||
blkif.reattached()
|
||||
|
||||
def recv_be_create(self, msg, req):
|
||||
#print 'recv_be_create>'
|
||||
def respond_be_create(self, msg, d):
|
||||
print 'respond_be_create>'
|
||||
val = unpackMsg('blkif_be_create_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
self.callDeferred(blkif)
|
||||
d.callback(blkif)
|
||||
|
||||
def recv_be_connect(self, msg, req):
|
||||
#print 'recv_be_create>'
|
||||
def respond_be_connect(self, msg):
|
||||
print 'respond_be_connect>', self
|
||||
val = unpackMsg('blkif_be_connect_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
if blkif:
|
||||
@@ -101,25 +132,30 @@ class BlkifControllerFactory(controller.ControllerFactory):
|
||||
else:
|
||||
pass
|
||||
|
||||
def recv_be_vbd_create(self, msg, req):
|
||||
#print 'recv_be_vbd_create>'
|
||||
def respond_be_vbd_create(self, msg, d):
|
||||
print 'recv_be_vbd_create>', self
|
||||
val = unpackMsg('blkif_be_vbd_create_t', msg)
|
||||
blkif = self.getInstanceByDom(val['domid'])
|
||||
if blkif:
|
||||
blkif.send_be_vbd_grow(val['vdevice'])
|
||||
d1 = defer.Deferred()
|
||||
d1.addCallback(self.respond_be_vbd_grow, d)
|
||||
if d: d1.addErrback(d.errback)
|
||||
blkif.send_be_vbd_grow(val['vdevice'], response=d1)
|
||||
else:
|
||||
pass
|
||||
|
||||
def recv_be_vbd_grow(self, msg, req):
|
||||
#print 'recv_be_vbd_grow>'
|
||||
def respond_be_vbd_grow(self, msg, d):
|
||||
print 'recv_be_vbd_grow>', self
|
||||
val = unpackMsg('blkif_be_vbd_grow_t', msg)
|
||||
# Check status?
|
||||
if self.attached:
|
||||
self.callDeferred(0)
|
||||
if d:
|
||||
d.callback(0)
|
||||
else:
|
||||
self.reattachDevice(val['domid'], val['vdevice'])
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
print 'recv_be_driver_status_changed>', self, req
|
||||
val = unpackMsg('blkif_be_driver_status_changed_t', msg)
|
||||
status = val['status']
|
||||
if status == BLKIF_DRIVER_STATUS_UP and not self.attached:
|
||||
@@ -147,7 +183,6 @@ class BlkDev(controller.Dev):
|
||||
return val
|
||||
|
||||
def destroy(self):
|
||||
print 'BlkDev>destroy>', self.vdev
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
self.controller.send_be_vbd_destroy(self.vdev)
|
||||
|
||||
@@ -157,7 +192,6 @@ class BlkifController(controller.Controller):
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
#print 'BlkifController> dom=', dom
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.devices = {}
|
||||
|
||||
@@ -172,7 +206,6 @@ class BlkifController(controller.Controller):
|
||||
self.attached = 1
|
||||
self.evtchn = None
|
||||
self.registerChannel()
|
||||
#print 'BlkifController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val = ['blkif', ['dom', self.dom]]
|
||||
@@ -182,11 +215,6 @@ class BlkifController(controller.Controller):
|
||||
self.evtchn['port2']])
|
||||
return val
|
||||
|
||||
def lostChannel(self):
|
||||
print 'BlkifController>lostChannel>', 'dom=', self.dom
|
||||
#self.destroyDevices()
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def getDevices(self):
|
||||
return self.devices.values()
|
||||
|
||||
@@ -201,26 +229,32 @@ class BlkifController(controller.Controller):
|
||||
|
||||
def attachDevice(self, vdev, mode, segment, recreate=0):
|
||||
"""Attach a device to the specified interface.
|
||||
|
||||
vdev device index
|
||||
mode read/write mode
|
||||
segment segment
|
||||
recreate if true it's being recreated (after xend restart)
|
||||
|
||||
returns deferred
|
||||
"""
|
||||
#print 'BlkifController>attach_device>', self.dom, vdev, mode, segment
|
||||
dev = self.addDevice(vdev, mode, segment)
|
||||
if not dev: return -1
|
||||
d = defer.Deferred()
|
||||
if recreate:
|
||||
d = defer.Deferred()
|
||||
d.callback(self)
|
||||
else:
|
||||
self.send_be_vbd_create(vdev)
|
||||
d = self.factory.addDeferred()
|
||||
d1 = defer.Deferred()
|
||||
d1.addCallback(self.factory.respond_be_vbd_create, d)
|
||||
d1.addErrback(d.errback)
|
||||
self.send_be_vbd_create(vdev, response=d1)
|
||||
return d
|
||||
|
||||
def destroy(self):
|
||||
print 'BlkifController>destroy> dom=', self.dom
|
||||
def cb_destroy(val):
|
||||
self.send_be_destroy()
|
||||
d = self.factory.addDeferred()
|
||||
d = defer.Deferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.send_be_disconnect()
|
||||
#self.destroyDevices()
|
||||
self.send_be_disconnect(response=d)
|
||||
|
||||
def destroyDevices(self):
|
||||
for dev in self.getDevices():
|
||||
@@ -232,7 +266,9 @@ class BlkifController(controller.Controller):
|
||||
self.attached = 0
|
||||
for dev in self.devices.values():
|
||||
dev.attached = 0
|
||||
self.send_be_vbd_create(vdev)
|
||||
d1 = defer.Deferred()
|
||||
d1.addCallback(self.factory.respond_be_vbd_create, None)
|
||||
self.send_be_vbd_create(vdev, response=d1)
|
||||
|
||||
def reattachDevice(self, vdev):
|
||||
"""Reattach a device, when the back-end control domain has changed.
|
||||
@@ -273,51 +309,47 @@ class BlkifController(controller.Controller):
|
||||
'blkif_handle' : val['handle'],
|
||||
'evtchn' : self.evtchn['port1'],
|
||||
'shmem_frame' : val['shmem_frame'] })
|
||||
self.factory.writeRequest(msg)
|
||||
pass
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.factory.respond_be_connect)
|
||||
self.factory.writeRequest(msg, response=d)
|
||||
|
||||
#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):
|
||||
def send_fe_interface_status_changed(self, response=None):
|
||||
msg = packMsg('blkif_fe_interface_status_changed_t',
|
||||
{ 'handle' : 0,
|
||||
'status' : BLKIF_INTERFACE_STATUS_CONNECTED,
|
||||
'evtchn' : self.evtchn['port2'] })
|
||||
self.writeRequest(msg)
|
||||
self.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_create(self):
|
||||
def send_be_create(self, response=None):
|
||||
msg = packMsg('blkif_be_create_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_disconnect(self):
|
||||
def send_be_disconnect(self, response=None):
|
||||
print '>BlkifController>send_be_disconnect>', 'dom=', self.dom
|
||||
msg = packMsg('blkif_be_disconnect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_destroy(self):
|
||||
def send_be_destroy(self, response=None):
|
||||
print '>BlkifController>send_be_destroy>', 'dom=', self.dom
|
||||
msg = packMsg('blkif_be_destroy_t',
|
||||
{ 'domid' : self.dom,
|
||||
'blkif_handle' : 0 })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_vbd_create(self, vdev):
|
||||
def send_be_vbd_create(self, vdev, response=None):
|
||||
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)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_vbd_grow(self, vdev):
|
||||
def send_be_vbd_grow(self, vdev, response=None):
|
||||
dev = self.devices[vdev]
|
||||
msg = packMsg('blkif_be_vbd_grow_t',
|
||||
{ 'domid' : self.dom,
|
||||
@@ -326,9 +358,9 @@ class BlkifController(controller.Controller):
|
||||
'extent.device' : dev.device,
|
||||
'extent.sector_start' : dev.start_sector,
|
||||
'extent.sector_length' : dev.nr_sectors })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_vbd_destroy(self, vdev):
|
||||
def send_be_vbd_destroy(self, vdev, response=None):
|
||||
print '>BlkifController>send_be_vbd_destroy>', 'dom=', self.dom, 'vdev=', vdev
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
dev = self.devices[vdev]
|
||||
@@ -337,5 +369,5 @@ class BlkifController(controller.Controller):
|
||||
'blkif_handle' : 0,
|
||||
'vdevice' : dev.vdev })
|
||||
del self.devices[vdev]
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import xen.lowlevel.xc; xc = xen.lowlevel.xc.new()
|
||||
from xen.lowlevel import xu
|
||||
from messages import msgTypeName
|
||||
@@ -24,14 +26,11 @@ class ChannelFactory:
|
||||
self.notifier = xu.notifier()
|
||||
|
||||
def addChannel(self, channel):
|
||||
"""Add a channel.
|
||||
"""Add a channel. Registers with the notifier.
|
||||
"""
|
||||
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):
|
||||
"""Get the channel with the given index (if any).
|
||||
@@ -40,6 +39,7 @@ class ChannelFactory:
|
||||
|
||||
def delChannel(self, idx):
|
||||
"""Remove the channel with the given index (if any).
|
||||
Deregisters with the notifier.
|
||||
"""
|
||||
if idx in self.channels:
|
||||
del self.channels[idx]
|
||||
@@ -48,15 +48,31 @@ class ChannelFactory:
|
||||
def domChannel(self, dom):
|
||||
"""Get the channel for the given domain.
|
||||
Construct if necessary.
|
||||
|
||||
dom domain
|
||||
|
||||
returns channel
|
||||
"""
|
||||
chan = self.getDomChannel(dom)
|
||||
if not chan:
|
||||
chan = Channel(self, dom)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
|
||||
def getDomChannel(self, dom):
|
||||
"""Get the channel for the given domain.
|
||||
|
||||
dom domain
|
||||
|
||||
returns channel (or None)
|
||||
"""
|
||||
dom = int(dom)
|
||||
for chan in self.channels.values():
|
||||
if not isinstance(chan, Channel): continue
|
||||
if chan.dom == dom:
|
||||
return chan
|
||||
chan = Channel(self, dom)
|
||||
self.addChannel(chan)
|
||||
return chan
|
||||
return None
|
||||
|
||||
|
||||
def virqChannel(self, virq):
|
||||
"""Get the channel for the given virq.
|
||||
@@ -109,12 +125,11 @@ class BaseChannel:
|
||||
|
||||
def notificationReceived(self):
|
||||
"""Called when a notification is received.
|
||||
Closes the channel on error, otherwise calls
|
||||
handleNotification(type), which should be defined
|
||||
Calls handleNotification(), which should be defined
|
||||
in a subclass.
|
||||
"""
|
||||
if not self.closed:
|
||||
self.handleNotification(type)
|
||||
if self.closed: return
|
||||
self.handleNotification()
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls channelClosed() on the factory.
|
||||
@@ -122,7 +137,7 @@ class BaseChannel:
|
||||
"""
|
||||
self.factory.channelClosed(self)
|
||||
|
||||
def handleNotification(self, type):
|
||||
def handleNotification(self):
|
||||
"""Handle notification.
|
||||
Define in subclass.
|
||||
"""
|
||||
@@ -159,9 +174,9 @@ class VirqChannel(BaseChannel):
|
||||
"""Close the channel. Calls lostChannel(self) on all its clients and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
for c in self.clients:
|
||||
for c in self.clients[:]:
|
||||
c.lostChannel(self)
|
||||
del self.clients
|
||||
self.clients = []
|
||||
BaseChannel.close(self)
|
||||
|
||||
def registerClient(self, client):
|
||||
@@ -172,7 +187,7 @@ class VirqChannel(BaseChannel):
|
||||
"""
|
||||
self.clients.append(client)
|
||||
|
||||
def handleNotification(self, type):
|
||||
def handleNotification(self):
|
||||
for c in self.clients:
|
||||
c.virqReceived(self.virq)
|
||||
|
||||
@@ -208,23 +223,27 @@ class Channel(BaseChannel):
|
||||
def getLocalPort(self):
|
||||
"""Get the local port.
|
||||
"""
|
||||
if self.closed: return -1
|
||||
return self.port.local_port
|
||||
|
||||
def getRemotePort(self):
|
||||
"""Get the remote port.
|
||||
"""
|
||||
if self.closed: return -1
|
||||
return self.port.remote_port
|
||||
|
||||
def close(self):
|
||||
"""Close the channel. Calls lostChannel() on all its devices and
|
||||
channelClosed() on the factory.
|
||||
"""
|
||||
if self.closed: return
|
||||
self.closed = 1
|
||||
for d in self.devs:
|
||||
for d in self.devs[:]:
|
||||
d.lostChannel()
|
||||
self.factory.channelClosed(self)
|
||||
self.devs = []
|
||||
self.devs_by_type = {}
|
||||
self.port.disconnect()
|
||||
|
||||
def registerDevice(self, types, dev):
|
||||
"""Register a device controller.
|
||||
@@ -265,10 +284,15 @@ class Channel(BaseChannel):
|
||||
def __repr__(self):
|
||||
return ('<Channel dom=%d ports=%d:%d>'
|
||||
% (self.dom,
|
||||
self.port.local_port,
|
||||
self.port.remote_port))
|
||||
self.getLocalPort(),
|
||||
self.getRemotePort()))
|
||||
|
||||
def handleNotification(self, type):
|
||||
def handleNotification(self):
|
||||
"""Process outstanding messages in repsonse to notification on the port.
|
||||
"""
|
||||
if self.closed:
|
||||
print 'handleNotification> Notification on closed channel', self
|
||||
return
|
||||
work = 0
|
||||
work += self.handleRequests()
|
||||
work += self.handleResponses()
|
||||
@@ -277,6 +301,9 @@ class Channel(BaseChannel):
|
||||
self.notify()
|
||||
|
||||
def notify(self):
|
||||
"""Notify the other end of the port that messages have been processed.
|
||||
"""
|
||||
if self.closed: return
|
||||
self.port.notify()
|
||||
|
||||
def handleRequests(self):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet import protocol
|
||||
@@ -12,11 +13,6 @@ 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.
|
||||
"""
|
||||
@@ -36,19 +32,12 @@ class ConsoleProtocol(protocol.Protocol):
|
||||
self.loseConnection()
|
||||
return
|
||||
else:
|
||||
# KAF: A nice quiet successful connect. Don't bother with telnet
|
||||
# control sequence -- telnet is not the appropriate protocol here.
|
||||
# KAF: A nice quiet successful connect.
|
||||
#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()
|
||||
@@ -104,11 +93,15 @@ class ConsoleController(controller.Controller):
|
||||
output and the connected TCP sockets to post console input.
|
||||
"""
|
||||
|
||||
STATUS_NEW = 'new'
|
||||
STATUS_CLOSED = 'closed'
|
||||
STATUS_CONNECTED = 'connected'
|
||||
STATUS_LISTENING = 'listening'
|
||||
|
||||
def __init__(self, factory, dom, console_port):
|
||||
#print 'ConsoleController> dom=', dom, type(dom)
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.majorTypes = [ CMSG_CONSOLE ]
|
||||
self.status = "new"
|
||||
self.status = self.STATUS_NEW
|
||||
self.addr = None
|
||||
self.conn = None
|
||||
self.rbuf = xu.buffer()
|
||||
@@ -118,7 +111,6 @@ class ConsoleController(controller.Controller):
|
||||
self.registerChannel()
|
||||
self.listener = None
|
||||
self.listen()
|
||||
#print 'ConsoleController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val =['console',
|
||||
@@ -136,29 +128,31 @@ class ConsoleController(controller.Controller):
|
||||
return not (self.closed() or self.rbuf.empty())
|
||||
|
||||
def closed(self):
|
||||
return self.status == 'closed'
|
||||
return self.status == self.STATUS_CLOSED
|
||||
|
||||
def connected(self):
|
||||
return self.status == 'connected'
|
||||
return self.status == self.STATUS_CONNECTED
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
#print 'ConsoleController> close dom=', self.dom
|
||||
self.status = "closed"
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.listener.stopListening()
|
||||
self.deregisterChannel()
|
||||
self.lostChannel()
|
||||
except Exception, ex:
|
||||
print 'ConsoleController>close>', ex
|
||||
raise
|
||||
"""Close the console controller.
|
||||
"""
|
||||
self.lostChannel()
|
||||
|
||||
def lostChannel(self):
|
||||
"""The channel to the domain has been lost.
|
||||
Cleanup: disconnect TCP connections and listeners, notify the controller.
|
||||
"""
|
||||
self.status = self.STATUS_CLOSED
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.listener.stopListening()
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def listen(self):
|
||||
"""Listen for TCP connections to the console port..
|
||||
"""
|
||||
if self.closed(): return
|
||||
self.status = "listening"
|
||||
self.status = self.STATUS_LISTENING
|
||||
if self.listener:
|
||||
#self.listener.startListening()
|
||||
pass
|
||||
@@ -167,15 +161,25 @@ class ConsoleController(controller.Controller):
|
||||
self.listener = reactor.listenTCP(self.console_port, f)
|
||||
|
||||
def connect(self, addr, conn):
|
||||
"""Connect a TCP connection to the console.
|
||||
Fails if closed or already connected.
|
||||
|
||||
addr peer address
|
||||
conn connection
|
||||
|
||||
returns 0 if ok, negative otherwise
|
||||
"""
|
||||
if self.closed(): return -1
|
||||
if self.connected(): return -1
|
||||
self.addr = addr
|
||||
self.conn = conn
|
||||
self.status = "connected"
|
||||
self.status = self.STATUS_CONNECTED
|
||||
self.handleOutput()
|
||||
return 0
|
||||
|
||||
def disconnect(self):
|
||||
"""Disconnect the TCP connection to the console.
|
||||
"""
|
||||
if self.conn:
|
||||
self.conn.loseConnection()
|
||||
self.addr = None
|
||||
@@ -183,15 +187,29 @@ class ConsoleController(controller.Controller):
|
||||
self.listen()
|
||||
|
||||
def requestReceived(self, msg, type, subtype):
|
||||
#print '***Console', self.dom, msg.get_payload()
|
||||
"""Receive console data from the console channel.
|
||||
|
||||
msg console message
|
||||
type major message type
|
||||
subtype minor message typ
|
||||
"""
|
||||
self.rbuf.write(msg.get_payload())
|
||||
self.handleOutput()
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
"""Handle a response to a request written to the console channel.
|
||||
Just ignore it because the return values are not interesting.
|
||||
|
||||
msg console message
|
||||
type major message type
|
||||
subtype minor message typ
|
||||
"""
|
||||
pass
|
||||
|
||||
def produceRequests(self):
|
||||
# Send as much pending console data as there is room for.
|
||||
"""Write pending console data to the console channel.
|
||||
Writes as much to the channel as it can.
|
||||
"""
|
||||
work = 0
|
||||
while not self.wbuf.empty() and self.channel.writeReady():
|
||||
msg = xu.message(CMSG_CONSOLE, 0, 0)
|
||||
@@ -201,7 +219,12 @@ class ConsoleController(controller.Controller):
|
||||
|
||||
def handleInput(self, conn, data):
|
||||
"""Handle some external input aimed at the console.
|
||||
Called from a TCP connection (conn).
|
||||
Called from a TCP connection (conn). Ignores the input
|
||||
if the calling connection (conn) is not the one connected
|
||||
to the console (self.conn).
|
||||
|
||||
conn connection
|
||||
data input data
|
||||
"""
|
||||
if self.closed(): return -1
|
||||
if conn != self.conn: return 0
|
||||
@@ -215,18 +238,14 @@ class ConsoleController(controller.Controller):
|
||||
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
|
||||
@@ -1,10 +1,50 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
|
||||
import channel
|
||||
from messages import msgTypeName
|
||||
|
||||
DEBUG=0
|
||||
|
||||
class OutOfOrderError(RuntimeError):
|
||||
"""Error reported when a response arrives out of order.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Responder:
|
||||
"""Handler for a response to a message.
|
||||
"""
|
||||
|
||||
def __init__(self, mid, deferred):
|
||||
"""Create a responder.
|
||||
|
||||
mid message id of response to handle
|
||||
deferred deferred object holding the callbacks
|
||||
"""
|
||||
self.mid = mid
|
||||
self.deferred = deferred
|
||||
|
||||
def responseReceived(self, msg):
|
||||
if self.deferred.called: return
|
||||
self.deferred.callback(msg)
|
||||
|
||||
def error(self, err):
|
||||
if self.deferred.called: return
|
||||
self.deferred.errback(err)
|
||||
|
||||
class CtrlMsgRcvr:
|
||||
"""Abstract class for things that deal with a control interface to a domain.
|
||||
|
||||
Instance variables:
|
||||
|
||||
dom : the domain we are a control interface for
|
||||
majorTypes: list of major message types we are interested in
|
||||
subTypes : mapping of message subtypes to methods
|
||||
|
||||
channel : channel to the domain
|
||||
idx : channel index
|
||||
"""
|
||||
|
||||
|
||||
@@ -15,50 +55,140 @@ class CtrlMsgRcvr:
|
||||
self.dom = None
|
||||
self.channel = None
|
||||
self.idx = None
|
||||
self.responders = []
|
||||
# Timeout (in seconds) for deferreds.
|
||||
self.timeout = 10
|
||||
|
||||
def setTimeout(self, timeout):
|
||||
self.timeout = timeout
|
||||
|
||||
def requestReceived(self, msg, type, subtype):
|
||||
"""Dispatch a request to handlers.
|
||||
|
||||
msg message
|
||||
type major message type
|
||||
subtype minor message type
|
||||
"""
|
||||
msgid = msg.get_header()['id']
|
||||
if DEBUG:
|
||||
print 'requestReceived>', self, msgid, msgTypeName(type, subtype)
|
||||
method = self.subTypes.get(subtype)
|
||||
if method:
|
||||
method(msg, 1)
|
||||
else:
|
||||
elif DEBUG:
|
||||
print ('requestReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
|
||||
def responseReceived(self, msg, type, subtype):
|
||||
"""Dispatch a response to handlers.
|
||||
|
||||
msg message
|
||||
type major message type
|
||||
subtype minor message type
|
||||
"""
|
||||
msgid = msg.get_header()['id']
|
||||
if DEBUG:
|
||||
print 'responseReceived>', self, msgid, msgTypeName(type, subtype)
|
||||
if self.callResponders(msg):
|
||||
return
|
||||
method = self.subTypes.get(subtype)
|
||||
if method:
|
||||
method(msg, 0)
|
||||
else:
|
||||
elif DEBUG:
|
||||
print ('responseReceived> No handler: Message type %s %d:%d'
|
||||
% (msgTypeName(type, subtype), type, subtype)), self
|
||||
|
||||
def addResponder(self, mid, deferred):
|
||||
"""Add a responder for a message id.
|
||||
The deferred is called with callback(msg) when a response
|
||||
with the given message id arrives. Responses are expected
|
||||
to arrive in order of message id. When a response arrives,
|
||||
waiting responders for messages with lower id have errback
|
||||
called with an OutOfOrder error.
|
||||
|
||||
mid message id of response expected
|
||||
deferred a Deferred to handle the response
|
||||
|
||||
returns Responder
|
||||
"""
|
||||
if self.timeout > 0:
|
||||
deferred.setTimeout(self.timeout)
|
||||
resp = Responder(mid, deferred)
|
||||
self.responders.append(resp)
|
||||
return resp
|
||||
|
||||
def callResponders(self, msg):
|
||||
"""Call any waiting responders for a response message.
|
||||
|
||||
msg response message
|
||||
|
||||
returns 1 if there was a responder for the message, 0 otherwise
|
||||
"""
|
||||
hdr = msg.get_header()
|
||||
mid = hdr['id']
|
||||
handled = 0
|
||||
while self.responders:
|
||||
resp = self.responders[0]
|
||||
if resp.mid > mid:
|
||||
break
|
||||
self.responders.pop()
|
||||
if resp.mid < mid:
|
||||
print 'handleResponse> Out of order:', resp.mid, mid
|
||||
resp.error(OutOfOrderError())
|
||||
else:
|
||||
handled = 1
|
||||
resp.responseReceived(msg)
|
||||
break
|
||||
return handled
|
||||
|
||||
def lostChannel(self):
|
||||
"""Called when the channel to the domain is lost.
|
||||
"""
|
||||
pass
|
||||
|
||||
def registerChannel(self):
|
||||
#print 'CtrlMsgRcvr>registerChannel>', self
|
||||
"""Register interest in our major message types with the
|
||||
channel to our domain.
|
||||
"""
|
||||
self.channel = self.channelFactory.domChannel(self.dom)
|
||||
self.idx = self.channel.getIndex()
|
||||
if self.majorTypes:
|
||||
self.channel.registerDevice(self.majorTypes, self)
|
||||
|
||||
def deregisterChannel(self):
|
||||
#print 'CtrlMsgRcvr>deregisterChannel>', self
|
||||
"""Deregister interest in our major message types with the
|
||||
channel to our domain.
|
||||
"""
|
||||
if self.channel:
|
||||
self.channel.deregisterDevice(self)
|
||||
del self.channel
|
||||
|
||||
def produceRequests(self):
|
||||
"""Produce any queued requests.
|
||||
|
||||
return number produced
|
||||
"""
|
||||
return 0
|
||||
|
||||
def writeRequest(self, msg):
|
||||
def writeRequest(self, msg, response=None):
|
||||
"""Write a request to the channel.
|
||||
|
||||
msg message
|
||||
response Deferred to handle the response (optional)
|
||||
"""
|
||||
if self.channel:
|
||||
if DEBUG: print 'CtrlMsgRcvr>writeRequest>', self, msg
|
||||
if response:
|
||||
self.addResponder(msg.get_header()['id'], response)
|
||||
self.channel.writeRequest(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeRequest>', 'no channel!', self
|
||||
|
||||
def writeResponse(self, msg):
|
||||
"""Write a response to the channel.
|
||||
"""
|
||||
if self.channel:
|
||||
if DEBUG: print 'CtrlMsgRcvr>writeResponse>', self, msg
|
||||
self.channel.writeResponse(msg)
|
||||
else:
|
||||
print 'CtrlMsgRcvr>writeResponse>', 'no channel!', self
|
||||
@@ -66,6 +196,12 @@ class CtrlMsgRcvr:
|
||||
class ControllerFactory(CtrlMsgRcvr):
|
||||
"""Abstract class for factories creating controllers.
|
||||
Maintains a table of instances.
|
||||
|
||||
Instance variables:
|
||||
|
||||
instances : mapping of index to controller instance
|
||||
dlist : list of deferreds
|
||||
dom : domain
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
@@ -73,55 +209,46 @@ class ControllerFactory(CtrlMsgRcvr):
|
||||
self.instances = {}
|
||||
self.dlist = []
|
||||
self.dom = 0
|
||||
# Timeout (in seconds) for deferreds.
|
||||
self.timeout = 10
|
||||
|
||||
def addInstance(self, instance):
|
||||
"""Add a controller instance (under its index).
|
||||
"""
|
||||
self.instances[instance.idx] = instance
|
||||
|
||||
def getInstance(self, idx):
|
||||
"""Get a controller instance from its index.
|
||||
"""
|
||||
return self.instances.get(idx)
|
||||
|
||||
def getInstances(self):
|
||||
"""Get a list of all controller instances.
|
||||
"""
|
||||
return self.instances.values()
|
||||
|
||||
def getInstanceByDom(self, dom):
|
||||
"""Get the controller instance for the given domain.
|
||||
"""
|
||||
for inst in self.instances.values():
|
||||
if inst.dom == dom:
|
||||
return inst
|
||||
return None
|
||||
|
||||
def delInstance(self, instance):
|
||||
#print 'ControllerFactory>delInstance>', instance.idx
|
||||
"""Delete an instance from the table.
|
||||
"""
|
||||
if instance.idx in self.instances:
|
||||
#print 'ControllerFactory>delInstance> remove', instance.idx
|
||||
del self.instances[instance.idx]
|
||||
|
||||
def createInstance(self, dom, recreate=0):
|
||||
"""Create an instance. Define in a subclass.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def instanceClosed(self, instance):
|
||||
#print 'ControllerFactory>instanceClosed>', instance.idx, instance
|
||||
"""Callback called when an instance is closed (usually by the instance).
|
||||
"""
|
||||
self.delInstance(instance)
|
||||
|
||||
def addDeferred(self):
|
||||
d = defer.Deferred()
|
||||
if self.timeout > 0:
|
||||
# The deferred will error if not called before timeout.
|
||||
d.setTimeout(self.timeout)
|
||||
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.
|
||||
"""
|
||||
@@ -134,15 +261,20 @@ class Controller(CtrlMsgRcvr):
|
||||
self.idx = None
|
||||
|
||||
def close(self):
|
||||
self.deregisterChannel()
|
||||
"""Close the controller.
|
||||
"""
|
||||
self.lostChannel()
|
||||
|
||||
def lostChannel(self):
|
||||
#print 'Controller>lostChannel>', self, self.factory
|
||||
"""The controller channel has been lost.
|
||||
"""
|
||||
self.deregisterChannel()
|
||||
self.factory.instanceClosed(self)
|
||||
|
||||
class Dev:
|
||||
|
||||
"""Abstract class for a device attached to a device controller.
|
||||
"""
|
||||
|
||||
def __init__(self, controller):
|
||||
self.controller = controller
|
||||
self.props = {}
|
||||
@@ -160,9 +292,6 @@ class Dev:
|
||||
if k in self.props:
|
||||
del self.props[k]
|
||||
|
||||
#def __repr__(self):
|
||||
# return str(self.sxpr())
|
||||
|
||||
def sxpr(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
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()
|
||||
@@ -1,3 +1,5 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import channel
|
||||
import controller
|
||||
from messages import *
|
||||
@@ -7,11 +9,23 @@ class DomainControllerFactory(controller.ControllerFactory):
|
||||
"""
|
||||
|
||||
def createInstance(self, dom):
|
||||
"""Create a domain controller.
|
||||
|
||||
dom domain
|
||||
|
||||
returns domain controller
|
||||
"""
|
||||
d = DomainController(self, dom)
|
||||
self.addInstance(d)
|
||||
return d
|
||||
|
||||
def getInstanceByDom(self, dom):
|
||||
"""Get a domain controller for a domain, creating if necessary.
|
||||
|
||||
dom domain
|
||||
|
||||
returns domain controller
|
||||
"""
|
||||
for inst in self.instances.values():
|
||||
if inst.dom == dom:
|
||||
return inst
|
||||
@@ -21,8 +35,11 @@ class DomainControllerFactory(controller.ControllerFactory):
|
||||
|
||||
class DomainController(controller.Controller):
|
||||
"""Generic controller for a domain.
|
||||
Used for domain shutdown.
|
||||
"""
|
||||
|
||||
"""Map shutdown reasons to the message type to use.
|
||||
"""
|
||||
reasons = {'poweroff' : 'shutdown_poweroff_t',
|
||||
'reboot' : 'shutdown_reboot_t',
|
||||
'suspend' : 'shutdown_suspend_t' }
|
||||
@@ -34,6 +51,10 @@ class DomainController(controller.Controller):
|
||||
print 'DomainController>', self, self.channel, self.idx
|
||||
|
||||
def shutdown(self, reason):
|
||||
"""Shutdown a domain.
|
||||
|
||||
reason shutdown reason
|
||||
"""
|
||||
msgtype = self.reasons.get(reason)
|
||||
if not msgtype:
|
||||
raise ValueError('invalid reason:' + reason)
|
||||
|
||||
@@ -148,6 +148,9 @@ netif_formats = {
|
||||
msg_formats.update(netif_formats)
|
||||
|
||||
#============================================================================
|
||||
# Domain shutdown message types.
|
||||
#============================================================================
|
||||
|
||||
CMSG_SHUTDOWN = 6
|
||||
|
||||
CMSG_SHUTDOWN_POWEROFF = 0
|
||||
@@ -176,8 +179,23 @@ msg_formats.update(shutdown_formats)
|
||||
class Msg:
|
||||
pass
|
||||
|
||||
_next_msgid = 0
|
||||
|
||||
def nextid():
|
||||
global _next_msgid
|
||||
return ++_next_msgid
|
||||
|
||||
def packMsg(ty, params):
|
||||
if DEBUG: print '>packMsg', ty, params
|
||||
"""Pack a message.
|
||||
Any 'mac' parameter is passed in as an int[6] array and converted.
|
||||
|
||||
ty message type name
|
||||
params message parameter dict
|
||||
|
||||
returns xu message
|
||||
"""
|
||||
msgid = nextid()
|
||||
if DEBUG: print '>packMsg', msgid, ty, params
|
||||
(major, minor) = msg_formats[ty]
|
||||
args = {}
|
||||
for (k, v) in params.items():
|
||||
@@ -189,11 +207,19 @@ def packMsg(ty, params):
|
||||
if DEBUG:
|
||||
for (k, v) in args.items():
|
||||
print 'packMsg>', k, v, type(v)
|
||||
msgid = 0
|
||||
msg = xu.message(major, minor, msgid, args)
|
||||
return msg
|
||||
|
||||
def unpackMsg(ty, msg):
|
||||
"""Unpack a message.
|
||||
Any mac addresses in the message are converted to int[6] array
|
||||
in the return dict.
|
||||
|
||||
ty message type
|
||||
msg xu message
|
||||
|
||||
returns parameter dict
|
||||
"""
|
||||
args = msg.get_payload()
|
||||
mac = [0, 0, 0, 0, 0, 0]
|
||||
macs = []
|
||||
@@ -208,10 +234,19 @@ def unpackMsg(ty, msg):
|
||||
args['mac'] = mac
|
||||
for k in macs:
|
||||
del args[k]
|
||||
if DEBUG: print '<unpackMsg', ty, args
|
||||
if DEBUG:
|
||||
msgid = msg.get_header()['id']
|
||||
print '<unpackMsg', msgid, ty, args
|
||||
return args
|
||||
|
||||
def msgTypeName(ty, subty):
|
||||
"""Convert a message type, subtype pair (ints) to a message type name (string).
|
||||
|
||||
ty integer message type
|
||||
subty integer message subtype
|
||||
|
||||
returns message type name (or None)
|
||||
"""
|
||||
for (name, info) in msg_formats.items():
|
||||
if info[0] == ty and info[1] == subty:
|
||||
return name
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
import random
|
||||
|
||||
from twisted.internet import defer
|
||||
defer.Deferred.debug = 1
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
@@ -21,8 +24,8 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
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_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
|
||||
@@ -30,8 +33,12 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
|
||||
def createInstance(self, dom, recreate=0):
|
||||
"""Create or find the network interface controller for a domain.
|
||||
|
||||
dom domain
|
||||
recreate if true this is a recreate (xend restarted)
|
||||
|
||||
returns netif controller
|
||||
"""
|
||||
#print 'netif>createInstance> dom=', dom
|
||||
netif = self.getInstanceByDom(dom)
|
||||
if netif is None:
|
||||
netif = NetifController(self, dom)
|
||||
@@ -39,15 +46,31 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
return netif
|
||||
|
||||
def getDomainDevices(self, dom):
|
||||
"""Get the network device controllers for a domain.
|
||||
|
||||
dom domain
|
||||
|
||||
returns netif controller
|
||||
"""
|
||||
netif = self.getInstanceByDom(dom)
|
||||
return (netif and netif.getDevices()) or []
|
||||
|
||||
def getDomainDevice(self, dom, vif):
|
||||
"""Get a virtual network interface device for a domain.
|
||||
|
||||
dom domain
|
||||
vif virtual interface index
|
||||
|
||||
returns NetDev
|
||||
"""
|
||||
netif = self.getInstanceByDom(dom)
|
||||
return (netif and netif.getDevice(vif)) or None
|
||||
|
||||
def setControlDomain(self, dom, recreate=0):
|
||||
"""Set the 'back-end' device driver domain.
|
||||
|
||||
dom domain
|
||||
recreate if true this is a recreate (xend restarted)
|
||||
"""
|
||||
if self.dom == dom: return
|
||||
self.deregisterChannel()
|
||||
@@ -55,19 +78,13 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
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)
|
||||
#
|
||||
|
||||
def getControlDomain(self):
|
||||
"""Get the domain id of the back-end control domain.
|
||||
"""
|
||||
return self.dom
|
||||
|
||||
def recv_be_create(self, msg, req):
|
||||
self.callDeferred(0)
|
||||
|
||||
def recv_be_connect(self, msg, req):
|
||||
def respond_be_connect(self, msg):
|
||||
val = unpackMsg('netif_be_connect_t', msg)
|
||||
dom = val['domid']
|
||||
vif = val['netif_handle']
|
||||
@@ -75,7 +92,7 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
if netif:
|
||||
netif.send_interface_connected(vif)
|
||||
else:
|
||||
print "recv_be_connect> unknown vif=", vif
|
||||
print "respond_be_connect> unknown vif=", vif
|
||||
pass
|
||||
|
||||
def recv_be_driver_status_changed(self, msg, req):
|
||||
@@ -88,23 +105,6 @@ class NetifControllerFactory(controller.ControllerFactory):
|
||||
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 = xu.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(controller.Dev):
|
||||
"""Info record for a network device.
|
||||
"""
|
||||
@@ -130,9 +130,13 @@ class NetDev(controller.Dev):
|
||||
return val
|
||||
|
||||
def get_vifname(self):
|
||||
"""Get the virtual interface device name.
|
||||
"""
|
||||
return "vif%d.%d" % (self.controller.dom, self.vif)
|
||||
|
||||
def get_mac(self):
|
||||
"""Get the MAC address as a string.
|
||||
"""
|
||||
return ':'.join(map(lambda x: "%x" % x, self.mac))
|
||||
|
||||
def vifctl_params(self):
|
||||
@@ -141,23 +145,31 @@ class NetDev(controller.Dev):
|
||||
'ipaddr': self.ipaddr }
|
||||
|
||||
def up(self, bridge=None, ipaddr=[]):
|
||||
"""Bring the device up.
|
||||
|
||||
bridge ethernet bridge to connect to
|
||||
ipaddr list of ipaddrs to filter using iptables
|
||||
"""
|
||||
self.bridge = bridge
|
||||
self.ipaddr = ipaddr
|
||||
Vifctl.up(self.get_vifname(), **self.vifctl_params())
|
||||
|
||||
def down(self):
|
||||
"""Bring the device down.
|
||||
"""
|
||||
Vifctl.down(self.get_vifname(), **self.vifctl_params())
|
||||
|
||||
def destroy(self):
|
||||
"""Destroy the device's resources and disconnect from the back-end
|
||||
device controller.
|
||||
"""
|
||||
def cb_destroy(val):
|
||||
self.controller.send_be_destroy(self.vif)
|
||||
print 'NetDev>destroy>', 'vif=', self.vif
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
self.down()
|
||||
d = self.controller.factory.addDeferred()
|
||||
#d = self.controller.factory.addDeferred()
|
||||
d = defer.Deferred()
|
||||
d.addCallback(cb_destroy)
|
||||
self.controller.send_be_disconnect(self.vif)
|
||||
#self.controller.send_be_destroy(self.vif)
|
||||
self.controller.send_be_disconnect(self.vif, response=d)
|
||||
|
||||
|
||||
class NetifController(controller.Controller):
|
||||
@@ -165,7 +177,6 @@ class NetifController(controller.Controller):
|
||||
"""
|
||||
|
||||
def __init__(self, factory, dom):
|
||||
#print 'NetifController> dom=', dom
|
||||
controller.Controller.__init__(self, factory, dom)
|
||||
self.devices = {}
|
||||
|
||||
@@ -178,21 +189,23 @@ class NetifController(controller.Controller):
|
||||
self.recv_fe_interface_connect,
|
||||
}
|
||||
self.registerChannel()
|
||||
#print 'NetifController<', 'dom=', self.dom, 'idx=', self.idx
|
||||
|
||||
def sxpr(self):
|
||||
val = ['netif', ['dom', self.dom]]
|
||||
return val
|
||||
|
||||
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.
|
||||
"""Generate a random MAC address.
|
||||
|
||||
Uses OUI (Organizationally Unique Identifier) AA:00:00, an
|
||||
unassigned one that used to belong to DEC. The OUI list is
|
||||
available at 'standards.ieee.org'.
|
||||
|
||||
The remaining 3 fields are random, with the first bit of the first
|
||||
random field set 0.
|
||||
|
||||
returns array of 6 ints
|
||||
"""
|
||||
mac = [ 0xaa, 0x00, 0x00,
|
||||
random.randint(0x00, 0x7f),
|
||||
random.randint(0x00, 0xff),
|
||||
@@ -200,29 +213,46 @@ class NetifController(controller.Controller):
|
||||
return mac
|
||||
|
||||
def lostChannel(self):
|
||||
print 'NetifController>lostChannel>', 'dom=', self.dom
|
||||
#self.destroyDevices()
|
||||
"""Method called when the channel has been lost.
|
||||
"""
|
||||
controller.Controller.lostChannel(self)
|
||||
|
||||
def getDevices(self):
|
||||
"""Get a list of the devices.
|
||||
"""
|
||||
return self.devices.values()
|
||||
|
||||
def getDevice(self, vif):
|
||||
"""Get a device.
|
||||
|
||||
vif device index
|
||||
|
||||
returns device (or None)
|
||||
"""
|
||||
return self.devices.get(vif)
|
||||
|
||||
def addDevice(self, vif, vmac):
|
||||
"""Add a network interface. If vmac is None a random MAC is
|
||||
assigned. If specified, vmac must be a string of the form
|
||||
XX:XX:XX:XX:XX where X is hex digit.
|
||||
|
||||
vif device index
|
||||
vmac device MAC
|
||||
|
||||
returns device
|
||||
"""
|
||||
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
|
||||
dev = NetDev(self, vif, mac)
|
||||
self.devices[vif] = dev
|
||||
return dev
|
||||
|
||||
def destroy(self):
|
||||
print 'NetifController>destroy>', 'dom=', self.dom
|
||||
"""Destroy the controller and all devices.
|
||||
"""
|
||||
self.destroyDevices()
|
||||
|
||||
def destroyDevices(self):
|
||||
@@ -237,20 +267,19 @@ class NetifController(controller.Controller):
|
||||
@param vmac mac address (string)
|
||||
"""
|
||||
self.addDevice(vif, vmac)
|
||||
d = defer.Deferred()
|
||||
if recreate:
|
||||
d = defer.Deferred()
|
||||
d.callback(self)
|
||||
else:
|
||||
d = self.factory.addDeferred()
|
||||
self.send_be_create(vif)
|
||||
self.send_be_create(vif, response=d)
|
||||
return d
|
||||
|
||||
def reattach_devices(self):
|
||||
"""Reattach all devices when the back-end control domain has changed.
|
||||
"""
|
||||
d = self.factory.addDeferred()
|
||||
#d = self.factory.addDeferred()
|
||||
self.send_be_create(vif)
|
||||
self.attach_fe_devices(0)
|
||||
self.attach_fe_devices()
|
||||
|
||||
def attach_fe_devices(self):
|
||||
for dev in self.devices.values():
|
||||
@@ -279,38 +308,39 @@ class NetifController(controller.Controller):
|
||||
'evtchn' : dev.evtchn['port1'],
|
||||
'tx_shmem_frame' : val['tx_shmem_frame'],
|
||||
'rx_shmem_frame' : val['rx_shmem_frame'] })
|
||||
self.factory.writeRequest(msg)
|
||||
d = defer.Deferred()
|
||||
d.addCallback(self.factory.respond_be_connect)
|
||||
self.factory.writeRequest(msg, response=d)
|
||||
|
||||
def send_interface_connected(self, vif):
|
||||
def send_interface_connected(self, vif, response=None):
|
||||
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)
|
||||
self.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_create(self, vif):
|
||||
def send_be_create(self, vif, response=None):
|
||||
dev = self.devices[vif]
|
||||
msg = packMsg('netif_be_create_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : dev.vif,
|
||||
'mac' : dev.mac })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_disconnect(self, vif):
|
||||
def send_be_disconnect(self, vif, response=None):
|
||||
dev = self.devices[vif]
|
||||
msg = packMsg('netif_be_disconnect_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : dev.vif })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
|
||||
def send_be_destroy(self, vif):
|
||||
print 'NetifController>send_be_destroy>', 'dom=', self.dom, 'vif=', vif
|
||||
def send_be_destroy(self, vif, response=None):
|
||||
PrettyPrint.prettyprint(self.sxpr())
|
||||
dev = self.devices[vif]
|
||||
del self.devices[vif]
|
||||
msg = packMsg('netif_be_destroy_t',
|
||||
{ 'domid' : self.dom,
|
||||
'netif_handle' : vif })
|
||||
self.factory.writeRequest(msg)
|
||||
self.factory.writeRequest(msg, response=response)
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Domain creation.
|
||||
"""
|
||||
import string
|
||||
@@ -12,15 +13,25 @@ from xen.util import console_client
|
||||
|
||||
from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options]
|
||||
gopts = Opts(use="""[options] [vars]
|
||||
|
||||
Create a domain.
|
||||
|
||||
Domain creation parameters can be set by command-line switches, from
|
||||
a python configuration script or an SXP config file. See documentation
|
||||
for --defaults, --config. Configuration variables can be set using
|
||||
VAR=VAL on the command line. For example vmid=3 sets vmid to 3.
|
||||
|
||||
""")
|
||||
|
||||
gopts.opt('help', short='h',
|
||||
fn=set_true, default=0,
|
||||
use="Print this help.")
|
||||
|
||||
gopts.opt('help_config',
|
||||
fn=set_true, default=0,
|
||||
use="Print help for configuration file.")
|
||||
|
||||
gopts.opt('quiet', short='q',
|
||||
fn=set_true, default=0,
|
||||
use="Quiet.")
|
||||
@@ -31,58 +42,73 @@ gopts.opt('path', val='PATH',
|
||||
|
||||
gopts.opt('defaults', short='f', val='FILE',
|
||||
fn=set_value, default='xmdefaults',
|
||||
use="Use the given default script.")
|
||||
use="""Use the given Python defaults script.
|
||||
The defaults script is loaded after arguments have been processed.
|
||||
Each command-line option sets a configuration variable named after
|
||||
its long option name, and these variables are placed in the
|
||||
environment of the script before it is loaded.
|
||||
Variables for options that may be repeated have list values.
|
||||
Other variables can be set using VAR=VAL on the command line.
|
||||
|
||||
After the script is loaded, option values that were not set on the
|
||||
command line are replaced by the values set in the script.
|
||||
""")
|
||||
|
||||
gopts.opt('config', short='F', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use='Domain configuration to use (SXP).')
|
||||
use="""Domain configuration to use (SXP).
|
||||
SXP is the underlying configuration format used by Xen.
|
||||
SXP configs can be hand-written or generated from Python defaults
|
||||
scripts, using the -n (dryrun) option to print the config.
|
||||
""")
|
||||
|
||||
gopts.opt('load', short='L', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use='Domain saved state to load.')
|
||||
|
||||
gopts.opt('define', short='D', val='VAR=VAL',
|
||||
fn=set_var, default=None,
|
||||
use="""Set a variable before loading defaults, e.g. '-D vmid=3'
|
||||
to set vmid. May be repeated to set more thanone variable.""")
|
||||
|
||||
gopts.opt('dryrun', short='n',
|
||||
fn=set_true, default=0,
|
||||
use="Dry run - print the config but don't create the domain.")
|
||||
|
||||
gopts.opt('name', short='N', val='NAME',
|
||||
fn=set_value, default=None,
|
||||
use="Domain name.")
|
||||
use="""Dry run - print the config but don't create the domain.
|
||||
The defaults file is loaded and the SXP configuration is created and printed.
|
||||
""")
|
||||
|
||||
gopts.opt('console_autoconnect', short='c',
|
||||
fn=set_true, default=0,
|
||||
use="Connect to console after domain is created.")
|
||||
|
||||
gopts.opt('kernel', short='k', val='FILE',
|
||||
gopts.var('name', val='NAME',
|
||||
fn=set_value, default=None,
|
||||
use="Domain name.")
|
||||
|
||||
gopts.var('kernel', val='FILE',
|
||||
fn=set_value, default=None,
|
||||
use="Path to kernel image.")
|
||||
|
||||
gopts.opt('ramdisk', short='r', val='FILE',
|
||||
gopts.var('ramdisk', val='FILE',
|
||||
fn=set_value, default='',
|
||||
use="Path to ramdisk.")
|
||||
|
||||
gopts.opt('builder', short='b', val='FUNCTION',
|
||||
gopts.var('builder', val='FUNCTION',
|
||||
fn=set_value, default='linux',
|
||||
use="Function to use to build the domain.")
|
||||
|
||||
gopts.opt('memory', short='m', val='MEMORY',
|
||||
gopts.var('memory', val='MEMORY',
|
||||
fn=set_value, default=128,
|
||||
use="Domain memory in MB.")
|
||||
|
||||
gopts.opt('blkif',
|
||||
fn=set_true, default=0,
|
||||
gopts.var('autorestart', val='no|yes',
|
||||
fn=set_bool, default=0,
|
||||
use="Whether to restart the domain on exit.")
|
||||
|
||||
gopts.var('blkif', val='no|yes',
|
||||
fn=set_bool, default=0,
|
||||
use="Make the domain a block device backend.")
|
||||
|
||||
gopts.opt('netif',
|
||||
fn=set_true, default=0,
|
||||
gopts.var('netif', val='no|yes',
|
||||
fn=set_bool, default=0,
|
||||
use="Make the domain a network interface backend.")
|
||||
|
||||
gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE',
|
||||
gopts.var('disk', val='phy:DEV,VDEV,MODE',
|
||||
fn=append_value, default=[],
|
||||
use="""Add a disk device to a domain. The physical device is DEV,
|
||||
which is exported to the domain as VDEV. The disk is read-only if MODE
|
||||
@@ -90,18 +116,18 @@ gopts.opt('disk', short='d', val='phy:DEV,VDEV,MODE',
|
||||
The option may be repeated to add more than one disk.
|
||||
""")
|
||||
|
||||
gopts.opt('pci', val='BUS,DEV,FUNC',
|
||||
gopts.var('pci', val='BUS,DEV,FUNC',
|
||||
fn=append_value, default=[],
|
||||
use="""Add a PCI device to a domain, using given params (in hex).
|
||||
For example '-pci c0,02,1a'.
|
||||
The option may be repeated to add more than one pci device.
|
||||
""")
|
||||
|
||||
gopts.opt('ipaddr', short='i', val="IPADDR",
|
||||
gopts.var('ipaddr', val="IPADDR",
|
||||
fn=append_value, default=[],
|
||||
use="Add an IP address to the domain.")
|
||||
|
||||
gopts.opt('vif', val="mac=MAC,bridge=BRIDGE",
|
||||
gopts.var('vif', val="mac=MAC,bridge=BRIDGE",
|
||||
fn=append_value, default=[],
|
||||
use="""Add a network interface with the given MAC address and bridge.
|
||||
If mac is not specified a random MAC address is used.
|
||||
@@ -110,7 +136,7 @@ gopts.opt('vif', val="mac=MAC,bridge=BRIDGE",
|
||||
Specifying vifs will increase the number of interfaces as needed.
|
||||
""")
|
||||
|
||||
gopts.opt('nics', val="NUM",
|
||||
gopts.var('nics', val="NUM",
|
||||
fn=set_int, default=1,
|
||||
use="""Set the number of network interfaces.
|
||||
Use the vif option to define interface parameters, otherwise
|
||||
@@ -118,44 +144,44 @@ gopts.opt('nics', val="NUM",
|
||||
number of interfaces as needed.
|
||||
""")
|
||||
|
||||
gopts.opt('root', short='R', val='DEVICE',
|
||||
gopts.var('root', val='DEVICE',
|
||||
fn=set_value, default='',
|
||||
use="""Set the root= parameter on the kernel command line.
|
||||
Use a device, e.g. /dev/sda1, or /dev/nfs for NFS root.""")
|
||||
|
||||
gopts.opt('extra', short='E', val="ARGS",
|
||||
gopts.var('extra', val="ARGS",
|
||||
fn=set_value, default='',
|
||||
use="Set extra arguments to append to the kernel command line.")
|
||||
|
||||
gopts.opt('ip', short='I', val='IPADDR',
|
||||
gopts.var('ip', val='IPADDR',
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP interface address.")
|
||||
|
||||
gopts.opt('gateway', val="IPADDR",
|
||||
gopts.var('gateway', val="IPADDR",
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP gateway.")
|
||||
|
||||
gopts.opt('netmask', val="MASK",
|
||||
gopts.var('netmask', val="MASK",
|
||||
fn=set_value, default = '',
|
||||
use="Set the kernel IP netmask.")
|
||||
|
||||
gopts.opt('hostname', val="NAME",
|
||||
gopts.var('hostname', val="NAME",
|
||||
fn=set_value, default='',
|
||||
use="Set the kernel IP hostname.")
|
||||
|
||||
gopts.opt('interface', val="INTF",
|
||||
gopts.var('interface', val="INTF",
|
||||
fn=set_value, default="eth0",
|
||||
use="Set the kernel IP interface name.")
|
||||
|
||||
gopts.opt('dhcp', val="off|dhcp",
|
||||
gopts.var('dhcp', val="off|dhcp",
|
||||
fn=set_value, default='off',
|
||||
use="Set the kernel dhcp option.")
|
||||
|
||||
gopts.opt('nfs_server', val="IPADDR",
|
||||
gopts.var('nfs_server', val="IPADDR",
|
||||
fn=set_value, default=None,
|
||||
use="Set the address of the NFS server for NFS root.")
|
||||
|
||||
gopts.opt('nfs_root', val="PATH",
|
||||
gopts.var('nfs_root', val="PATH",
|
||||
fn=set_value, default=None,
|
||||
use="Set the path of the root NFS directory.")
|
||||
|
||||
@@ -244,6 +270,8 @@ def make_config(opts):
|
||||
config.append(['backend', ['blkif']])
|
||||
if opts.netif:
|
||||
config.append(['backend', ['netif']])
|
||||
if opts.autorestart:
|
||||
config.append(['autorestart'])
|
||||
|
||||
configure_image(config, opts)
|
||||
config_devs = []
|
||||
@@ -282,7 +310,7 @@ def preprocess_vifs(opts):
|
||||
d = {}
|
||||
a = vif.split(',')
|
||||
for b in a:
|
||||
(k, v) = b.strip().split('=')
|
||||
(k, v) = b.strip().split('=', 1)
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
if k not in ['mac', 'bridge']:
|
||||
@@ -352,7 +380,15 @@ def main(argv):
|
||||
args = opts.parse(argv)
|
||||
if opts.vals.help:
|
||||
opts.usage()
|
||||
if opts.vals.help or opts.vals.help_config:
|
||||
opts.load_defaults(help=1)
|
||||
if opts.vals.help or opts.vals.help_config:
|
||||
return
|
||||
# Process remaining args as config variables.
|
||||
for arg in args:
|
||||
if '=' in arg:
|
||||
(var, val) = arg.strip().split('=', 1)
|
||||
gopts.setvar(var.strip(), val.strip())
|
||||
if opts.vals.config:
|
||||
pass
|
||||
else:
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Variable definition and help support for Python defaults files.
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
class Vars:
|
||||
"""A set of configuration variables.
|
||||
"""
|
||||
|
||||
def __init__(self, name, help, env):
|
||||
"""Create a variable set.
|
||||
|
||||
name name of the defaults file
|
||||
help help flag
|
||||
env local environment
|
||||
"""
|
||||
self.name = name
|
||||
self.help = help
|
||||
self.env = env
|
||||
self.vars = []
|
||||
|
||||
def var(self, name, use=None, check=None):
|
||||
"""Define a configuration variable.
|
||||
If provided, the check function will be called as check(var, val)
|
||||
where var is the variable name and val is its value (string).
|
||||
It should return a new value for the variable, or raise ValueError if
|
||||
the value is not acceptable.
|
||||
|
||||
name variable name
|
||||
use variable usage string
|
||||
check variable check function
|
||||
"""
|
||||
self.vars.append(Var(name, use, check))
|
||||
|
||||
def check(self):
|
||||
"""Execute the variable checks or print help, depending on the value
|
||||
of the help flag passed to the constructor.
|
||||
"""
|
||||
if self.help:
|
||||
self.doHelp()
|
||||
else:
|
||||
for v in self.vars:
|
||||
v.doCheck(self.env)
|
||||
|
||||
def doHelp(self, out=sys.stderr):
|
||||
"""Print help for the variables.
|
||||
"""
|
||||
if self.vars:
|
||||
print >>out, "\nConfiguration variables for %s:\n" % self.name
|
||||
for v in self.vars:
|
||||
v.doHelp(out)
|
||||
print >>out
|
||||
|
||||
class Var:
|
||||
"""A single variable.
|
||||
"""
|
||||
|
||||
def __init__(self, name, use, check):
|
||||
"""Create a variable.
|
||||
|
||||
name variable name
|
||||
use variable use string
|
||||
check variable value check function
|
||||
"""
|
||||
self.name = name
|
||||
self.use = use or ''
|
||||
self.check = check
|
||||
|
||||
def doCheck(self, env):
|
||||
"""Execute the check and set the variable to the new value.
|
||||
"""
|
||||
if not self.check: return
|
||||
env[self.name] = self.check(self.name, env.get(self.name))
|
||||
|
||||
def doHelp(self, out):
|
||||
"""Print help for the variable.
|
||||
"""
|
||||
print >>out, "%-12s" % self.name, self.use
|
||||
|
||||
|
||||
@@ -5,10 +5,12 @@ import os
|
||||
import os.path
|
||||
import sys
|
||||
from getopt import getopt
|
||||
import socket
|
||||
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import sxp
|
||||
from xen.xend.XendClient import server
|
||||
from xen.xend.XendClient import main as xend_client_main
|
||||
from xen.xm import create, shutdown
|
||||
|
||||
class Prog:
|
||||
@@ -65,6 +67,13 @@ class Xm:
|
||||
sys.exit(1)
|
||||
|
||||
def main(self, args):
|
||||
try:
|
||||
self.main_call(args)
|
||||
except socket.error, ex:
|
||||
print >>sys.stderr, ex
|
||||
self.err("Error connecting to xend, is xend running?")
|
||||
|
||||
def main_call(self, args):
|
||||
"""Main entry point. Dispatches to the progs.
|
||||
"""
|
||||
self.name = args[0]
|
||||
@@ -478,5 +487,21 @@ class ProgConsole(Prog):
|
||||
|
||||
xm.prog(ProgConsole)
|
||||
|
||||
class ProgCall(Prog):
|
||||
name = "call"
|
||||
info = "Call xend api functions."
|
||||
|
||||
def help (self, args):
|
||||
print "call fn argss..."
|
||||
print """
|
||||
Call a xend HTTP API function. The leading 'xend_' on the function
|
||||
can be omitted. See xen.xend.XendClient for the API functions.
|
||||
"""
|
||||
|
||||
def main(self, args):
|
||||
xend_client_main(args)
|
||||
|
||||
xm.prog(ProgCall)
|
||||
|
||||
def main(args):
|
||||
xm.main(args)
|
||||
+92
-13
@@ -126,6 +126,42 @@ class Opt:
|
||||
"""
|
||||
return self.specified_opt
|
||||
|
||||
class OptVar(Opt):
|
||||
"""An individual option variable.
|
||||
"""
|
||||
def __init__(self, opts, name,
|
||||
val=None, fn=None, use=None, default=None):
|
||||
"""Create an option.
|
||||
|
||||
opts parent options object
|
||||
name name of the field it controls
|
||||
val string used to print option args in help.
|
||||
If val is not specified the option has no arg.
|
||||
fn function to call when the option is specified.
|
||||
use usage (help) string
|
||||
default default value if not specified on command-line
|
||||
"""
|
||||
if val is None:
|
||||
val = name.upper()
|
||||
Opt.__init__(self, opts, name, val=val, fn=fn, use=use, default=default)
|
||||
self.optkeys = []
|
||||
self.optkeys.append(self.long)
|
||||
|
||||
def short_opt(self):
|
||||
return None
|
||||
|
||||
def long_opt(self):
|
||||
return None
|
||||
|
||||
def show(self):
|
||||
print '%s=%s' %(self.optkeys[0], self.val)
|
||||
print
|
||||
if self.use:
|
||||
print '\t',
|
||||
print self.use
|
||||
if self.val:
|
||||
print '\tDefault', self.default or 'None'
|
||||
|
||||
class OptVals:
|
||||
"""Class to hold option values.
|
||||
"""
|
||||
@@ -134,6 +170,13 @@ class OptVals:
|
||||
class Opts:
|
||||
"""Container for options.
|
||||
"""
|
||||
|
||||
imports = ["import sys",
|
||||
"import os",
|
||||
"import os.path",
|
||||
"from xen.util.ip import *",
|
||||
]
|
||||
|
||||
def __init__(self, use=None):
|
||||
"""Options constructor.
|
||||
|
||||
@@ -168,6 +211,12 @@ class Opts:
|
||||
self.options_map[name] = x
|
||||
return x
|
||||
|
||||
def var(self, name, **args):
|
||||
x = OptVar(self, name, **args)
|
||||
self.options.append(x)
|
||||
self.options_map[name] = x
|
||||
return x
|
||||
|
||||
def setvar(self, var, val):
|
||||
"""Set a default script variable.
|
||||
"""
|
||||
@@ -232,7 +281,18 @@ class Opts:
|
||||
else:
|
||||
print >>sys.stderr, "Error: Unknown option:", k
|
||||
self.usage()
|
||||
return args
|
||||
xargs = []
|
||||
for arg in args:
|
||||
isvar = 0
|
||||
if '=' in arg:
|
||||
(k, v) = arg.split('=', 1)
|
||||
for opt in self.options:
|
||||
if opt.specify(k, v):
|
||||
isvar = 1
|
||||
break
|
||||
if not isvar:
|
||||
xargs.append(arg)
|
||||
return xargs
|
||||
|
||||
def short_opts(self):
|
||||
"""Get short options specifier for getopt.
|
||||
@@ -257,9 +317,10 @@ class Opts:
|
||||
def usage(self):
|
||||
print 'Usage: ', self.argv[0], self.use or 'OPTIONS'
|
||||
for opt in self.options:
|
||||
print
|
||||
opt.show()
|
||||
|
||||
def load_defaults(self):
|
||||
def load_defaults(self, help=0):
|
||||
"""Load a defaults script. Assumes these options set:
|
||||
'path' search path
|
||||
'default' script name
|
||||
@@ -270,12 +331,12 @@ class Opts:
|
||||
else:
|
||||
p = self.vals.defaults
|
||||
if os.path.exists(p):
|
||||
self.load(p)
|
||||
self.load(p, help)
|
||||
break
|
||||
else:
|
||||
self.err("Cannot open defaults file %s" % self.vals.defaults)
|
||||
|
||||
def load(self, defaults, help=0):
|
||||
def load(self, defaults, help):
|
||||
"""Load a defaults file. Local variables in the file
|
||||
are used to set options with the same names.
|
||||
Variables are not used to set options that are already specified.
|
||||
@@ -287,15 +348,22 @@ class Opts:
|
||||
globals = {}
|
||||
locals = {}
|
||||
locals.update(self.vars)
|
||||
cmd = '\n'.join(["import sys",
|
||||
"import os",
|
||||
"import os.path",
|
||||
"import xen.util.ip",
|
||||
"xm_file = '%s'" % defaults,
|
||||
"xm_help = %d" % help ])
|
||||
cmd = '\n'.join(self.imports +
|
||||
[ "from xen.xm.help import Vars",
|
||||
"xm_file = '%s'" % defaults,
|
||||
"xm_help = %d" % help,
|
||||
"xm_vars = Vars(xm_file, xm_help, locals())"
|
||||
])
|
||||
exec cmd in globals, locals
|
||||
execfile(defaults, globals, locals)
|
||||
if help: return
|
||||
try:
|
||||
execfile(defaults, globals, locals)
|
||||
except:
|
||||
if not help: raise
|
||||
if help:
|
||||
print 'The following imports are done automatically:'
|
||||
for x in self.imports:
|
||||
print x
|
||||
return
|
||||
# Extract the values set by the script and set the corresponding
|
||||
# options, if not set on the command line.
|
||||
vtypes = [ types.StringType,
|
||||
@@ -316,6 +384,17 @@ def set_false(opt, k, v):
|
||||
"""Set an option false."""
|
||||
opt.set(0)
|
||||
|
||||
def set_bool(opt, k, v):
|
||||
"""Set a boolean option.
|
||||
"""
|
||||
if v in ['yes']:
|
||||
opt.set(1)
|
||||
elif v in ['no']:
|
||||
opt.set(0)
|
||||
else:
|
||||
opt.opts.err('Invalid value:' +v)
|
||||
|
||||
|
||||
def set_value(opt, k, v):
|
||||
"""Set an option to a valoue."""
|
||||
opt.set(v)
|
||||
@@ -335,6 +414,6 @@ def append_value(opt, k, v):
|
||||
def set_var(opt, k, v):
|
||||
"""Set a default script variable.
|
||||
"""
|
||||
(var, val) = v.strip().split('=')
|
||||
(var, val) = v.strip().split('=', 1)
|
||||
opt.opts.setvar(var.strip(), val.strip())
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright (C) 2004 Mike Wray <mike.wray@hp.com>
|
||||
|
||||
"""Domain shutdown.
|
||||
"""
|
||||
import string
|
||||
@@ -10,7 +11,8 @@ from xen.xm.opts import *
|
||||
|
||||
gopts = Opts(use="""[options] [DOM]
|
||||
|
||||
Shutdown one or more domains gracefully.""")
|
||||
Shutdown one or more domains gracefully.
|
||||
""")
|
||||
|
||||
gopts.opt('help', short='h',
|
||||
fn=set_true, default=0,
|
||||
@@ -24,18 +26,22 @@ gopts.opt('wait', short='w',
|
||||
fn=set_true, default=0,
|
||||
use='Wait for shutdown to complete.')
|
||||
|
||||
gopts.opt('norestart', short='n',
|
||||
gopts.opt('halt', short='H',
|
||||
fn=set_true, default=0,
|
||||
use='Prevent domain restart.')
|
||||
use='Shutdown without reboot.')
|
||||
|
||||
def shutdown(opts, doms, wait):
|
||||
gopts.opt('reboot', short='R',
|
||||
fn=set_true, default=0,
|
||||
use='Shutdown and reboot.')
|
||||
|
||||
def shutdown(opts, doms, mode, wait):
|
||||
def domains():
|
||||
return [ int(a) for a in server.xend_domains() ]
|
||||
if doms == None: doms = domains()
|
||||
if 0 in doms:
|
||||
doms.remove(0)
|
||||
for d in doms:
|
||||
server.xend_domain_shutdown(d)
|
||||
server.xend_domain_shutdown(d, mode)
|
||||
if wait:
|
||||
while doms:
|
||||
alive = domains()
|
||||
@@ -49,6 +55,21 @@ def shutdown(opts, doms, wait):
|
||||
time.sleep(1)
|
||||
opts.info("All domains terminated")
|
||||
|
||||
def shutdown_mode(opts):
|
||||
mode = 'poweroff'
|
||||
if opts.vals.wait:
|
||||
mode = 'halt'
|
||||
if opts.vals.reboot:
|
||||
opts.err("Can't specify wait and reboot")
|
||||
else:
|
||||
if opts.vals.halt and opts.vals.reboot:
|
||||
opts.err("Can't specify halt and reboot")
|
||||
if opts.vals.halt:
|
||||
mode = 'halt'
|
||||
elif opts.vals.reboot:
|
||||
mode = 'reboot'
|
||||
return mode
|
||||
|
||||
def main_all(opts, args):
|
||||
shutdown(opts, None, opts.vals.wait)
|
||||
|
||||
@@ -59,7 +80,9 @@ def main_dom(opts, args):
|
||||
domid = int(dom)
|
||||
except:
|
||||
opts.err('Invalid domain: ' + dom)
|
||||
shutdown(opts, [ domid ], opts.vals.wait)
|
||||
|
||||
mode = shutdown_mode(opts)
|
||||
shutdown(opts, [ domid ], mode, opts.vals.wait)
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
|
||||