mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
387 lines
11 KiB
Python
387 lines
11 KiB
Python
#!/usr/bin/python
|
|
"""
|
|
Copyright (C) International Business Machines Corp., 2005
|
|
Author: Dan Smith <danms@us.ibm.com>
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; under version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
"""
|
|
|
|
import sys
|
|
import commands
|
|
import re
|
|
import time
|
|
|
|
from Xm import *
|
|
from arch import *
|
|
from Test import *
|
|
from config import *
|
|
from Console import *
|
|
from XenDevice import *
|
|
from DomainTracking import *
|
|
|
|
|
|
DOM0_UUID = "00000000-0000-0000-0000-000000000000"
|
|
|
|
|
|
def getDefaultKernel():
|
|
return arch.getDefaultKernel()
|
|
|
|
def getRdPath():
|
|
return arch.getRdPath()
|
|
|
|
def getUniqueName():
|
|
"""Get a uniqueish name for use in a domain"""
|
|
unixtime = int(time.time())
|
|
test_name = sys.argv[0]
|
|
test_name = re.sub("\.test", "", test_name)
|
|
test_name = re.sub("[\/\.]", "", test_name)
|
|
name = "%s-%i" % (test_name, unixtime)
|
|
|
|
return name
|
|
|
|
class XenConfig:
|
|
"""An object to help create a xen-compliant config file"""
|
|
def __init__(self):
|
|
self.defaultOpts = {}
|
|
|
|
# These options need to be lists
|
|
self.defaultOpts["disk"] = []
|
|
self.defaultOpts["vif"] = []
|
|
self.defaultOpts["vtpm"] = []
|
|
|
|
self.opts = self.defaultOpts
|
|
|
|
def toString(self):
|
|
"""Convert this config to a string for writing out
|
|
to a file"""
|
|
string = "# Xen configuration generated by xm-test\n"
|
|
for k, v in self.opts.items():
|
|
if isinstance(v, int):
|
|
piece = "%s = %i" % (k, v)
|
|
elif isinstance(v, list) and v:
|
|
piece = "%s = %s" % (k, v)
|
|
elif isinstance(v, str) and v:
|
|
piece = "%s = \"%s\"" % (k, v)
|
|
else:
|
|
piece = None
|
|
|
|
if piece:
|
|
string += "%s\n" % piece
|
|
|
|
return string
|
|
|
|
def write(self, filename):
|
|
"""Write this config out to filename"""
|
|
output = file(filename, "w")
|
|
output.write(self.toString())
|
|
output.close()
|
|
|
|
def __str__(self):
|
|
"""When used as a string, we represent ourself by a config
|
|
filename, which points to a temporary config that we write
|
|
out ahead of time"""
|
|
filename = "/tmp/xm-test.conf"
|
|
self.write(filename)
|
|
return filename
|
|
|
|
def setOpt(self, name, value):
|
|
"""Set an option in the config"""
|
|
if name in self.opts.keys() and isinstance(self.opts[name] ,
|
|
list) and not isinstance(value, list):
|
|
self.opts[name] = [value]
|
|
# "extra" is special so append to it.
|
|
elif name == "extra" and name in self.opts.keys():
|
|
self.opts[name] += " %s" % (value)
|
|
else:
|
|
self.opts[name] = value
|
|
|
|
def appOpt(self, name, value):
|
|
"""Append a value to a list option"""
|
|
if name in self.opts.keys() and isinstance(self.opts[name], list):
|
|
self.opts[name].append(value)
|
|
|
|
def getOpt(self, name):
|
|
"""Return the value of a config option"""
|
|
if name in self.opts.keys():
|
|
return self.opts[name]
|
|
else:
|
|
return None
|
|
|
|
def setOpts(self, opts):
|
|
"""Batch-set options from a dictionary"""
|
|
for k, v in opts.items():
|
|
self.setOpt(k, v)
|
|
|
|
def clearOpts(self, name=None):
|
|
"""Clear one or all config options"""
|
|
if name:
|
|
self.opts[name] = self.defaultOpts[name]
|
|
else:
|
|
self.opts = self.defaultOpts
|
|
|
|
class DomainError(Exception):
|
|
def __init__(self, msg, extra="", errorcode=0):
|
|
self.msg = msg
|
|
self.extra = extra
|
|
try:
|
|
self.errorcode = int(errorcode)
|
|
except Exception, e:
|
|
self.errorcode = -1
|
|
|
|
def __str__(self):
|
|
return str(self.msg)
|
|
|
|
|
|
class XenDomain:
|
|
|
|
def __init__(self, name=None, config=None, isManaged=False):
|
|
"""Create a domain object.
|
|
@param config: String filename of config file
|
|
"""
|
|
|
|
if name:
|
|
self.name = name
|
|
else:
|
|
self.name = getUniqueName()
|
|
|
|
self.config = config
|
|
self.console = None
|
|
self.devices = {}
|
|
self.netEnv = "bridge"
|
|
|
|
if os.getenv("XM_MANAGED_DOMAINS"):
|
|
isManaged = True
|
|
self.isManaged = isManaged
|
|
|
|
# Set domain type, either PV for ParaVirt domU or HVM for
|
|
# FullVirt domain
|
|
if ENABLE_HVM_SUPPORT:
|
|
self.type = "HVM"
|
|
else:
|
|
self.type = "PV"
|
|
|
|
def start(self, noConsole=False):
|
|
|
|
if not self.isManaged:
|
|
ret, output = traceCommand("xm create %s" % self.config)
|
|
print self.config
|
|
else:
|
|
ret, output = traceCommand("xm new %s" % self.config)
|
|
if ret != 0:
|
|
_ret, output = traceCommand("xm delete " +
|
|
self.config.getOpt("name"))
|
|
else:
|
|
ret, output = traceCommand("xm start " +
|
|
self.config.getOpt("name"))
|
|
addManagedDomain(self.config.getOpt("name"))
|
|
|
|
if ret != 0:
|
|
raise DomainError("Failed to create domain",
|
|
extra=output,
|
|
errorcode=ret)
|
|
|
|
# HVM domains require waiting for boot
|
|
if self.getDomainType() == "HVM":
|
|
waitForBoot()
|
|
|
|
# Go through device list and run console cmds
|
|
for dev in self.devices.keys():
|
|
self.devices[dev].execAddCmds()
|
|
|
|
if self.console and noConsole == True:
|
|
self.closeConsole()
|
|
|
|
elif self.console and noConsole == False:
|
|
return self.console
|
|
|
|
elif not self.console and noConsole == False:
|
|
return self.getConsole()
|
|
|
|
def stop(self):
|
|
prog = "xm"
|
|
cmd = " shutdown "
|
|
|
|
self.removeAllDevices()
|
|
|
|
if self.console:
|
|
self.closeConsole()
|
|
|
|
ret, output = traceCommand(prog + cmd + self.config.getOpt("name"))
|
|
|
|
return ret
|
|
|
|
def destroy(self):
|
|
prog = "xm"
|
|
cmd = " destroy "
|
|
|
|
self.removeAllDevices()
|
|
|
|
if self.console:
|
|
self.closeConsole()
|
|
|
|
ret, output = traceCommand(prog + cmd + self.config.getOpt("name"))
|
|
if self.isManaged:
|
|
ret, output = traceCommand(prog + " delete " +
|
|
self.config.getOpt("name"))
|
|
delManagedDomain(self.config.getOpt("name"))
|
|
|
|
return ret
|
|
|
|
def getName(self):
|
|
return self.name
|
|
|
|
def getId(self):
|
|
return domid(self.getName());
|
|
|
|
def getDomainType(self):
|
|
return self.type
|
|
|
|
def closeConsole(self):
|
|
# The domain closeConsole command must be called by tests, not the
|
|
# console's close command. Once close is called, the console is
|
|
# gone. You can't get history or anything else from it.
|
|
if self.console:
|
|
self.console._XmConsole__closeConsole()
|
|
self.console = None
|
|
|
|
def getConsole(self):
|
|
if self.console:
|
|
self.closeConsole()
|
|
|
|
self.console = XmConsole(self.getName())
|
|
# Activate the console
|
|
self.console.sendInput("input")
|
|
|
|
return self.console
|
|
|
|
def newDevice(self, Device, *args):
|
|
"""Device Factory: Generic factory for creating new XenDevices.
|
|
All device creation should be done through the XenDomain
|
|
factory. Supply a XenDevice instance and its args and the
|
|
constructor will be called."""
|
|
# Make sure device with id hasn't already been added
|
|
if self.devices.has_key(args[0]):
|
|
raise DeviceError("Error: Domain already has device %s" % args[0])
|
|
|
|
# Call constructor for supplied Device instance
|
|
dargs = (self,)
|
|
dargs += args
|
|
dev = apply(Device, dargs)
|
|
|
|
if self.isRunning():
|
|
# Note: This needs to be done, XenDevice should have an attach
|
|
# method.
|
|
print "Domain is running, need to attach new device to domain."
|
|
|
|
self.devices[dev.id] = dev
|
|
self.config.appOpt(dev.configNode, str(dev))
|
|
return dev
|
|
|
|
def removeDevice(self, id):
|
|
if self.devices.has_key(id):
|
|
self.devices[id].removeDevice()
|
|
|
|
def removeAllDevices(self):
|
|
for k in self.devices.keys():
|
|
self.removeDevice(k)
|
|
|
|
def isRunning(self):
|
|
return isDomainRunning(self.name)
|
|
|
|
def getNetEnv(self):
|
|
# We need to know the network environment: bridge, NAT, or routed.
|
|
return self.netEnv
|
|
|
|
def getDevice(self, id):
|
|
dev = self.devices[id]
|
|
if dev:
|
|
return dev
|
|
print "Device %s not found for domain %s" % (id, self.getName())
|
|
|
|
|
|
class XmTestDomain(XenDomain):
|
|
|
|
def __init__(self, name=None, extraConfig=None,
|
|
baseConfig=arch.configDefaults, isManaged=False):
|
|
"""Create a new xm-test domain
|
|
@param name: The requested domain name
|
|
@param extraConfig: Additional configuration options
|
|
@param baseConfig: The initial configuration defaults to use
|
|
"""
|
|
config = XenConfig()
|
|
config.setOpts(baseConfig)
|
|
if extraConfig:
|
|
config.setOpts(extraConfig)
|
|
|
|
if name:
|
|
config.setOpt("name", name)
|
|
elif not config.getOpt("name"):
|
|
config.setOpt("name", getUniqueName())
|
|
|
|
XenDomain.__init__(self, config.getOpt("name"), config=config,
|
|
isManaged=isManaged)
|
|
|
|
def minSafeMem(self):
|
|
return arch.minSafeMem
|
|
|
|
class XmTestNetDomain(XmTestDomain):
|
|
|
|
def __init__(self, name=None, extraConfig=None,
|
|
baseConfig=arch.configDefaults):
|
|
"""Create a new xm-test domain with one network device
|
|
@param name: The requested domain name
|
|
@param extraConfig: Additional configuration options
|
|
@param baseConfig: The initial configuration defaults to use
|
|
"""
|
|
config = XenConfig()
|
|
config.setOpts(baseConfig)
|
|
if extraConfig:
|
|
config.setOpts(extraConfig)
|
|
|
|
if name:
|
|
config.setOpt("name", name)
|
|
elif not config.getOpt("name"):
|
|
config.setOpt("name", getUniqueName())
|
|
|
|
XenDomain.__init__(self, config.getOpt("name"), config=config)
|
|
|
|
# Add one network devices to domain
|
|
self.newDevice(XenNetDevice, "eth0")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
c = XenConfig()
|
|
|
|
c.setOpt("foo", "bar")
|
|
c.setOpt("foob", 1)
|
|
opts = {"opt1" : 19,
|
|
"opt2" : "blah"}
|
|
c.setOpts(opts)
|
|
|
|
c.setOpt("disk", "phy:/dev/ram0,hda1,w")
|
|
c.appOpt("disk", "phy:/dev/ram1,hdb1,w")
|
|
|
|
print str(c)
|
|
|
|
|
|
|
|
# c.write("/tmp/foo.conf")
|
|
|
|
# d = XmTestDomain();
|
|
#
|
|
# d.start();
|
|
|