mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
merge with xen-unstable.hg
This commit is contained in:
commit
a77c31c882
341 files changed
+14414
-6261
No files matched your search
+16
-2
@@ -7,16 +7,30 @@ all: build
|
||||
.PHONY: build
|
||||
build:
|
||||
CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py build
|
||||
if which $(MSGFMT) >/dev/null ; then \
|
||||
for file in `cd xen/xm; find messages -name xen-xm.po`; do \
|
||||
dest=`echo "build/$$file" | \
|
||||
sed -e 's#xen-xm.po#LC_MESSAGES/xen-xm.mo#'`; \
|
||||
mkdir -p `dirname "$$dest"`; \
|
||||
$(MSGFMT) -c -o "$$dest" "xen/xm/$$file"; \
|
||||
done; \
|
||||
fi
|
||||
|
||||
.PHONY: install
|
||||
ifndef XEN_PYTHON_NATIVE_INSTALL
|
||||
install: all
|
||||
install: install-messages
|
||||
CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --home="$(DESTDIR)/usr" --prefix="" --force
|
||||
else
|
||||
install: all
|
||||
install: install-messages
|
||||
CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py install --root="$(DESTDIR)" --force
|
||||
endif
|
||||
|
||||
install-messages: all
|
||||
if which $(MSGFMT) >/dev/null ; then \
|
||||
mkdir -p "$(DESTDIR)/usr/share/locale"; \
|
||||
cp -R build/messages/* "$(DESTDIR)/usr/share/locale/"; \
|
||||
fi
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
export LD_LIBRARY_PATH=$$(readlink -f ../libxc):$$(readlink -f ../xenstore); python test.py -b -u
|
||||
|
||||
@@ -41,6 +41,7 @@ VBD_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(VDI)-8s '\
|
||||
COMMANDS = {
|
||||
'host-info': ('', 'Get Xen Host Info'),
|
||||
'host-set-name': ('', 'Set host name'),
|
||||
'pif-list': ('', 'List all PIFs'),
|
||||
'sr-list': ('', 'List all SRs'),
|
||||
'vbd-list': ('', 'List all VBDs'),
|
||||
'vbd-create': ('<domname> <pycfg> [opts]',
|
||||
@@ -63,6 +64,15 @@ COMMANDS = {
|
||||
}
|
||||
|
||||
OPTIONS = {
|
||||
'sr-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of SR'})
|
||||
],
|
||||
|
||||
'vdi-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of VDI'})
|
||||
],
|
||||
'vm-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of VMs'})
|
||||
@@ -145,7 +155,7 @@ _session = None
|
||||
def _connect(*args):
|
||||
global _server, _session, _initialised
|
||||
if not _initialised:
|
||||
_server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock')
|
||||
_server = ServerProxy('httpu:///var/run/xend/xen-api.sock')
|
||||
login = raw_input("Login: ")
|
||||
password = getpass()
|
||||
creds = (login, password)
|
||||
@@ -361,29 +371,53 @@ def xapi_vbd_list(*args):
|
||||
print VBD_LIST_FORMAT % vbd_struct
|
||||
|
||||
def xapi_vdi_list(*args):
|
||||
opts, args = parse_args('vdi-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
server, session = _connect()
|
||||
vdis = execute(server.VDI.get_all, session)
|
||||
|
||||
print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
|
||||
'uuid' : 'UUID',
|
||||
'virtual_size': 'Sectors',
|
||||
'sector_size': 'Sector Size'}
|
||||
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server.VDI.get_record, session, vdi)
|
||||
print VDI_LIST_FORMAT % vdi_struct
|
||||
if not is_long:
|
||||
print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
|
||||
'uuid' : 'UUID',
|
||||
'virtual_size': 'Sectors',
|
||||
'sector_size': 'Sector Size'}
|
||||
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server.VDI.get_record, session, vdi)
|
||||
print VDI_LIST_FORMAT % vdi_struct
|
||||
|
||||
else:
|
||||
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server.VDI.get_record, session, vdi)
|
||||
pprint(vdi_struct)
|
||||
|
||||
def xapi_sr_list(*args):
|
||||
opts, args = parse_args('sr-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
server, session = _connect()
|
||||
srs = execute(server.SR.get_all, session)
|
||||
print SR_LIST_FORMAT % {'name_label': 'SR Label',
|
||||
'uuid' : 'UUID',
|
||||
'physical_size': 'Size',
|
||||
'type': 'Type'}
|
||||
for sr in srs:
|
||||
sr_struct = execute(server.SR.get_record, session, sr)
|
||||
sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB
|
||||
print SR_LIST_FORMAT % sr_struct
|
||||
if not is_long:
|
||||
print SR_LIST_FORMAT % {'name_label': 'SR Label',
|
||||
'uuid' : 'UUID',
|
||||
'physical_size': 'Size (MB)',
|
||||
'type': 'Type'}
|
||||
|
||||
for sr in srs:
|
||||
sr_struct = execute(server.SR.get_record, session, sr)
|
||||
sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB
|
||||
print SR_LIST_FORMAT % sr_struct
|
||||
else:
|
||||
for sr in srs:
|
||||
sr_struct = execute(server.SR.get_record, session, sr)
|
||||
pprint(sr_struct)
|
||||
|
||||
def xapi_sr_rename(*args):
|
||||
server, session = _connect()
|
||||
sr = execute(server.SR.get_by_name_label, session, args[0])
|
||||
execute(server.SR.set_name_label, session, sr[0], args[1])
|
||||
|
||||
def xapi_vdi_create(*args):
|
||||
opts, args = parse_args('vdi-create', args)
|
||||
@@ -421,10 +455,11 @@ def xapi_vdi_rename(*args):
|
||||
if len(args) < 2:
|
||||
raise OptionError('Not enough arguments')
|
||||
|
||||
vdi_uuid = args[0]
|
||||
vdi_uuid = execute(server.VDI.get_by_name_label, session, args[0])
|
||||
vdi_name = args[1]
|
||||
print 'Renaming VDI %s to %s' % (vdi_uuid, vdi_name)
|
||||
result = execute(server.VDI.set_name_label, session, vdi_uuid, vdi_name)
|
||||
|
||||
print 'Renaming VDI %s to %s' % (vdi_uuid[0], vdi_name)
|
||||
result = execute(server.VDI.set_name_label, session, vdi_uuid[0], vdi_name)
|
||||
print 'Done.'
|
||||
|
||||
|
||||
@@ -448,6 +483,14 @@ def xapi_vtpm_create(*args):
|
||||
print "Has vtpm record '%s'" % vtpm_rec
|
||||
|
||||
|
||||
def xapi_pif_list(*args):
|
||||
server, session = _connect()
|
||||
pif_uuids = execute(server.PIF.get_all, session)
|
||||
for pif_uuid in pif_uuids:
|
||||
pif = execute(server.PIF.get_record, session, pif_uuid)
|
||||
print pif
|
||||
|
||||
|
||||
#
|
||||
# Command Line Utils
|
||||
#
|
||||
@@ -517,10 +560,12 @@ def usage(command = None, print_usage = True):
|
||||
print
|
||||
print 'Subcommands:'
|
||||
print
|
||||
sorted_commands = sorted(COMMANDS.keys())
|
||||
for command in sorted_commands:
|
||||
args, description = COMMANDS[command]
|
||||
print '%-16s %-40s' % (command, description)
|
||||
|
||||
for func in sorted(globals().keys()):
|
||||
if func.startswith('xapi_'):
|
||||
command = func[5:].replace('_', '-')
|
||||
args, description = COMMANDS.get(command, ('', ''))
|
||||
print '%-16s %-40s' % (command, description)
|
||||
print
|
||||
else:
|
||||
parse_args(command, ['-h'])
|
||||
@@ -549,7 +594,7 @@ def main(args):
|
||||
try:
|
||||
subcmd_func(*args[1:])
|
||||
except XenAPIError, e:
|
||||
print 'Error: %s' % str(e.args[1])
|
||||
print 'Error: %s' % str(e.args[0])
|
||||
sys.exit(2)
|
||||
except OptionError, e:
|
||||
print 'Error: %s' % e
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
fprintf(stderr, "ERROR: " _m " (%d = %s)\n" , ## _a , \
|
||||
errno, strerror(errno))
|
||||
|
||||
static PyObject *acm_error_obj;
|
||||
|
||||
/* generic shared function */
|
||||
void * __getssid(int domid, uint32_t *buflen)
|
||||
{
|
||||
@@ -80,28 +82,26 @@ static PyObject *policy(PyObject * self, PyObject * args)
|
||||
{
|
||||
/* out */
|
||||
char *policyreference;
|
||||
PyObject *ret = NULL;
|
||||
PyObject *ret;
|
||||
void *ssid_buffer;
|
||||
uint32_t buf_len;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "", NULL)) {
|
||||
goto out1;
|
||||
return NULL;
|
||||
}
|
||||
ssid_buffer = __getssid(0, &buf_len);
|
||||
if (ssid_buffer == NULL) {
|
||||
goto out1;
|
||||
} else if (buf_len < sizeof(struct acm_ssid_buffer)) {
|
||||
goto out2;
|
||||
} else {
|
||||
if (ssid_buffer == NULL || buf_len < sizeof(struct acm_ssid_buffer)) {
|
||||
free(ssid_buffer);
|
||||
return PyErr_SetFromErrno(acm_error_obj);
|
||||
}
|
||||
else {
|
||||
struct acm_ssid_buffer *ssid = (struct acm_ssid_buffer *)ssid_buffer;
|
||||
policyreference = (char *)(ssid_buffer + ssid->policy_reference_offset
|
||||
+ sizeof (struct acm_policy_reference_buffer));
|
||||
ret = Py_BuildValue("s", policyreference);
|
||||
free(ssid_buffer);
|
||||
return ret;
|
||||
}
|
||||
ret = Py_BuildValue("s", policyreference);
|
||||
out2:
|
||||
free(ssid_buffer);
|
||||
out1:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -213,5 +213,8 @@ static PyMethodDef acmMethods[] = {
|
||||
/* inits */
|
||||
PyMODINIT_FUNC initacm(void)
|
||||
{
|
||||
Py_InitModule("acm", acmMethods);
|
||||
PyObject *m = Py_InitModule("acm", acmMethods);
|
||||
acm_error_obj = PyErr_NewException("acm.Error", PyExc_RuntimeError, NULL);
|
||||
Py_INCREF(acm_error_obj);
|
||||
PyModule_AddObject(m, "Error", acm_error_obj);
|
||||
}
|
||||
@@ -28,6 +28,31 @@ def cmd(p, s):
|
||||
if not opts.dryrun:
|
||||
os.system(c)
|
||||
|
||||
bridgeRE = re.compile(r'([^\t]*)\t*[^\t]*\t*[^\t]*\t*([^\t]*)')
|
||||
def get_state():
|
||||
fin = os.popen(CMD_BRCTL + ' show', 'r')
|
||||
try:
|
||||
bridges = {}
|
||||
brlist = None
|
||||
brname = None
|
||||
first = True
|
||||
for line in fin:
|
||||
if first:
|
||||
first = False
|
||||
elif line[0] == '\t':
|
||||
brlist.append(line.strip())
|
||||
else:
|
||||
if brname:
|
||||
bridges[brname] = brlist
|
||||
m = bridgeRE.match(line)
|
||||
brname = m.group(1)
|
||||
brlist = [m.group(2).strip()]
|
||||
if brname:
|
||||
bridges[brname] = brlist
|
||||
return bridges
|
||||
finally:
|
||||
fin.close()
|
||||
|
||||
def vif_bridge_add(params):
|
||||
"""Add the network interface for vif on dom to a bridge.
|
||||
"""
|
||||
|
||||
@@ -115,7 +115,7 @@ def get_security_info(info, field):
|
||||
if isinstance(info, dict):
|
||||
security = info['security']
|
||||
elif isinstance(info, list):
|
||||
security = sxp.child_value(info, 'security', )
|
||||
security = sxp.child_value(info, 'security')
|
||||
if not security:
|
||||
if field == 'ssidref':
|
||||
#return default ssid
|
||||
@@ -357,7 +357,7 @@ def refresh_ssidref(config):
|
||||
if isinstance(config, dict):
|
||||
security = config['security']
|
||||
elif isinstance(config, list):
|
||||
security = sxp.child_value(config, 'security',)
|
||||
security = sxp.child_value(config, 'security')
|
||||
else:
|
||||
err("Instance type of config parameter not supported.")
|
||||
if not security:
|
||||
@@ -637,11 +637,11 @@ def res_security_check(resource, domain_label):
|
||||
"""
|
||||
rtnval = 1
|
||||
|
||||
#build canonical resource name
|
||||
resource = unify_resname(resource)
|
||||
|
||||
# if security is on, ask the hypervisor for a decision
|
||||
if on():
|
||||
#build canonical resource name
|
||||
resource = unify_resname(resource)
|
||||
|
||||
(label, ssidref, policy) = get_res_security_details(resource)
|
||||
domac = ['access_control']
|
||||
domac.append(['policy', active_policy])
|
||||
@@ -660,6 +660,8 @@ def res_security_check(resource, domain_label):
|
||||
|
||||
# security is off, make sure resource isn't labeled
|
||||
else:
|
||||
# Note, we can't canonicalise the resource here, because people using
|
||||
# xm without ACM are free to use relative paths.
|
||||
(label, policy) = get_res_label(resource)
|
||||
if policy != 'NULL':
|
||||
raise ACMError("Security is off, but '"+resource+"' is labeled")
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
An enhanced XML-RPC client/server interface for Python.
|
||||
"""
|
||||
|
||||
import re
|
||||
import string
|
||||
import fcntl
|
||||
from types import *
|
||||
@@ -49,13 +50,15 @@ except ImportError:
|
||||
|
||||
|
||||
def stringify(value):
|
||||
if isinstance(value, IntType) and not isinstance(value, BooleanType):
|
||||
if isinstance(value, float) or \
|
||||
isinstance(value, long) or \
|
||||
(isinstance(value, int) and not isinstance(value, bool)):
|
||||
return str(value)
|
||||
elif isinstance(value, DictType):
|
||||
elif isinstance(value, dict):
|
||||
for k, v in value.items():
|
||||
value[k] = stringify(v)
|
||||
return value
|
||||
elif isinstance(value, (TupleType, ListType)):
|
||||
elif isinstance(value, (tuple, list)):
|
||||
return [stringify(v) for v in value]
|
||||
else:
|
||||
return value
|
||||
@@ -163,8 +166,10 @@ class ServerProxy(xmlrpclib.ServerProxy):
|
||||
class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
|
||||
allow_reuse_address = True
|
||||
|
||||
def __init__(self, addr, allowed, requestHandler=None,
|
||||
def __init__(self, addr, allowed, xenapi, requestHandler=None,
|
||||
logRequests = 1):
|
||||
self.xenapi = xenapi
|
||||
|
||||
if requestHandler is None:
|
||||
requestHandler = XMLRPCRequestHandler
|
||||
SimpleXMLRPCServer.__init__(self, addr,
|
||||
@@ -182,7 +187,7 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
|
||||
flags |= fcntl.FD_CLOEXEC
|
||||
fcntl.fcntl(client.fileno(), fcntl.F_SETFD, flags)
|
||||
return (client, addr)
|
||||
|
||||
|
||||
def _marshaled_dispatch(self, data, dispatch_method = None):
|
||||
params, method = xmlrpclib.loads(data)
|
||||
if False:
|
||||
@@ -214,13 +219,30 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
|
||||
except xmlrpclib.Fault, fault:
|
||||
response = xmlrpclib.dumps(fault)
|
||||
except Exception, exn:
|
||||
import xen.xend.XendClient
|
||||
log.exception(exn)
|
||||
response = xmlrpclib.dumps(
|
||||
xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
|
||||
|
||||
if self.xenapi:
|
||||
if _is_not_supported(exn):
|
||||
errdesc = ['MESSAGE_METHOD_UNKNOWN', method]
|
||||
else:
|
||||
log.exception('Internal error handling %s', method)
|
||||
errdesc = ['INTERNAL_ERROR', str(exn)]
|
||||
response = xmlrpclib.dumps(
|
||||
({ "Status": "Failure",
|
||||
"ErrorDescription": errdesc },),
|
||||
methodresponse = 1)
|
||||
else:
|
||||
log.exception('Internal error handling %s', method)
|
||||
import xen.xend.XendClient
|
||||
response = xmlrpclib.dumps(
|
||||
xmlrpclib.Fault(xen.xend.XendClient.ERROR_INTERNAL, str(exn)))
|
||||
return response
|
||||
|
||||
|
||||
notSupportedRE = re.compile(r'method "(.*)" is not supported')
|
||||
def _is_not_supported(exn):
|
||||
m = notSupportedRE.search(exn[0])
|
||||
return m is not None
|
||||
|
||||
|
||||
# This is a XML-RPC server that sits on a Unix domain socket.
|
||||
# It implements proper support for allow_reuse_address by
|
||||
# unlink()'ing an existing socket.
|
||||
@@ -235,10 +257,10 @@ class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
|
||||
class UnixXMLRPCServer(TCPXMLRPCServer):
|
||||
address_family = socket.AF_UNIX
|
||||
|
||||
def __init__(self, addr, allowed, logRequests = 1):
|
||||
def __init__(self, addr, allowed, xenapi, logRequests = 1):
|
||||
mkdir.parents(os.path.dirname(addr), stat.S_IRWXU, True)
|
||||
if self.allow_reuse_address and os.path.exists(addr):
|
||||
os.unlink(addr)
|
||||
|
||||
TCPXMLRPCServer.__init__(self, addr, allowed,
|
||||
TCPXMLRPCServer.__init__(self, addr, allowed, xenapi,
|
||||
UnixXMLRPCRequestHandler, logRequests)
|
||||
+521
-410
File diff suppressed because it is too large.
Load diff
@@ -41,7 +41,7 @@ XEN_API_CPU_FEATURE = [
|
||||
'CMOV', 'PAT', 'PSE36', 'PN', 'CLFLSH', 'DTES',
|
||||
'ACPI', 'MMX', 'FXCR', 'XMM', 'XMM2', 'SELFSNOOP',
|
||||
'HT', 'ACC', 'IA64', 'SYSCALL', 'MP', 'NX', 'MMXEXT',
|
||||
'LM', '3DNOWEXT', '3DNOW', 'RECOVERY', 'LONGRUN',
|
||||
'LM', 'THREEDNOWEXT', 'THREEDNOW', 'RECOVERY', 'LONGRUN',
|
||||
'LRTI', 'CXMMX', 'K6_MTRR', 'CYRIX_ARR', 'CENTAUR_MCR',
|
||||
'K8', 'K7', 'P3', 'P4', 'CONSTANT_TSC', 'FXSAVE_LEAK',
|
||||
'XMM3', 'MWAIT', 'DSCPL', 'EST', 'TM2', 'CID', 'CX16',
|
||||
@@ -73,3 +73,4 @@ XEN_API_BOOT_TYPE = [
|
||||
XEN_API_VBD_MODE = ['RO', 'RW']
|
||||
XEN_API_VDI_TYPE = ['system', 'user', 'ephemeral']
|
||||
XEN_API_DRIVER_TYPE = ['ioemu', 'paravirtualised']
|
||||
XEN_API_VBD_TYPE = ['CD', 'Disk']
|
||||
@@ -130,16 +130,3 @@ def instance():
|
||||
inst = XendAuthSessions()
|
||||
inst.init()
|
||||
return inst
|
||||
|
||||
# Handy Authentication Decorators
|
||||
# -------------------------------
|
||||
def session_required(func):
|
||||
def check_session(self, session, *args, **kwargs):
|
||||
if instance().is_session_valid(session):
|
||||
return func(self, session, *args, **kwargs)
|
||||
else:
|
||||
return {'Status': 'Failure',
|
||||
'ErrorDescription': XEND_ERROR_SESSION_INVALID}
|
||||
return check_session
|
||||
|
||||
|
||||
@@ -53,6 +53,12 @@ def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
child = os.fork()
|
||||
if (not child):
|
||||
args = [ blexec ]
|
||||
if kernel:
|
||||
args.append("--kernel=%s" % kernel)
|
||||
if ramdisk:
|
||||
args.append("--ramdisk=%s" % ramdisk)
|
||||
if kernel_args:
|
||||
args.append("--args=%s" % kernel_args)
|
||||
if quiet:
|
||||
args.append("-q")
|
||||
args.append("--output=%s" % fifo)
|
||||
|
||||
@@ -93,6 +93,8 @@ XENAPI_CFG_TO_LEGACY_CFG = {
|
||||
'actions_after_reboot': 'on_reboot',
|
||||
'actions_after_crash': 'on_crash',
|
||||
'platform_localtime': 'localtime',
|
||||
'PV_bootloader': 'bootloader',
|
||||
'PV_bootloader_args': 'bootloader_args',
|
||||
}
|
||||
|
||||
LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
|
||||
@@ -139,7 +141,7 @@ XENAPI_CFG_TYPES = {
|
||||
'tpm_backend': int,
|
||||
'PV_bootloader': str,
|
||||
'PV_kernel': str,
|
||||
'PV_initrd': str,
|
||||
'PV_ramdisk': str,
|
||||
'PV_args': str,
|
||||
'PV_bootloader_args': str,
|
||||
'HVM_boot': str,
|
||||
@@ -297,7 +299,7 @@ class XendConfig(dict):
|
||||
self._sxp_to_xapi_unsupported(sxp_obj)
|
||||
elif xapi:
|
||||
self.update_with_xenapi_config(xapi)
|
||||
self._add_xapi_unsupported()
|
||||
self._add_xapi_unsupported(xapi)
|
||||
elif dominfo:
|
||||
# output from xc.domain_getinfo
|
||||
self._dominfo_to_xapi(dominfo)
|
||||
@@ -506,8 +508,12 @@ class XendConfig(dict):
|
||||
pci_devs = []
|
||||
for pci_dev in sxp.children(config, 'dev'):
|
||||
pci_dev_info = {}
|
||||
for opt, val in pci_dev[1:]:
|
||||
pci_dev_info[opt] = val
|
||||
for opt_val in pci_dev[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
pci_dev_info[opt] = val
|
||||
except TypeError:
|
||||
pass
|
||||
pci_devs.append(pci_dev_info)
|
||||
|
||||
cfg['devices'][pci_devs_uuid] = (dev_type,
|
||||
@@ -570,7 +576,6 @@ class XendConfig(dict):
|
||||
if 'security' in cfg and isinstance(cfg['security'], str):
|
||||
cfg['security'] = sxp.from_string(cfg['security'])
|
||||
|
||||
# TODO: get states
|
||||
old_state = sxp.child_value(sxp_cfg, 'state')
|
||||
if old_state:
|
||||
for i in range(len(CONFIG_OLD_DOM_STATES)):
|
||||
@@ -715,6 +720,8 @@ class XendConfig(dict):
|
||||
_set_cfg_if_exists('on_xend_start')
|
||||
_set_cfg_if_exists('vcpu_avail')
|
||||
_set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
|
||||
_set_cfg_if_exists('cpu_weight')
|
||||
_set_cfg_if_exists('cpu_cap')
|
||||
|
||||
# Parse and store runtime configuration
|
||||
_set_cfg_if_exists('start_time')
|
||||
@@ -724,19 +731,22 @@ class XendConfig(dict):
|
||||
_set_cfg_if_exists('up_time')
|
||||
_set_cfg_if_exists('status') # TODO, deprecated
|
||||
|
||||
def _add_xapi_unsupported(self):
|
||||
def _add_xapi_unsupported(self, xapi_dict):
|
||||
"""Updates the configuration object with entries that are not
|
||||
officially supported by the Xen API but is required for
|
||||
the rest of Xend to function.
|
||||
"""
|
||||
|
||||
# populate image
|
||||
hvm = self['HVM_boot'] != ''
|
||||
self['image']['type'] = hvm and 'hvm' or 'linux'
|
||||
if hvm:
|
||||
self['image']['hvm'] = {}
|
||||
for xapi, cfgapi in XENAPI_HVM_CFG.items():
|
||||
self['image']['hvm'][cfgapi] = self[xapi]
|
||||
if 'image' in xapi_dict:
|
||||
self['image'].update(xapi_dict['image'])
|
||||
else:
|
||||
hvm = self['HVM_boot'] != ''
|
||||
self['image']['type'] = hvm and 'hvm' or 'linux'
|
||||
if hvm:
|
||||
self['image']['hvm'] = {}
|
||||
for xapi, cfgapi in XENAPI_HVM_CFG.items():
|
||||
self['image']['hvm'][cfgapi] = self[xapi]
|
||||
|
||||
|
||||
def _get_old_state_string(self):
|
||||
@@ -848,14 +858,15 @@ class XendConfig(dict):
|
||||
for cls in XendDevices.valid_devices():
|
||||
found = False
|
||||
|
||||
# figure if there is a device that is running
|
||||
if domain:
|
||||
# figure if there is a dev controller is valid and running
|
||||
if domain and domain.getDomid() != None:
|
||||
try:
|
||||
controller = domain.getDeviceController(cls)
|
||||
configs = controller.configurations()
|
||||
for config in configs:
|
||||
sxpr.append(['device', config])
|
||||
found = True
|
||||
|
||||
found = True
|
||||
except:
|
||||
log.exception("dumping sxp from device controllers")
|
||||
pass
|
||||
@@ -916,11 +927,12 @@ class XendConfig(dict):
|
||||
dev_type = sxp.name(config)
|
||||
dev_info = {}
|
||||
|
||||
try:
|
||||
for opt, val in config[1:]:
|
||||
for opt_val in config[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
dev_info[opt] = val
|
||||
except ValueError:
|
||||
pass # SXP has no options for this device
|
||||
except (TypeError, ValueError): # unpack error
|
||||
pass
|
||||
|
||||
if dev_type == 'vbd':
|
||||
if dev_info.get('dev', '').startswith('ioemu:'):
|
||||
@@ -989,7 +1001,7 @@ class XendConfig(dict):
|
||||
self['vbd_refs'].append(dev_uuid)
|
||||
return dev_uuid
|
||||
|
||||
elif dev_type in ('vtpm'):
|
||||
elif dev_type == 'vtpm':
|
||||
if cfg_xenapi.get('type'):
|
||||
dev_info['type'] = cfg_xenapi.get('type')
|
||||
|
||||
@@ -1012,11 +1024,12 @@ class XendConfig(dict):
|
||||
dev_type = sxp.name(config)
|
||||
dev_info = {}
|
||||
|
||||
try:
|
||||
for opt, val in config[1:]:
|
||||
self['devices'][opt] = val
|
||||
except ValueError:
|
||||
pass # SXP has no options for this device
|
||||
for opt_val in config[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
self['devices'][dev_uuid][opt] = val
|
||||
except (TypeError, ValueError):
|
||||
pass # no value for this config option
|
||||
|
||||
return True
|
||||
|
||||
@@ -1095,23 +1108,34 @@ class XendConfig(dict):
|
||||
|
||||
return image
|
||||
|
||||
def update_with_image_sxp(self, image_sxp):
|
||||
def update_with_image_sxp(self, image_sxp, bootloader = False):
|
||||
# Convert Legacy "image" config to Xen API PV_*
|
||||
# configuration
|
||||
log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
|
||||
|
||||
self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
|
||||
self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
|
||||
kernel_args = sxp.child_value(image_sxp, 'args', '')
|
||||
|
||||
kernel_args = ""
|
||||
|
||||
# attempt to extract extra arguments from SXP config
|
||||
arg_ip = sxp.child_value(image_sxp, 'ip')
|
||||
if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
|
||||
kernel_args += ' ip=%s' % arg_ip
|
||||
kernel_args += 'ip=%s ' % arg_ip
|
||||
arg_root = sxp.child_value(image_sxp, 'root')
|
||||
if arg_root and not re.search(r'root=', kernel_args):
|
||||
kernel_args += ' root=%s' % arg_root
|
||||
self['PV_args'] = kernel_args
|
||||
kernel_args += 'root=%s ' % arg_root
|
||||
|
||||
# user-specified args must come last: previous releases did this and
|
||||
# some domU kernels rely upon the ordering.
|
||||
kernel_args += sxp.child_value(image_sxp, 'args', '')
|
||||
|
||||
if bootloader:
|
||||
self['_temp_using_bootloader'] = '1'
|
||||
self['_temp_kernel'] = sxp.child_value(image_sxp, 'kernel','')
|
||||
self['_temp_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
|
||||
self['_temp_args'] = kernel_args
|
||||
else:
|
||||
self['PV_kernel'] = sxp.child_value(image_sxp, 'kernel','')
|
||||
self['PV_ramdisk'] = sxp.child_value(image_sxp, 'ramdisk','')
|
||||
self['PV_args'] = kernel_args
|
||||
|
||||
# Store image SXP in python dictionary format
|
||||
image = {}
|
||||
|
||||
@@ -80,6 +80,7 @@ ZOMBIE_PREFIX = 'Zombie-'
|
||||
MINIMUM_RESTART_TIME = 20
|
||||
|
||||
RESTART_IN_PROGRESS = 'xend/restart_in_progress'
|
||||
LAST_SHUTDOWN_REASON = 'xend/last_shutdown_reason'
|
||||
|
||||
#
|
||||
# Device migration stages (eg. XendDomainInfo, XendCheckpoint, server.tpmif)
|
||||
|
||||
@@ -36,6 +36,7 @@ from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
|
||||
from xen.xend.PrettyPrint import prettyprint
|
||||
from xen.xend.XendConfig import XendConfig
|
||||
from xen.xend.XendError import XendError, XendInvalidDomain, VmError
|
||||
from xen.xend.XendError import VMBadState
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendAPIConstants import XEN_API_VM_POWER_STATE
|
||||
from xen.xend.XendConstants import XS_VMROOT
|
||||
@@ -376,7 +377,7 @@ class XendDomain:
|
||||
dom0.setVCpuCount(target)
|
||||
|
||||
|
||||
def _refresh(self):
|
||||
def _refresh(self, refresh_shutdown = True):
|
||||
"""Refresh the domain list. Needs to be called when
|
||||
either xenstore has changed or when a method requires
|
||||
up to date information (like uptime, cputime stats).
|
||||
@@ -392,7 +393,7 @@ class XendDomain:
|
||||
for dom in running:
|
||||
domid = dom['domid']
|
||||
if domid in self.domains:
|
||||
self.domains[domid].update(dom)
|
||||
self.domains[domid].update(dom, refresh_shutdown)
|
||||
elif domid not in self.domains and dom['dying'] != 1:
|
||||
try:
|
||||
new_dom = XendDomainInfo.recreate(dom, False)
|
||||
@@ -494,7 +495,7 @@ class XendDomain:
|
||||
"""
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self._refresh()
|
||||
self._refresh(refresh_shutdown = False)
|
||||
dom = self.domain_lookup_nr(domid)
|
||||
if not dom:
|
||||
raise XendError("No domain named '%s'." % str(domid))
|
||||
@@ -604,6 +605,16 @@ class XendDomain:
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
def get_all_vms(self):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
result = self.domains.values()
|
||||
result += [x for x in self.managed_domains.values() if
|
||||
x not in result]
|
||||
return result
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
def get_vm_by_uuid(self, vm_uuid):
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
@@ -720,7 +731,7 @@ class XendDomain:
|
||||
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self._refresh()
|
||||
self._refresh(refresh_shutdown = False)
|
||||
|
||||
# active domains
|
||||
active_domains = self.domains.values()
|
||||
@@ -782,7 +793,9 @@ class XendDomain:
|
||||
raise XendError("Cannot save privileged domain %s" % domname)
|
||||
|
||||
if dominfo.state != DOM_STATE_RUNNING:
|
||||
raise XendError("Cannot suspend domain that is not running.")
|
||||
raise VMBadState("Domain is not running",
|
||||
POWER_STATE_NAMES[DOM_STATE_RUNNING],
|
||||
POWER_STATE_NAMES[dominfo.state])
|
||||
|
||||
dom_uuid = dominfo.get_uuid()
|
||||
|
||||
@@ -869,6 +882,26 @@ class XendDomain:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_create_from_dict(self, config_dict):
|
||||
"""Create a domain from a configuration dictionary.
|
||||
|
||||
@param config_dict: configuration
|
||||
@rtype: XendDomainInfo
|
||||
"""
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self._refresh()
|
||||
|
||||
dominfo = XendDomainInfo.create_from_dict(config_dict)
|
||||
self._add_domain(dominfo)
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
|
||||
|
||||
def domain_new(self, config):
|
||||
"""Create a domain from a configuration but do not start it.
|
||||
|
||||
@@ -912,7 +945,9 @@ class XendDomain:
|
||||
raise XendInvalidDomain(str(domid))
|
||||
|
||||
if dominfo.state != DOM_STATE_HALTED:
|
||||
raise XendError("Domain is already running")
|
||||
raise VMBadState("Domain is already running",
|
||||
POWER_STATE_NAMES[DOM_STATE_HALTED],
|
||||
POWER_STATE_NAMES[dominfo.state])
|
||||
|
||||
dominfo.start(is_managed = True)
|
||||
self._add_domain(dominfo)
|
||||
@@ -940,7 +975,9 @@ class XendDomain:
|
||||
raise XendInvalidDomain(str(domid))
|
||||
|
||||
if dominfo.state != DOM_STATE_HALTED:
|
||||
raise XendError("Domain is still running")
|
||||
raise VMBadState("Domain is still running",
|
||||
POWER_STATE_NAMES[DOM_STATE_HALTED],
|
||||
POWER_STATE_NAMES[dominfo.state])
|
||||
|
||||
log.info("Domain %s (%s) deleted." %
|
||||
(dominfo.getName(), dominfo.info.get('uuid')))
|
||||
|
||||
@@ -37,7 +37,7 @@ from xen.util import asserts
|
||||
from xen.util.blkif import blkdev_uname_to_file
|
||||
from xen.util import security
|
||||
|
||||
from xen.xend import balloon, sxp, uuid, image, arch
|
||||
from xen.xend import balloon, sxp, uuid, image, arch, osdep
|
||||
from xen.xend import XendRoot, XendNode, XendConfig
|
||||
|
||||
from xen.xend.XendConfig import scrub_password
|
||||
@@ -81,13 +81,12 @@ log = logging.getLogger("xend.XendDomainInfo")
|
||||
|
||||
def create(config):
|
||||
"""Creates and start a VM using the supplied configuration.
|
||||
(called from XMLRPCServer directly)
|
||||
|
||||
@param config: A configuration object involving lists of tuples.
|
||||
@type config: list of lists, eg ['vm', ['image', 'xen.gz']]
|
||||
|
||||
@rtype: XendDomainInfo
|
||||
@return: A up and running XendDomainInfo instance
|
||||
@return: An up and running XendDomainInfo instance
|
||||
@raise VmError: Invalid configuration or failure to start.
|
||||
"""
|
||||
|
||||
@@ -102,6 +101,28 @@ def create(config):
|
||||
|
||||
return vm
|
||||
|
||||
def create_from_dict(config_dict):
|
||||
"""Creates and start a VM using the supplied configuration.
|
||||
|
||||
@param config_dict: An configuration dictionary.
|
||||
|
||||
@rtype: XendDomainInfo
|
||||
@return: An up and running XendDomainInfo instance
|
||||
@raise VmError: Invalid configuration or failure to start.
|
||||
"""
|
||||
|
||||
log.debug("XendDomainInfo.create_from_dict(%s)",
|
||||
scrub_password(config_dict))
|
||||
vm = XendDomainInfo(XendConfig.XendConfig(xapi = config_dict))
|
||||
try:
|
||||
vm.start()
|
||||
except:
|
||||
log.exception('Domain construction failed')
|
||||
vm.destroy()
|
||||
raise
|
||||
|
||||
return vm
|
||||
|
||||
def recreate(info, priv):
|
||||
"""Create the VM object for an existing domain. The domain must not
|
||||
be dying, as the paths in the store should already have been removed,
|
||||
@@ -418,7 +439,7 @@ class XendDomainInfo:
|
||||
|
||||
def shutdown(self, reason):
|
||||
"""Shutdown a domain by signalling this via xenstored."""
|
||||
log.debug('XendDomainInfo.shutdown')
|
||||
log.debug('XendDomainInfo.shutdown(%s)', reason)
|
||||
if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
|
||||
raise XendError('Domain cannot be shutdown')
|
||||
|
||||
@@ -490,7 +511,7 @@ class XendDomainInfo:
|
||||
# look up uuid of the device
|
||||
dev_control = self.getDeviceController(deviceClass)
|
||||
dev_sxpr = dev_control.sxpr(devid)
|
||||
dev_uuid = sxp.child_value(sxpr, 'uuid')
|
||||
dev_uuid = sxp.child_value(dev_sxpr, 'uuid')
|
||||
if not dev_uuid:
|
||||
return False
|
||||
|
||||
@@ -893,7 +914,7 @@ class XendDomainInfo:
|
||||
return self.info.get('cpu_cap', 0)
|
||||
|
||||
def getWeight(self):
|
||||
return self.info['cpu_weight']
|
||||
return self.info.get('cpu_weight', 256)
|
||||
|
||||
def setResume(self, state):
|
||||
self._resume = state
|
||||
@@ -948,9 +969,15 @@ class XendDomainInfo:
|
||||
|
||||
log.warn('Domain has crashed: name=%s id=%d.',
|
||||
self.info['name_label'], self.domid)
|
||||
self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
|
||||
|
||||
if xroot.get_enable_dump():
|
||||
self.dumpCore()
|
||||
try:
|
||||
self.dumpCore()
|
||||
except XendError:
|
||||
# This error has been logged -- there's nothing more
|
||||
# we can do in this context.
|
||||
pass
|
||||
|
||||
restart_reason = 'crash'
|
||||
self._stateSet(DOM_STATE_HALTED)
|
||||
@@ -967,6 +994,7 @@ class XendDomainInfo:
|
||||
|
||||
log.info('Domain has shutdown: name=%s id=%d reason=%s.',
|
||||
self.info['name_label'], self.domid, reason)
|
||||
self._writeVm(LAST_SHUTDOWN_REASON, reason)
|
||||
|
||||
self._clearRestart()
|
||||
|
||||
@@ -1051,12 +1079,6 @@ class XendDomainInfo:
|
||||
"""
|
||||
from xen.xend import XendDomain
|
||||
|
||||
config = self.sxpr()
|
||||
|
||||
if self._infoIsSet('cpus') and len(self.info['cpus']) != 0:
|
||||
config.append(['cpus', reduce(lambda x, y: str(x) + "," + str(y),
|
||||
self.info['cpus'])])
|
||||
|
||||
if self._readVm(RESTART_IN_PROGRESS):
|
||||
log.error('Xend failed during restart of domain %s. '
|
||||
'Refusing to restart to avoid loops.',
|
||||
@@ -1097,7 +1119,8 @@ class XendDomainInfo:
|
||||
|
||||
new_dom = None
|
||||
try:
|
||||
new_dom = XendDomain.instance().domain_create(config)
|
||||
new_dom = XendDomain.instance().domain_create_from_dict(
|
||||
self.info)
|
||||
new_dom.unpause()
|
||||
rst_cnt = self._readVm('xend/restart_count')
|
||||
rst_cnt = int(rst_cnt) + 1
|
||||
@@ -1146,7 +1169,10 @@ class XendDomainInfo:
|
||||
#
|
||||
|
||||
def dumpCore(self, corefile = None):
|
||||
"""Create a core dump for this domain. Nothrow guarantee."""
|
||||
"""Create a core dump for this domain.
|
||||
|
||||
@raise: XendError if core dumping failed.
|
||||
"""
|
||||
|
||||
try:
|
||||
if not corefile:
|
||||
@@ -1539,6 +1565,8 @@ class XendDomainInfo:
|
||||
if boot:
|
||||
# HVM booting.
|
||||
self.info['image']['type'] = 'hvm'
|
||||
if not 'devices' in self.info['image']:
|
||||
self.info['image']['devices'] = {}
|
||||
self.info['image']['devices']['boot'] = boot
|
||||
elif not blexec and kernel:
|
||||
# Boot from dom0. Nothing left to do -- the kernel and ramdisk
|
||||
@@ -1547,7 +1575,7 @@ class XendDomainInfo:
|
||||
else:
|
||||
# Boot using bootloader
|
||||
if not blexec or blexec == 'pygrub':
|
||||
blexec = '/usr/bin/pygrub'
|
||||
blexec = osdep.pygrub_path
|
||||
|
||||
blcfg = None
|
||||
for (devtype, devinfo) in self.info.all_devices_sxpr():
|
||||
@@ -1598,7 +1626,7 @@ class XendDomainInfo:
|
||||
log.error(msg)
|
||||
raise VmError(msg)
|
||||
|
||||
self.info.update_with_image_sxp(blcfg)
|
||||
self.info.update_with_image_sxp(blcfg, True)
|
||||
|
||||
|
||||
#
|
||||
@@ -1831,8 +1859,7 @@ class XendDomainInfo:
|
||||
return self.info.get('memory_dynamic_max', 0)
|
||||
def get_memory_dynamic_min(self):
|
||||
return self.info.get('memory_dynamic_min', 0)
|
||||
|
||||
|
||||
|
||||
def get_vcpus_policy(self):
|
||||
sched_id = xc.sched_id_get()
|
||||
if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
|
||||
@@ -1847,8 +1874,6 @@ class XendDomainInfo:
|
||||
return XEN_API_VM_POWER_STATE[self.state]
|
||||
def get_platform_std_vga(self):
|
||||
return self.info.get('platform_std_vga', False)
|
||||
def get_platform_keymap(self):
|
||||
return ''
|
||||
def get_platform_serial(self):
|
||||
return self.info.get('platform_serial', '')
|
||||
def get_platform_localtime(self):
|
||||
@@ -1952,8 +1977,19 @@ class XendDomainInfo:
|
||||
config['device'] = 'eth%d' % devid
|
||||
else:
|
||||
config['device'] = ''
|
||||
|
||||
config['network'] = '' # Invalid for Xend
|
||||
|
||||
if not config.has_key('network'):
|
||||
try:
|
||||
config['network'] = \
|
||||
XendNode.instance().bridge_to_network(
|
||||
config.get('bridge')).uuid
|
||||
except Exception:
|
||||
log.exception('bridge_to_network')
|
||||
# Ignore this for now -- it may happen if the device
|
||||
# has been specified using the legacy methods, but at
|
||||
# some point we're going to have to figure out how to
|
||||
# handle that properly.
|
||||
|
||||
config['MTU'] = 1500 # TODO
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
@@ -1965,7 +2001,7 @@ class XendDomainInfo:
|
||||
config['image'] = config.get('uname', '')
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
if config['mode'] == 'r':
|
||||
if config.get('mode', 'r') == 'r':
|
||||
config['mode'] = 'RO'
|
||||
else:
|
||||
config['mode'] = 'RW'
|
||||
|
||||
@@ -32,6 +32,17 @@ class XendError(Fault):
|
||||
def __str__(self):
|
||||
return self.value
|
||||
|
||||
class VMBadState(XendError):
|
||||
def __init__(self, value, expected, actual):
|
||||
XendError.__init__(self, value)
|
||||
self.expected = expected
|
||||
self.actual = actual
|
||||
|
||||
class NetworkAlreadyConnected(XendError):
|
||||
def __init__(self, pif_uuid):
|
||||
XendError.__init__(self, 'Network already connected')
|
||||
self.pif_uuid = pif_uuid
|
||||
|
||||
class VmError(XendError):
|
||||
"""Vm construction error."""
|
||||
pass
|
||||
|
||||
@@ -16,7 +16,10 @@
|
||||
# Copyright (C) 2005, 2006 XenSource Ltd.
|
||||
#============================================================================
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import stat
|
||||
import tempfile
|
||||
import types
|
||||
@@ -38,6 +41,24 @@ if 'TRACE' not in logging.__dict__:
|
||||
self.log(logging.TRACE, *args, **kwargs)
|
||||
logging.Logger.trace = trace
|
||||
|
||||
def findCaller(self):
|
||||
"""
|
||||
Override logging.Logger.findCaller so that the above trace function
|
||||
does not appear as the source of log messages. The signature of this
|
||||
function changed between Python 2.3 and 2.4.
|
||||
"""
|
||||
frames = inspect.stack()
|
||||
thisfile = os.path.normcase(frames[0][1])
|
||||
for frame in frames:
|
||||
filename = os.path.normcase(frame[1])
|
||||
if filename != thisfile and filename != logging._srcfile:
|
||||
major, minor, _, _, _ = sys.version_info
|
||||
if major == 2 and minor >= 4:
|
||||
return filename, frame[2], frame[3]
|
||||
else:
|
||||
return filename, frame[2]
|
||||
logging.Logger.findCaller = findCaller
|
||||
|
||||
|
||||
log = logging.getLogger("xend")
|
||||
|
||||
@@ -46,7 +67,7 @@ MAX_BYTES = 1 << 20 # 1MB
|
||||
BACKUP_COUNT = 5
|
||||
|
||||
STDERR_FORMAT = "[%(name)s] %(levelname)s (%(module)s:%(lineno)d) %(message)s"
|
||||
LOGFILE_FORMAT = "[%(asctime)s %(name)s %(process)d] %(levelname)s (%(module)s:%(lineno)d) %(message)s"
|
||||
LOGFILE_FORMAT = "[%(asctime)s %(process)d] %(levelname)s (%(module)s:%(lineno)d) %(message)s"
|
||||
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (c) 2006 Xensource Inc.
|
||||
#============================================================================
|
||||
|
||||
import os
|
||||
import commands
|
||||
import re
|
||||
import struct
|
||||
import socket
|
||||
|
||||
import XendDomain
|
||||
import XendNode
|
||||
from XendLogging import log
|
||||
|
||||
IP_ROUTE_RE = r'^default via ([\d\.]+) dev (\w+)'
|
||||
|
||||
def linux_get_default_network():
|
||||
"""Returns the network details of the host."""
|
||||
|
||||
ip_cmd = '/sbin/ip route'
|
||||
rc, output = commands.getstatusoutput(ip_cmd)
|
||||
default_route = None
|
||||
default_dev = None
|
||||
default_netmask = None
|
||||
if rc == 0:
|
||||
# find default route/device
|
||||
for line in output.split('\n'):
|
||||
is_default = re.search(IP_ROUTE_RE, line)
|
||||
if is_default:
|
||||
default_route = is_default.group(1)
|
||||
default_dev = is_default.group(2)
|
||||
|
||||
# find network address and network mask
|
||||
if default_dev:
|
||||
dev_re = r'^([\d\.]+)/(\d+) dev %s' % default_dev
|
||||
for line in output.split('\n'):
|
||||
is_dev = re.search(dev_re, line)
|
||||
if is_dev:
|
||||
# convert integer netmask to string representation
|
||||
netmask = 0xffffffff ^ (2**(32-int(is_dev.group(2))) - 1)
|
||||
packed = struct.pack('!I', netmask)
|
||||
default_netmask = socket.inet_ntoa(packed)
|
||||
|
||||
return (default_route, default_netmask)
|
||||
|
||||
|
||||
class XendNetwork:
|
||||
def __init__(self, uuid, name, description, gateway, netmask):
|
||||
self.uuid = uuid
|
||||
self.name_label = name
|
||||
self.name_description = description
|
||||
self.default_gateway = gateway
|
||||
self.default_netmask = netmask
|
||||
|
||||
def set_name_label(self, new_name):
|
||||
self.name_label = new_name
|
||||
XendNode.instance().save_networks()
|
||||
|
||||
def set_name_description(self, new_desc):
|
||||
self.name_description = new_desc
|
||||
XendNode.instance().save_networks()
|
||||
|
||||
def set_default_gateway(self, new_gateway):
|
||||
if re.search('^\d+\.\d+\.\d+\.\d+$', new_gateway):
|
||||
self.default_gateway = new_gateway
|
||||
XendNode.instance().save_networks()
|
||||
|
||||
def set_default_netmask(self, new_netmask):
|
||||
if re.search('^\d+\.\d+\.\d+\.\d+$', new_netmask):
|
||||
self.default_netmask = new_netmask
|
||||
XendNode.instance().save_networks()
|
||||
|
||||
def get_VIF_UUIDs(self):
|
||||
result = []
|
||||
vms = XendDomain.instance().get_all_vms()
|
||||
for vm in vms:
|
||||
vifs = vm.get_vifs()
|
||||
for vif in vifs:
|
||||
vif_cfg = vm.get_dev_xenapi_config('vif', vif)
|
||||
if vif_cfg.get('network') == self.uuid:
|
||||
result.append(vif)
|
||||
return result
|
||||
|
||||
def get_PIF_UUIDs(self):
|
||||
return [x.uuid for x in XendNode.instance().pifs.values()
|
||||
if x.network == self]
|
||||
|
||||
def get_record(self, transient = True):
|
||||
result = {
|
||||
'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'default_gateway': self.default_gateway,
|
||||
'default_netmask': self.default_netmask,
|
||||
}
|
||||
if transient:
|
||||
result['VIFs'] = self.get_VIF_UUIDs()
|
||||
result['PIFs'] = self.get_PIF_UUIDs()
|
||||
return result
|
||||
@@ -19,34 +19,181 @@
|
||||
import os
|
||||
import socket
|
||||
import xen.lowlevel.xc
|
||||
|
||||
from xen.util import Brctl
|
||||
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendError import XendError, NetworkAlreadyConnected
|
||||
from xen.xend.XendRoot import instance as xendroot
|
||||
from xen.xend.XendStorageRepository import XendStorageRepository
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendPIF import *
|
||||
from xen.xend.XendNetwork import *
|
||||
from xen.xend.XendStateStore import XendStateStore
|
||||
|
||||
class XendNode:
|
||||
"""XendNode - Represents a Domain 0 Host."""
|
||||
|
||||
def __init__(self):
|
||||
self.xc = xen.lowlevel.xc.xc()
|
||||
self.uuid = uuid.createString()
|
||||
self.cpus = {}
|
||||
self.name = socket.gethostname()
|
||||
self.desc = ""
|
||||
self.sr = XendStorageRepository()
|
||||
"""Initalises the state of all host specific objects such as
|
||||
|
||||
* Host
|
||||
* Host_CPU
|
||||
* PIF
|
||||
* Network
|
||||
* Storage Repository
|
||||
"""
|
||||
|
||||
self.xc = xen.lowlevel.xc.xc()
|
||||
self.state_store = XendStateStore(xendroot().get_xend_state_path())
|
||||
|
||||
# load host state from XML file
|
||||
saved_host = self.state_store.load_state('host')
|
||||
if saved_host and len(saved_host.keys()) == 1:
|
||||
self.uuid = saved_host.keys()[0]
|
||||
host = saved_host[self.uuid]
|
||||
self.name = host.get('name_label', socket.gethostname())
|
||||
self.desc = host.get('name_description', '')
|
||||
self.cpus = {}
|
||||
else:
|
||||
self.uuid = uuid.createString()
|
||||
self.name = socket.gethostname()
|
||||
self.desc = ''
|
||||
self.cpus = {}
|
||||
|
||||
# load CPU UUIDs
|
||||
saved_cpus = self.state_store.load_state('cpu')
|
||||
for cpu_uuid, cpu in saved_cpus.items():
|
||||
self.cpus[cpu_uuid] = cpu
|
||||
|
||||
# verify we have enough cpus here
|
||||
physinfo = self.physinfo_dict()
|
||||
cpu_count = physinfo['nr_cpus']
|
||||
cpu_features = physinfo['hw_caps']
|
||||
|
||||
for i in range(cpu_count):
|
||||
# construct uuid by appending extra bit on the host.
|
||||
# since CPUs belong to a host.
|
||||
cpu_uuid = self.uuid + '-%04d' % i
|
||||
cpu_info = {'uuid': cpu_uuid,
|
||||
'host': self.uuid,
|
||||
'number': i,
|
||||
'features': cpu_features}
|
||||
self.cpus[cpu_uuid] = cpu_info
|
||||
|
||||
# If the number of CPUs don't match, we should just reinitialise
|
||||
# the CPU UUIDs.
|
||||
if cpu_count != len(self.cpus):
|
||||
self.cpus = {}
|
||||
for i in range(cpu_count):
|
||||
cpu_uuid = uuid.createString()
|
||||
cpu_info = {'uuid': cpu_uuid,
|
||||
'host': self.uuid,
|
||||
'number': i,
|
||||
'features': cpu_features}
|
||||
self.cpus[cpu_uuid] = cpu_info
|
||||
|
||||
self.pifs = {}
|
||||
self.networks = {}
|
||||
|
||||
# initialise networks
|
||||
saved_networks = self.state_store.load_state('network')
|
||||
if saved_networks:
|
||||
for net_uuid, network in saved_networks.items():
|
||||
self.network_create(network.get('name_label'),
|
||||
network.get('name_description', ''),
|
||||
network.get('default_gateway', ''),
|
||||
network.get('default_netmask', ''),
|
||||
False, net_uuid)
|
||||
else:
|
||||
gateway, netmask = linux_get_default_network()
|
||||
self.network_create('net0', '', gateway, netmask, False)
|
||||
|
||||
# initialise PIFs
|
||||
saved_pifs = self.state_store.load_state('pif')
|
||||
if saved_pifs:
|
||||
for pif_uuid, pif in saved_pifs.items():
|
||||
if pif.get('network') in self.networks:
|
||||
network = self.networks[pif['network']]
|
||||
try:
|
||||
self.PIF_create(pif['name'], pif['MTU'], pif['VLAN'],
|
||||
pif['MAC'], network, False, pif_uuid)
|
||||
except NetworkAlreadyConnected, exn:
|
||||
log.error('Cannot load saved PIF %s, as network %s ' +
|
||||
'is already connected to PIF %s',
|
||||
pif_uuid, pif['network'], exn.pif_uuid)
|
||||
else:
|
||||
for name, mtu, mac in linux_get_phy_ifaces():
|
||||
network = self.networks.values()[0]
|
||||
self.PIF_create(name, mtu, '', mac, network, False)
|
||||
|
||||
# initialise storage
|
||||
saved_sr = self.state_store.load_state('sr')
|
||||
if saved_sr and len(saved_sr) == 1:
|
||||
sr_uuid = saved_sr.keys()[0]
|
||||
self.sr = XendStorageRepository(sr_uuid)
|
||||
else:
|
||||
sr_uuid = uuid.createString()
|
||||
self.sr = XendStorageRepository(sr_uuid)
|
||||
|
||||
|
||||
def network_create(self, name_label, name_description,
|
||||
default_gateway, default_netmask, persist = True,
|
||||
net_uuid = None):
|
||||
if net_uuid is None:
|
||||
net_uuid = uuid.createString()
|
||||
self.networks[net_uuid] = XendNetwork(net_uuid, name_label,
|
||||
name_description,
|
||||
default_gateway,
|
||||
default_netmask)
|
||||
if persist:
|
||||
self.save_networks()
|
||||
return net_uuid
|
||||
|
||||
|
||||
def network_destroy(self, net_uuid):
|
||||
del self.networks[net_uuid]
|
||||
self.save_networks()
|
||||
|
||||
|
||||
def PIF_create(self, name, mtu, vlan, mac, network, persist = True,
|
||||
pif_uuid = None):
|
||||
for pif in self.pifs.values():
|
||||
if pif.network == network:
|
||||
raise NetworkAlreadyConnected(pif.uuid)
|
||||
|
||||
if pif_uuid is None:
|
||||
pif_uuid = uuid.createString()
|
||||
self.pifs[pif_uuid] = XendPIF(pif_uuid, name, mtu, vlan, mac, network,
|
||||
self)
|
||||
if persist:
|
||||
self.save_PIFs()
|
||||
self.refreshBridges()
|
||||
return pif_uuid
|
||||
|
||||
|
||||
def PIF_create_VLAN(self, pif_uuid, network_uuid, vlan):
|
||||
pif = self.pifs[pif_uuid]
|
||||
network = self.networks[network_uuid]
|
||||
return self.PIF_create(pif.name, pif.mtu, vlan, pif.mac, network)
|
||||
|
||||
|
||||
def PIF_destroy(self, pif_uuid):
|
||||
del self.pifs[pif_uuid]
|
||||
self.save_PIFs()
|
||||
|
||||
|
||||
def save(self):
|
||||
# save state
|
||||
host_record = {self.uuid: {'name_label':self.name,
|
||||
'name_description':self.desc}}
|
||||
self.state_store.save_state('host',host_record)
|
||||
self.state_store.save_state('cpu', self.cpus)
|
||||
self.save_PIFs()
|
||||
self.save_networks()
|
||||
|
||||
sr_record = {self.sr.uuid: self.sr.get_record()}
|
||||
self.state_store.save_state('sr', sr_record)
|
||||
|
||||
def save_PIFs(self):
|
||||
pif_records = dict([(k, v.get_record(transient = False))
|
||||
for k, v in self.pifs.items()])
|
||||
self.state_store.save_state('pif', pif_records)
|
||||
|
||||
def save_networks(self):
|
||||
net_records = dict([(k, v.get_record(transient = False))
|
||||
for k, v in self.networks.items()])
|
||||
self.state_store.save_state('network', net_records)
|
||||
|
||||
def shutdown(self):
|
||||
return 0
|
||||
@@ -56,7 +203,8 @@ class XendNode:
|
||||
|
||||
def notify(self, _):
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Ref validation
|
||||
#
|
||||
@@ -67,6 +215,9 @@ class XendNode:
|
||||
def is_valid_cpu(self, cpu_ref):
|
||||
return (cpu_ref in self.cpus)
|
||||
|
||||
def is_valid_network(self, network_ref):
|
||||
return (network_ref in self.networks)
|
||||
|
||||
#
|
||||
# Storage Repo
|
||||
#
|
||||
@@ -100,6 +251,9 @@ class XendNode:
|
||||
def set_description(self, new_desc):
|
||||
self.desc = new_desc
|
||||
|
||||
def get_uuid(self):
|
||||
return self.uuid
|
||||
|
||||
#
|
||||
# Host CPU Functions
|
||||
#
|
||||
@@ -133,7 +287,42 @@ class XendNode:
|
||||
def get_host_cpu_load(self, host_cpu_ref):
|
||||
return 0.0
|
||||
|
||||
|
||||
#
|
||||
# Network Functions
|
||||
#
|
||||
|
||||
def get_network_refs(self):
|
||||
return self.networks.keys()
|
||||
|
||||
def get_network(self, network_ref):
|
||||
return self.networks[network_ref]
|
||||
|
||||
def bridge_to_network(self, bridge):
|
||||
"""
|
||||
Determine which network a particular bridge is attached to.
|
||||
|
||||
@param bridge The name of the bridge. If empty, the default bridge
|
||||
will be used instead (the first one in the list returned by brctl
|
||||
show); this is the behaviour of the vif-bridge script.
|
||||
@return The XendNetwork instance to which this bridge is attached.
|
||||
@raise Exception if the interface is not connected to a network.
|
||||
"""
|
||||
if not bridge:
|
||||
rc, bridge = commands.getstatusoutput(
|
||||
'brctl show | cut -d "\n" -f 2 | cut -f 1')
|
||||
if rc != 0 or not bridge:
|
||||
raise Exception(
|
||||
'Could not find default bridge, and none was specified')
|
||||
|
||||
bridges = Brctl.get_state()
|
||||
if bridge not in bridges:
|
||||
raise Exception('Bridge %s is not up' % bridge)
|
||||
for pif in self.pifs.values():
|
||||
if pif.interface_name() in bridges[bridge]:
|
||||
return pif.network
|
||||
raise Exception('Bridge %s is not connected to a network' % bridge)
|
||||
|
||||
|
||||
#
|
||||
# Getting host information.
|
||||
@@ -210,7 +399,12 @@ class XendNode:
|
||||
return dict(self.physinfo())
|
||||
def info_dict(self):
|
||||
return dict(self.info())
|
||||
|
||||
|
||||
|
||||
def refreshBridges(self):
|
||||
for pif in self.pifs.values():
|
||||
pif.refresh(Brctl.get_state())
|
||||
|
||||
|
||||
def instance():
|
||||
global inst
|
||||
@@ -218,5 +412,5 @@ def instance():
|
||||
inst
|
||||
except:
|
||||
inst = XendNode()
|
||||
inst.save()
|
||||
return inst
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (c) 2006 Xensource Inc.
|
||||
#============================================================================
|
||||
|
||||
import commands
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
|
||||
|
||||
log = logging.getLogger("xend.XendPIF")
|
||||
log.setLevel(logging.TRACE)
|
||||
|
||||
|
||||
MAC_RE = re.compile(':'.join(['[0-9a-f]{2}'] * 6))
|
||||
IP_IFACE_RE = re.compile(r'^\d+: (\w+):.*mtu (\d+) .* link/\w+ ([0-9a-f:]+)')
|
||||
|
||||
def linux_phy_to_virt(pif_name):
|
||||
return 'eth' + re.sub(r'^[a-z]+', '', pif_name)
|
||||
|
||||
def linux_get_phy_ifaces():
|
||||
"""Returns a list of physical interfaces.
|
||||
|
||||
Identifies PIFs as those that have a interface name starting with 'p'
|
||||
and have the fake 'fe:ff:ff:ff:ff:ff' MAC address.
|
||||
|
||||
See /etc/xen/scripts/network-bridge for how the devices are renamed.
|
||||
|
||||
@rtype: array of 3-element tuple (name, mtu, mac)
|
||||
"""
|
||||
|
||||
ip_cmd = 'ip -o link show'
|
||||
rc, output = commands.getstatusoutput(ip_cmd)
|
||||
ifaces = {}
|
||||
phy_ifaces = []
|
||||
if rc == 0:
|
||||
# parse all interfaces into (name, mtu, mac)
|
||||
for line in output.split('\n'):
|
||||
has_if = re.search(IP_IFACE_RE, line)
|
||||
if has_if:
|
||||
ifaces[has_if.group(1)] = has_if.groups()
|
||||
|
||||
# resolve pifs' mac addresses
|
||||
for name, mtu, mac in ifaces.values():
|
||||
if name[0] == 'p' and mac == 'fe:ff:ff:ff:ff:ff':
|
||||
bridged_ifname = linux_phy_to_virt(name)
|
||||
bridged_if = ifaces.get(bridged_ifname)
|
||||
if bridged_if:
|
||||
bridged_mac = bridged_if[2]
|
||||
phy_ifaces.append((name, int(mtu), bridged_mac))
|
||||
|
||||
return phy_ifaces
|
||||
|
||||
def linux_set_mac(iface, mac):
|
||||
if not re.search(MAC_RE, mac):
|
||||
return False
|
||||
|
||||
ip_mac_cmd = 'ip link set %s addr %s' % \
|
||||
(linux_phy_to_virt(iface), mac)
|
||||
rc, output = commands.getstatusoutput(ip_mac_cmd)
|
||||
if rc == 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def linux_set_mtu(iface, mtu):
|
||||
try:
|
||||
ip_mtu_cmd = 'ip link set %s mtu %d' % \
|
||||
(linux_phy_to_virt(iface), int(mtu))
|
||||
rc, output = commands.getstatusoutput(ip_mtu_cmd)
|
||||
if rc == 0:
|
||||
return True
|
||||
return False
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
class XendPIF:
|
||||
"""Representation of a Physical Network Interface."""
|
||||
|
||||
def __init__(self, uuid, name, mtu, vlan, mac, network, host):
|
||||
self.uuid = uuid
|
||||
self.name = name
|
||||
self.mac = mac
|
||||
self.mtu = mtu
|
||||
self.vlan = vlan
|
||||
self.network = network
|
||||
self.host = host
|
||||
|
||||
def set_name(self, new_name):
|
||||
self.name = new_name
|
||||
|
||||
def set_mac(self, new_mac):
|
||||
success = linux_set_mac(new_mac)
|
||||
if success:
|
||||
self.mac = new_mac
|
||||
return success
|
||||
|
||||
def set_mtu(self, new_mtu):
|
||||
success = linux_set_mtu(new_mtu)
|
||||
if success:
|
||||
self.mtu = new_mtu
|
||||
return success
|
||||
|
||||
def get_io_read_kbs(self):
|
||||
return 0.0
|
||||
|
||||
def get_io_write_kbs(self):
|
||||
return 0.0
|
||||
|
||||
def get_record(self, transient = True):
|
||||
result = {'name': self.name,
|
||||
'MAC': self.mac,
|
||||
'MTU': self.mtu,
|
||||
'VLAN': self.vlan,
|
||||
'host': self.host.uuid,
|
||||
'network': self.network.uuid}
|
||||
if transient:
|
||||
result['io_read_kbs'] = self.get_io_read_kbs()
|
||||
result['io_write_kbs'] = self.get_io_write_kbs()
|
||||
return result
|
||||
|
||||
|
||||
def refresh(self, bridges):
|
||||
ifname = self.interface_name()
|
||||
rc, _ = _cmd('ip link show %s', ifname)
|
||||
if rc != 0:
|
||||
# Interface does not exist. If it's a physical interface, then
|
||||
# there's nothing we can do -- this should have been set up with
|
||||
# the network script. Otherwise, we can use vconfig to derive
|
||||
# a subinterface.
|
||||
if not self.vlan:
|
||||
return
|
||||
|
||||
rc, _ = _cmd('vconfig add %s %s', self.name, self.vlan)
|
||||
if rc != 0:
|
||||
log.error('Could not refresh %s', ifname)
|
||||
return
|
||||
log.info('Created network interface %s', ifname)
|
||||
|
||||
for brname, nics in bridges.items():
|
||||
if ifname in nics:
|
||||
log.debug('%s is already attached to %s', ifname, brname)
|
||||
return
|
||||
|
||||
# The interface is not attached to a bridge. Create one, and attach
|
||||
# the interface to it.
|
||||
brname = _new_bridge_name(bridges)
|
||||
rc, _ = _cmd('brctl addbr %s', brname)
|
||||
if rc != 0:
|
||||
log.error('Could not create bridge %s for interface %s', brname,
|
||||
ifname)
|
||||
return
|
||||
log.info('Created network bridge %s', brname)
|
||||
|
||||
rc, _ = _cmd('brctl addif %s %s', brname, ifname)
|
||||
if rc != 0:
|
||||
log.error('Could not add %s to %s', ifname, brname)
|
||||
return
|
||||
log.info('Added network interface %s to bridge %s', ifname, brname)
|
||||
|
||||
|
||||
def interface_name(self):
|
||||
if self.vlan:
|
||||
return '%s.%s' % (self.name, self.vlan)
|
||||
else:
|
||||
return self.name
|
||||
|
||||
|
||||
def _cmd(cmd, *args):
|
||||
if len(args) > 0:
|
||||
cmd = cmd % args
|
||||
rc, output = commands.getstatusoutput(cmd)
|
||||
if rc != 0:
|
||||
log.debug('%s failed with code %d' % (cmd, rc))
|
||||
log.trace('%s: %s' % (cmd, output))
|
||||
return rc, output
|
||||
|
||||
|
||||
def _new_bridge_name(bridges):
|
||||
n = 0
|
||||
while True:
|
||||
brname = 'xenbr%d' % n
|
||||
if brname not in bridges:
|
||||
return brname
|
||||
n += 1
|
||||
@@ -104,6 +104,9 @@ class XendRoot:
|
||||
"""Default session storage path."""
|
||||
xend_domains_path_default = '/var/lib/xend/domains'
|
||||
|
||||
"""Default xend management state storage."""
|
||||
xend_state_path_default = '/var/lib/xend/state'
|
||||
|
||||
components = {}
|
||||
|
||||
def __init__(self):
|
||||
@@ -263,6 +266,11 @@ class XendRoot:
|
||||
"""
|
||||
return self.get_config_value("xend-domains-path", self.xend_domains_path_default)
|
||||
|
||||
def get_xend_state_path(self):
|
||||
""" Get the path for persistent domain configuration storage
|
||||
"""
|
||||
return self.get_config_value("xend-state-path", self.xend_state_path_default)
|
||||
|
||||
def get_network_script(self):
|
||||
"""@return the script used to alter the network configuration when
|
||||
Xend starts and stops, or None if no such script is specified."""
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
#============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#============================================================================
|
||||
# Copyright (C) 2004, 2005 Mike Wray <mike.wray@hp.com>
|
||||
# Copyright (c) 2006 Xensource Inc.
|
||||
#============================================================================
|
||||
|
||||
import os
|
||||
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendLogging import log
|
||||
from xml.dom import minidom
|
||||
from xml.dom import Node
|
||||
|
||||
class XendStateStore:
|
||||
"""Manages persistent storage of Xend's internal state, mainly
|
||||
relating to API objects.
|
||||
|
||||
It stores objects atomically in the file system as flat XML files
|
||||
categorised by their 'class'.
|
||||
|
||||
For example:
|
||||
|
||||
/var/lib/xend/state/cpu.xml will contain the host cpu state
|
||||
/var/lib/xend/state/sr.xml will contain the storage repository state.
|
||||
|
||||
For the application, it will load the state via this class:
|
||||
|
||||
load_state('cpu') will return a marshalled dictionary object
|
||||
containing the cpu state.
|
||||
|
||||
save_state('cpu', dict) will save the state contained in the dictionary
|
||||
object about the 'cpu'.
|
||||
|
||||
The state is stored where each top level element has a UUID in its
|
||||
attributes. eg:
|
||||
|
||||
host['49c01812-3c28-1ad4-a59d-2a3f81b13ec2'] = {
|
||||
'name': 'norwich',
|
||||
'desc': 'Test Xen Host',
|
||||
'cpu': {'6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0': <obj>,
|
||||
'669df3b8-62be-4e61-800b-bbe8ee63a760': <obj>}
|
||||
}
|
||||
|
||||
will turn into:
|
||||
|
||||
<hosts>
|
||||
<host uuid='49c01812-3c28-1ad4-a59d-2a3f81b13ec2'>
|
||||
<name type='string'>norwich</name>
|
||||
<description type='string'>Test Xen Host</description>
|
||||
<cpu uuid='6fc2d1ed-7eb0-4c9d-8006-3657d5483ae0' />
|
||||
<cpu uuid='669df3b8-62be-4e61-800b-bbe8ee63a760' />
|
||||
</host>
|
||||
</hosts>
|
||||
|
||||
Note that it only dumps one level, so the references to CPU are
|
||||
stored in a separate file.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, base = "/var/lib/xend/state"):
|
||||
self.base = base
|
||||
if not os.path.exists(self.base):
|
||||
os.makedirs(self.base)
|
||||
|
||||
def _xml_file(self, cls):
|
||||
"""Return the absolute filename of the XML state storage file.
|
||||
|
||||
@param cls: name of the class.
|
||||
@type cls: string
|
||||
@rtype: string
|
||||
@return absolute filename of XML file to write/read from.
|
||||
"""
|
||||
return os.path.join(self.base, '%s.xml' % cls)
|
||||
|
||||
def load_state(self, cls):
|
||||
"""Load the saved state of a class from persistent XML storage.
|
||||
|
||||
References loaded from the XML will just point to an empty
|
||||
dictionary which the caller will need to replace manually.
|
||||
|
||||
@param cls: name of the class to load.
|
||||
@type cls: string
|
||||
@rtype: dict
|
||||
"""
|
||||
|
||||
xml_path = self._xml_file(cls)
|
||||
if not os.path.exists(xml_path):
|
||||
return {}
|
||||
|
||||
dom = minidom.parse(xml_path)
|
||||
root = dom.documentElement
|
||||
state = {}
|
||||
|
||||
for child in root.childNodes:
|
||||
if child.nodeType != Node.ELEMENT_NODE:
|
||||
continue # skip non element nodes
|
||||
|
||||
uuid = child.getAttribute('uuid')
|
||||
cls_dict = {}
|
||||
for val_elem in child.childNodes:
|
||||
if val_elem.nodeType != Node.ELEMENT_NODE:
|
||||
continue # skip non element nodes
|
||||
|
||||
val_name = val_elem.tagName
|
||||
val_type = val_elem.getAttribute('type').encode('utf8')
|
||||
val_uuid = val_elem.getAttribute('uuid').encode('utf8')
|
||||
val_elem.normalize()
|
||||
val_text = ''
|
||||
if val_elem.firstChild:
|
||||
val_text = val_elem.firstChild.nodeValue.strip()
|
||||
|
||||
if val_type == '' and val_uuid != '':
|
||||
# this is a reference
|
||||
if val_name not in cls_dict:
|
||||
cls_dict[val_name] = {}
|
||||
cls_dict[val_name][val_uuid] = None
|
||||
elif val_type == 'string':
|
||||
cls_dict[val_name] = val_text.encode('utf8')
|
||||
elif val_type == 'float':
|
||||
cls_dict[val_name] = float(val_text)
|
||||
elif val_type == 'int':
|
||||
cls_dict[val_name] = int(val_text)
|
||||
elif val_type == 'bool':
|
||||
cls_dict[val_name] = bool(int(val_text))
|
||||
state[uuid] = cls_dict
|
||||
|
||||
return state
|
||||
|
||||
def save_state(self, cls, state):
|
||||
"""Save a Xen API record struct into an XML persistent storage
|
||||
for future loading when Xend restarts.
|
||||
|
||||
If we encounter a dictionary or a list, we only store the
|
||||
keys because they are going to be UUID references to another
|
||||
object.
|
||||
|
||||
@param cls: Class name (singular) of the record
|
||||
@type cls: string
|
||||
@param state: a Xen API struct of the state of the class.
|
||||
@type state: dict
|
||||
@rtype: None
|
||||
"""
|
||||
|
||||
xml_path = self._xml_file(cls)
|
||||
|
||||
doc = minidom.getDOMImplementation().createDocument(None,
|
||||
cls + 's',
|
||||
None)
|
||||
root = doc.documentElement
|
||||
|
||||
# Marshall a dictionary into our custom XML file format.
|
||||
for uuid, info in state.items():
|
||||
node = doc.createElement(cls)
|
||||
root.appendChild(node)
|
||||
node.setAttribute('uuid', uuid)
|
||||
|
||||
for key, val in info.items():
|
||||
store_val = val
|
||||
store_type = None
|
||||
|
||||
# deal with basic types
|
||||
if type(val) in (str, unicode):
|
||||
store_val = val
|
||||
store_type = 'string'
|
||||
elif type(val) == int:
|
||||
store_val = str(val)
|
||||
store_type = 'int'
|
||||
elif type(val) == float:
|
||||
store_val = str(val)
|
||||
store_type = 'float'
|
||||
elif type(val) == bool:
|
||||
store_val = str(int(val))
|
||||
store_type = 'bool'
|
||||
|
||||
if store_type != None:
|
||||
val_node = doc.createElement(key)
|
||||
val_node.setAttribute('type', store_type)
|
||||
node.appendChild(val_node)
|
||||
# attach the value
|
||||
val_text = doc.createTextNode(store_val)
|
||||
val_node.appendChild(val_text)
|
||||
continue
|
||||
|
||||
# deal with dicts and lists
|
||||
if type(val) == dict:
|
||||
for val_uuid in val.keys():
|
||||
val_node = doc.createElement(key)
|
||||
val_node.setAttribute('uuid', val_uuid)
|
||||
node.appendChild(val_node)
|
||||
elif type(val) in (list, tuple):
|
||||
for val_uuid in val:
|
||||
val_node = doc.createElement(key)
|
||||
val_node.setAttribute('uuid', val_uuid)
|
||||
node.appendChild(val_node)
|
||||
|
||||
open(xml_path, 'w').write(doc.toprettyxml())
|
||||
|
||||
|
||||
@@ -24,25 +24,38 @@ import logging
|
||||
import os
|
||||
import stat
|
||||
import threading
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from xen.util import mkdir
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendVDI import *
|
||||
|
||||
XEND_STORAGE_MAX_IGNORE = -1
|
||||
|
||||
XEND_STORAGE_NO_MAXIMUM = sys.maxint
|
||||
XEND_STORAGE_DIR = "/var/lib/xend/storage/"
|
||||
XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
|
||||
XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
|
||||
QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -p %d %s"
|
||||
QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s"
|
||||
|
||||
MB = 1024 * 1024
|
||||
|
||||
log = logging.getLogger("xend.XendStorageRepository")
|
||||
|
||||
|
||||
class DeviceInvalidError(Exception):
|
||||
pass
|
||||
def qcow_virtual_size(qcow_file):
|
||||
"""Read the first 32 bytes of the QCoW header to determine its size.
|
||||
|
||||
See: http://www.gnome.org/~markmc/qcow-image-format.html.
|
||||
"""
|
||||
try:
|
||||
qcow_header = open(qcow_file, 'rb').read(32)
|
||||
parts = struct.unpack('>IIQIIQ', qcow_header)
|
||||
return parts[-1]
|
||||
except IOError:
|
||||
return -1
|
||||
|
||||
class XendStorageRepository:
|
||||
"""A simple file backed QCOW Storage Repository.
|
||||
@@ -54,11 +67,13 @@ class XendStorageRepository:
|
||||
The actual images are created in the format <uuid>.img and <uuid>.qcow.
|
||||
"""
|
||||
|
||||
def __init__(self, storage_dir = XEND_STORAGE_DIR,
|
||||
storage_max = XEND_STORAGE_MAX_IGNORE):
|
||||
def __init__(self, uuid,
|
||||
sr_type = "qcow_file",
|
||||
name_label = "Local",
|
||||
name_description = "Xend Storage Repository",
|
||||
location = XEND_STORAGE_DIR,
|
||||
storage_max = XEND_STORAGE_NO_MAXIMUM):
|
||||
"""
|
||||
@keyword storage_dir: Where the images will be stored.
|
||||
@type storage_dir: string
|
||||
@keyword storage_max: Maximum disk space to use in bytes.
|
||||
@type storage_max: int
|
||||
|
||||
@@ -67,71 +82,82 @@ class XendStorageRepository:
|
||||
@type images: dictionary by image uuid.
|
||||
@ivar lock: lock to provide thread safety.
|
||||
"""
|
||||
|
||||
self.storage_dir = storage_dir
|
||||
self.storage_max = storage_max
|
||||
self.storage_free = 0
|
||||
self.images = {}
|
||||
|
||||
# XenAPI Parameters
|
||||
self.uuid = self._sr_uuid()
|
||||
self.type = "qcow-file"
|
||||
self.location = self.storage_dir
|
||||
self.name_label = "Local"
|
||||
self.name_description = "Xend Storage Repository"
|
||||
self.uuid = uuid
|
||||
self.type = sr_type
|
||||
self.location = location
|
||||
self.name_label = name_label
|
||||
self.name_description = name_description
|
||||
self.images = {}
|
||||
|
||||
self.storage_max = storage_max
|
||||
self.storage_free = 0
|
||||
self.storage_used = 0
|
||||
self.storage_alloc = 0
|
||||
|
||||
self.lock = threading.RLock()
|
||||
self._refresh()
|
||||
self._refresh()
|
||||
|
||||
def _sr_uuid(self):
|
||||
uuid_file = os.path.join(XEND_STORAGE_DIR, 'uuid')
|
||||
try:
|
||||
if uuid_file and os.path.exists(uuid_file):
|
||||
return open(uuid_file, 'r').read().strip()
|
||||
else:
|
||||
new_uuid = uuid.createString()
|
||||
open(uuid_file, 'w').write(new_uuid + '\n')
|
||||
return new_uuid
|
||||
except IOError:
|
||||
log.exception("Failed to determine SR UUID")
|
||||
|
||||
return uuid.createString()
|
||||
def get_record(self):
|
||||
retval = {'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'virtual_allocation': self.storage_alloc,
|
||||
'physical_utilisation': self.storage_used,
|
||||
'physical_size': self.storage_max,
|
||||
'type': self.type,
|
||||
'location': self.location,
|
||||
'VDIs': self.images.keys()}
|
||||
|
||||
if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
retval['physical_size'] = stfs.f_blocks * stfs.f_frsize
|
||||
|
||||
return retval
|
||||
|
||||
def _refresh(self):
|
||||
"""Internal function that refreshes the state of the disk and
|
||||
updates the list of images available.
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
mkdir.parents(XEND_STORAGE_DIR, stat.S_IRWXU)
|
||||
mkdir.parents(self.location, stat.S_IRWXU)
|
||||
|
||||
# scan the directory and populate self.images
|
||||
total_used = 0
|
||||
virtual_alloc = 0
|
||||
physical_used = 0
|
||||
seen_images = []
|
||||
for filename in os.listdir(XEND_STORAGE_DIR):
|
||||
for filename in os.listdir(self.location):
|
||||
if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
|
||||
image_uuid = filename[:-5]
|
||||
seen_images.append(image_uuid)
|
||||
|
||||
qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
|
||||
cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
|
||||
qcow_path = os.path.join(self.location, qcow_file)
|
||||
cfg_path = os.path.join(self.location, cfg_file)
|
||||
|
||||
phys_size = os.stat(qcow_path).st_size
|
||||
virt_size = qcow_virtual_size(qcow_path)
|
||||
|
||||
# add this image if we haven't seen it before
|
||||
if image_uuid not in self.images:
|
||||
qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
|
||||
cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
|
||||
qcow_path = os.path.join(XEND_STORAGE_DIR, qcow_file)
|
||||
cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_file)
|
||||
|
||||
qcow_size = os.stat(qcow_path).st_size
|
||||
|
||||
# TODO: no way to stat virtual size of qcow
|
||||
vdi = XendQCOWVDI(image_uuid, self.uuid,
|
||||
qcow_path, cfg_path,
|
||||
qcow_size, qcow_size)
|
||||
virt_size, phys_size)
|
||||
|
||||
if cfg_path and os.path.exists(cfg_path):
|
||||
vdi.load_config(cfg_path)
|
||||
try:
|
||||
vdi.load_config(cfg_path)
|
||||
except:
|
||||
log.error('Corrupt VDI configuration file %s' %
|
||||
cfg_path)
|
||||
|
||||
self.images[image_uuid] = vdi
|
||||
total_used += qcow_size
|
||||
|
||||
physical_used += phys_size
|
||||
virtual_alloc += virt_size
|
||||
|
||||
# remove images that aren't valid
|
||||
for image_uuid in self.images.keys():
|
||||
@@ -142,11 +168,14 @@ class XendStorageRepository:
|
||||
pass
|
||||
del self.images[image_uuid]
|
||||
|
||||
self.storage_alloc = virtual_alloc
|
||||
self.storage_used = physical_used
|
||||
|
||||
# update free storage if we have to track that
|
||||
if self.storage_max != XEND_STORAGE_MAX_IGNORE:
|
||||
self.storage_free = self.storage_max - total_used
|
||||
else:
|
||||
if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
|
||||
self.storage_free = self._get_free_space()
|
||||
else:
|
||||
self.storage_free = self.storage_max - self.storage_alloc
|
||||
|
||||
finally:
|
||||
self.lock.release()
|
||||
@@ -158,7 +187,7 @@ class XendStorageRepository:
|
||||
|
||||
@rtype: int
|
||||
"""
|
||||
stfs = os.statvfs(self.storage_dir)
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_bavail * stfs.f_frsize
|
||||
|
||||
def _has_space_available_for(self, size_bytes):
|
||||
@@ -167,22 +196,19 @@ class XendStorageRepository:
|
||||
|
||||
@rtype: bool
|
||||
"""
|
||||
if self.storage_max != -1:
|
||||
return self.storage_free
|
||||
if self.storage_max != XEND_STORAGE_NO_MAXIMUM:
|
||||
return self.storage_free > size_bytes
|
||||
|
||||
bytes_free = self._get_free_space()
|
||||
try:
|
||||
if size_bytes < bytes_free:
|
||||
return True
|
||||
except DeviceInvalidError:
|
||||
pass
|
||||
if size_bytes < bytes_free:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_image_files(self, desired_size_bytes):
|
||||
"""Create an image and return its assigned UUID.
|
||||
|
||||
@param desired_size_kb: Desired image size in KB.
|
||||
@type desired_size_kb: int
|
||||
@param desired_size_bytes: Desired image size in bytes
|
||||
@type desired_size_bytes: int
|
||||
@rtype: string
|
||||
@return: uuid
|
||||
|
||||
@@ -194,7 +220,7 @@ class XendStorageRepository:
|
||||
raise XendError("Not enough space")
|
||||
|
||||
image_uuid = uuid.createString()
|
||||
qcow_path = os.path.join(XEND_STORAGE_DIR,
|
||||
qcow_path = os.path.join(self.location,
|
||||
XEND_STORAGE_QCOW_FILENAME % image_uuid)
|
||||
|
||||
if qcow_path and os.path.exists(qcow_path):
|
||||
@@ -268,10 +294,11 @@ class XendStorageRepository:
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
if self.storage_max != XEND_STORAGE_MAX_IGNORE:
|
||||
return self.storage_max
|
||||
if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_blocks * stfs.f_frsize
|
||||
else:
|
||||
return self.free_space_bytes() + self.used_space_bytes()
|
||||
return self.storage_max
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
@@ -281,10 +308,17 @@ class XendStorageRepository:
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
total_used = 0
|
||||
for val in self.images.values():
|
||||
total_used += val.physical_utilisation
|
||||
return total_used
|
||||
return self.storage_used
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def virtual_allocation(self):
|
||||
"""Returns the total virtual space allocated within the storage repo.
|
||||
@rtype: int
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self.storage_alloc
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
@@ -315,7 +349,7 @@ class XendStorageRepository:
|
||||
|
||||
# save configuration to file
|
||||
cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid
|
||||
cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_filename)
|
||||
cfg_path = os.path.join(self.location, cfg_filename)
|
||||
image.save_config(cfg_path)
|
||||
|
||||
except Exception, e:
|
||||
@@ -327,10 +361,10 @@ class XendStorageRepository:
|
||||
|
||||
return image_uuid
|
||||
|
||||
def xen_api_get_by_label(self, label):
|
||||
def xen_api_get_by_name_label(self, label):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
for image_uuid, val in self.images.values():
|
||||
for image_uuid, val in self.images.items():
|
||||
if val.name_label == label:
|
||||
return image_uuid
|
||||
return None
|
||||
|
||||
@@ -141,6 +141,21 @@ class XendVDI(AutoSaveObject):
|
||||
|
||||
return True
|
||||
|
||||
def get_record(self):
|
||||
return {'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'virtual_size': self.virtual_size,
|
||||
'physical_utilisation': self.physical_utilisation,
|
||||
'sector_size': self.sector_size,
|
||||
'parent': None,
|
||||
'children': [],
|
||||
'sharable': False,
|
||||
'readonly': False,
|
||||
'SR': self.sr.get_uuid(),
|
||||
'VBDs': []}
|
||||
|
||||
|
||||
class XendQCOWVDI(XendVDI):
|
||||
|
||||
def __init__(self, uuid, sr_uuid, qcow_path, cfg_path, vsize, psize):
|
||||
|
||||
@@ -68,7 +68,7 @@ class ImageHandler:
|
||||
def __init__(self, vm, vmConfig, imageConfig, deviceConfig):
|
||||
self.vm = vm
|
||||
|
||||
self.bootloader = None
|
||||
self.bootloader = False
|
||||
self.kernel = None
|
||||
self.ramdisk = None
|
||||
self.cmdline = None
|
||||
@@ -77,10 +77,15 @@ class ImageHandler:
|
||||
|
||||
def configure(self, vmConfig, imageConfig, _):
|
||||
"""Config actions common to all unix-like domains."""
|
||||
self.bootloader = vmConfig['PV_bootloader']
|
||||
self.kernel = vmConfig['PV_kernel']
|
||||
self.cmdline = vmConfig['PV_args']
|
||||
self.ramdisk = vmConfig['PV_ramdisk']
|
||||
if '_temp_using_bootloader' in vmConfig:
|
||||
self.bootloader = True
|
||||
self.kernel = vmConfig['_temp_kernel']
|
||||
self.cmdline = vmConfig['_temp_args']
|
||||
self.ramdisk = vmConfig['_temp_ramdisk']
|
||||
else:
|
||||
self.kernel = vmConfig['PV_kernel']
|
||||
self.cmdline = vmConfig['PV_args']
|
||||
self.ramdisk = vmConfig['PV_ramdisk']
|
||||
self.vm.storeVm(("image/ostype", self.ostype),
|
||||
("image/kernel", self.kernel),
|
||||
("image/cmdline", self.cmdline),
|
||||
@@ -312,6 +317,9 @@ class HVMImageHandler(ImageHandler):
|
||||
def configure(self, vmConfig, imageConfig, deviceConfig):
|
||||
ImageHandler.configure(self, vmConfig, imageConfig, deviceConfig)
|
||||
|
||||
if not self.kernel:
|
||||
self.kernel = '/usr/lib/xen/boot/hvmloader'
|
||||
|
||||
info = xc.xeninfo()
|
||||
if 'hvm' not in info['xen_caps']:
|
||||
raise VmError("HVM guest support is unavailable: is VT/AMD-V "
|
||||
|
||||
@@ -29,8 +29,13 @@ _xend_autorestart = {
|
||||
"SunOS": False,
|
||||
}
|
||||
|
||||
_pygrub_path = {
|
||||
"SunOS": "/usr/lib/xen/bin/pygrub"
|
||||
}
|
||||
|
||||
def _get(var, default=None):
|
||||
return var.get(os.uname()[0], default)
|
||||
|
||||
scripts_dir = _get(_scripts_dir, "/etc/xen/scripts")
|
||||
xend_autorestart = _get(_xend_autorestart)
|
||||
pygrub_path = _get(_pygrub_path, "/usr/bin/pygrub")
|
||||
@@ -203,7 +203,7 @@ class Daemon:
|
||||
|
||||
if not osdep.xend_autorestart:
|
||||
self.run(os.fdopen(w, 'w'))
|
||||
break
|
||||
os._exit(0)
|
||||
|
||||
pid = self.fork_pid()
|
||||
if pid:
|
||||
|
||||
@@ -48,7 +48,7 @@ from threading import Thread
|
||||
|
||||
from xen.web.httpserver import HttpServer, UnixHttpServer
|
||||
|
||||
from xen.xend import XendRoot, XendAPI
|
||||
from xen.xend import XendNode, XendRoot, XendAPI
|
||||
from xen.xend import Vifctl
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendClient import XEN_API_SOCKET
|
||||
@@ -100,6 +100,8 @@ class XendServers:
|
||||
signal.signal(signal.SIGHUP, self.reloadConfig)
|
||||
|
||||
while True:
|
||||
XendNode.instance().refreshBridges()
|
||||
|
||||
threads = []
|
||||
for server in self.servers:
|
||||
if server.ready:
|
||||
@@ -198,16 +200,18 @@ def _loadConfig(servers, root, reload):
|
||||
|
||||
if len(addrport) == 1:
|
||||
if addrport[0] == 'unix':
|
||||
servers.add(XMLRPCServer(auth,
|
||||
servers.add(XMLRPCServer(auth, True,
|
||||
path = XEN_API_SOCKET,
|
||||
hosts_allowed = allowed))
|
||||
else:
|
||||
servers.add(
|
||||
XMLRPCServer(auth, True, '', int(addrport[0]),
|
||||
XMLRPCServer(auth, True, True, '',
|
||||
int(addrport[0]),
|
||||
hosts_allowed = allowed))
|
||||
else:
|
||||
addr, port = addrport
|
||||
servers.add(XMLRPCServer(auth, True, addr, int(port),
|
||||
servers.add(XMLRPCServer(auth, True, True, addr,
|
||||
int(port),
|
||||
hosts_allowed = allowed))
|
||||
except ValueError, exn:
|
||||
log.error('Xen-API server configuration %s is invalid.', api_cfg)
|
||||
@@ -215,10 +219,10 @@ def _loadConfig(servers, root, reload):
|
||||
log.error('Xen-API server configuration %s is invalid.', api_cfg)
|
||||
|
||||
if xroot.get_xend_tcp_xmlrpc_server():
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, True))
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, True))
|
||||
|
||||
if xroot.get_xend_unix_xmlrpc_server():
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM))
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False))
|
||||
|
||||
|
||||
def create():
|
||||
|
||||
@@ -89,8 +89,8 @@ methods = ['device_create', 'device_configure',
|
||||
exclude = ['domain_create', 'domain_restore']
|
||||
|
||||
class XMLRPCServer:
|
||||
def __init__(self, auth, use_tcp=False, host = "localhost", port = 8006,
|
||||
path = XML_RPC_SOCKET, hosts_allowed = None):
|
||||
def __init__(self, auth, use_xenapi, use_tcp=False, host = "localhost",
|
||||
port = 8006, path = XML_RPC_SOCKET, hosts_allowed = None):
|
||||
self.use_tcp = use_tcp
|
||||
self.port = port
|
||||
self.host = host
|
||||
@@ -100,7 +100,7 @@ class XMLRPCServer:
|
||||
self.ready = False
|
||||
self.running = True
|
||||
self.auth = auth
|
||||
self.xenapi = XendAPI.XendAPI(auth)
|
||||
self.xenapi = use_xenapi and XendAPI.XendAPI(auth) or None
|
||||
|
||||
def run(self):
|
||||
authmsg = (self.auth == XendAPI.AUTH_NONE and
|
||||
@@ -115,11 +115,13 @@ class XMLRPCServer:
|
||||
self.port, authmsg)
|
||||
self.server = TCPXMLRPCServer((self.host, self.port),
|
||||
self.hosts_allowed,
|
||||
self.xenapi is not None,
|
||||
logRequests = False)
|
||||
else:
|
||||
log.info("Opening Unix domain socket XML-RPC server on %s%s",
|
||||
self.path, authmsg)
|
||||
self.server = UnixXMLRPCServer(self.path, self.hosts_allowed,
|
||||
self.xenapi is not None,
|
||||
logRequests = False)
|
||||
except socket.error, exn:
|
||||
log.error('Cannot start server: %s!', exn.args[1])
|
||||
@@ -133,9 +135,10 @@ class XMLRPCServer:
|
||||
# and has the 'api' attribute.
|
||||
|
||||
for meth_name in dir(self.xenapi):
|
||||
meth = getattr(self.xenapi, meth_name)
|
||||
if meth_name[0] != '_' and callable(meth) and hasattr(meth, 'api'):
|
||||
self.server.register_function(meth, getattr(meth, 'api'))
|
||||
if meth_name[0] != '_':
|
||||
meth = getattr(self.xenapi, meth_name)
|
||||
if callable(meth) and hasattr(meth, 'api'):
|
||||
self.server.register_function(meth, getattr(meth, 'api'))
|
||||
|
||||
# Legacy deprecated xm xmlrpc api
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@@ -150,16 +150,20 @@ class NetifController(DevController):
|
||||
|
||||
devid = self.allocateDeviceID()
|
||||
|
||||
# The default type is 'netfront'.
|
||||
if not typ:
|
||||
typ = 'netfront'
|
||||
|
||||
if not mac:
|
||||
mac = randomMAC()
|
||||
|
||||
back = { 'script' : script,
|
||||
'mac' : mac,
|
||||
'handle' : "%i" % devid }
|
||||
'handle' : "%i" % devid,
|
||||
'type' : typ }
|
||||
|
||||
if typ == 'ioemu':
|
||||
front = {}
|
||||
back['type'] = 'ioemu'
|
||||
else:
|
||||
front = { 'handle' : "%i" % devid,
|
||||
'mac' : mac }
|
||||
|
||||
@@ -64,7 +64,7 @@ class VfbifController(DevController):
|
||||
if config.has_key("vncunused"):
|
||||
args += ["--unused"]
|
||||
elif config.has_key("vncdisplay"):
|
||||
args += ["--vncport", "%d" % (5900 + config["vncdisplay"])]
|
||||
args += ["--vncport", "%d" % (5900 + int(config["vncdisplay"]))]
|
||||
vnclisten = config.get("vnclisten",
|
||||
xen.xend.XendRoot.instance().get_vnclisten_address())
|
||||
args += [ "--listen", vnclisten ]
|
||||
|
||||
@@ -44,17 +44,45 @@
|
||||
# OF THIS SOFTWARE.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
import gettext
|
||||
import xmlrpclib
|
||||
|
||||
import xen.util.xmlrpclib2
|
||||
|
||||
|
||||
translation = gettext.translation('xen-xm', fallback = True)
|
||||
|
||||
class Failure(Exception):
|
||||
def __init__(self, details):
|
||||
self.details = details
|
||||
try:
|
||||
# If this failure is MESSAGE_PARAMETER_COUNT_MISMATCH, then we
|
||||
# correct the return values here, to account for the fact that we
|
||||
# transparently add the session handle as the first argument.
|
||||
if details[0] == 'MESSAGE_PARAMETER_COUNT_MISMATCH':
|
||||
details[2] = str(int(details[2]) - 1)
|
||||
details[3] = str(int(details[3]) - 1)
|
||||
|
||||
self.details = details
|
||||
except Exception, exn:
|
||||
self.details = ['INTERNAL_ERROR', 'Client-side: ' + str(exn)]
|
||||
|
||||
def __str__(self):
|
||||
return "Xen-API failure: %s" % str(self.details)
|
||||
try:
|
||||
return translation.ugettext(self.details[0]) % self._details_map()
|
||||
except TypeError, exn:
|
||||
return "Message database broken: %s.\nXen-API failure: %s" % \
|
||||
(exn, str(self.details))
|
||||
except Exception, exn:
|
||||
import sys
|
||||
print >>sys.stderr, exn
|
||||
return "Xen-API failure: %s" % str(self.details)
|
||||
|
||||
def _details_map(self):
|
||||
return dict([(str(i), self.details[i])
|
||||
for i in range(len(self.details))])
|
||||
|
||||
|
||||
_RECONNECT_AND_RETRY = (lambda _ : ())
|
||||
|
||||
|
||||
class Session(xen.util.xmlrpclib2.ServerProxy):
|
||||
@@ -80,6 +108,8 @@ class Session(xen.util.xmlrpclib2.ServerProxy):
|
||||
encoding, verbose,
|
||||
allow_none)
|
||||
self._session = None
|
||||
self.last_login_method = None
|
||||
self.last_login_params = None
|
||||
|
||||
|
||||
def xenapi_request(self, methodname, params):
|
||||
@@ -87,13 +117,31 @@ class Session(xen.util.xmlrpclib2.ServerProxy):
|
||||
self._login(methodname, params)
|
||||
return None
|
||||
else:
|
||||
full_params = (self._session,) + params
|
||||
return _parse_result(getattr(self, methodname)(*full_params))
|
||||
retry_count = 0
|
||||
while retry_count < 3:
|
||||
full_params = (self._session,) + params
|
||||
result = _parse_result(getattr(self, methodname)(*full_params))
|
||||
if result == _RECONNECT_AND_RETRY:
|
||||
retry_count += 1
|
||||
if self.last_login_method:
|
||||
self._login(self.last_login_method,
|
||||
self.last_login_params)
|
||||
else:
|
||||
raise xmlrpclib.Fault(401, 'You must log in')
|
||||
else:
|
||||
return result
|
||||
raise xmlrpclib.Fault(
|
||||
500, 'Tried 3 times to get a valid session, but failed')
|
||||
|
||||
|
||||
def _login(self, method, params):
|
||||
self._session = _parse_result(
|
||||
getattr(self, 'session.%s' % method)(*params))
|
||||
result = _parse_result(getattr(self, 'session.%s' % method)(*params))
|
||||
if result == _RECONNECT_AND_RETRY:
|
||||
raise xmlrpclib.Fault(
|
||||
500, 'Received SESSION_INVALID when logging in')
|
||||
self._session = result
|
||||
self.last_login_method = method
|
||||
self.last_login_params = params
|
||||
|
||||
|
||||
def __getattr__(self, name):
|
||||
@@ -106,7 +154,7 @@ class Session(xen.util.xmlrpclib2.ServerProxy):
|
||||
|
||||
|
||||
def _parse_result(result):
|
||||
if 'Status' not in result:
|
||||
if type(result) != dict or 'Status' not in result:
|
||||
raise xmlrpclib.Fault(500, 'Missing Status in response from server')
|
||||
if result['Status'] == 'Success':
|
||||
if 'Value' in result:
|
||||
@@ -116,7 +164,10 @@ def _parse_result(result):
|
||||
'Missing Value in response from server')
|
||||
else:
|
||||
if 'ErrorDescription' in result:
|
||||
raise Failure(result['ErrorDescription'])
|
||||
if result['ErrorDescription'][0] == 'SESSION_INVALID':
|
||||
return _RECONNECT_AND_RETRY
|
||||
else:
|
||||
raise Failure(result['ErrorDescription'])
|
||||
else:
|
||||
raise xmlrpclib.Fault(
|
||||
500, 'Missing ErrorDescription in response from server')
|
||||
@@ -127,10 +178,18 @@ class _Dispatcher:
|
||||
def __init__(self, send, name):
|
||||
self.__send = send
|
||||
self.__name = name
|
||||
|
||||
def __repr__(self):
|
||||
if self.__name:
|
||||
return '<XenAPI._Dispatcher for %s>' % self.__name
|
||||
else:
|
||||
return '<XenAPI._Dispatcher>'
|
||||
|
||||
def __getattr__(self, name):
|
||||
if self.__name is None:
|
||||
return _Dispatcher(self.__send, name)
|
||||
else:
|
||||
return _Dispatcher(self.__send, "%s.%s" % (self.__name, name))
|
||||
|
||||
def __call__(self, *args):
|
||||
return self.__send(self.__name, args)
|
||||
@@ -28,6 +28,7 @@ import xmlrpclib
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import osdep
|
||||
import xen.xend.XendClient
|
||||
from xen.xend.XendBootloader import bootloader
|
||||
from xen.util import blkif
|
||||
@@ -300,7 +301,7 @@ gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,ba
|
||||
fn=append_value, default=[],
|
||||
use="""Add a network interface with the given MAC address and bridge.
|
||||
The vif is configured by calling the given configuration script.
|
||||
If type is not specified, default is netfront not ioemu device.
|
||||
If type is not specified, default is netfront.
|
||||
If mac is not specified a random MAC address is used.
|
||||
If not specified then the network backend chooses it's own MAC address.
|
||||
If bridge is not specified the first bridge found is used.
|
||||
@@ -718,8 +719,11 @@ def run_bootloader(vals, config_image):
|
||||
"--entry= directly.")
|
||||
vals.bootargs = "--entry=%s" %(vals.bootentry,)
|
||||
|
||||
kernel = sxp.child_value(config_image, 'kernel')
|
||||
ramdisk = sxp.child_value(config_image, 'ramdisk')
|
||||
args = sxp.child_value(config_image, 'args')
|
||||
return bootloader(vals.bootloader, file, not vals.console_autoconnect,
|
||||
vals.bootargs, config_image)
|
||||
vals.bootargs, kernel, ramdisk, args)
|
||||
|
||||
def make_config(vals):
|
||||
"""Create the domain configuration.
|
||||
@@ -759,7 +763,14 @@ def make_config(vals):
|
||||
|
||||
config_image = configure_image(vals)
|
||||
if vals.bootloader:
|
||||
config_image = run_bootloader(vals, config_image)
|
||||
if vals.bootloader == "pygrub":
|
||||
vals.bootloader = osdep.pygrub_path
|
||||
|
||||
# if a kernel is specified, we're using the bootloader
|
||||
# non-interactively, and need to let xend run it so we preserve the
|
||||
# real kernel choice.
|
||||
if not vals.kernel:
|
||||
config_image = run_bootloader(vals, config_image)
|
||||
config.append(['bootloader', vals.bootloader])
|
||||
if vals.bootargs:
|
||||
config.append(['bootloader_args', vals.bootargs])
|
||||
@@ -823,7 +834,7 @@ def preprocess_ioports(vals):
|
||||
if len(d) == 1:
|
||||
d.append(d[0])
|
||||
# Components are in hex: add hex specifier.
|
||||
hexd = map(lambda v: '0x'+v, d)
|
||||
hexd = ['0x' + x for x in d]
|
||||
ioports.append(hexd)
|
||||
vals.ioports = ioports
|
||||
|
||||
@@ -990,8 +1001,6 @@ def preprocess_vnc(vals):
|
||||
vals.extra = vnc + ' ' + vals.extra
|
||||
|
||||
def preprocess(vals):
|
||||
if not vals.kernel and not vals.bootloader:
|
||||
err("No kernel specified")
|
||||
preprocess_disk(vals)
|
||||
preprocess_pci(vals)
|
||||
preprocess_ioports(vals)
|
||||
@@ -1176,6 +1185,7 @@ def config_security_check(config, verbose):
|
||||
try:
|
||||
domain_label = security.ssidref2label(security.NULL_SSIDREF)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc(limit=1)
|
||||
return 0
|
||||
domain_policy = 'NULL'
|
||||
|
||||
+32
-15
@@ -130,7 +130,7 @@ SUBCOMMAND_HELP = {
|
||||
'log' : ('', 'Print Xend log'),
|
||||
'rename' : ('<Domain> <NewDomainName>', 'Rename a domain.'),
|
||||
'sched-sedf' : ('<Domain> [options]', 'Get/set EDF parameters.'),
|
||||
'sched-credit': ('-d <Domain> [-w[=WEIGHT]|-c[=CAP]]',
|
||||
'sched-credit': ('[-d <Domain> [-w[=WEIGHT]|-c[=CAP]]]',
|
||||
'Get/set credit scheduler parameters.'),
|
||||
'sysrq' : ('<Domain> <letter>', 'Send a sysrq to a domain.'),
|
||||
'vcpu-list' : ('[<Domain>]',
|
||||
@@ -717,6 +717,10 @@ def parse_sedf_info(info):
|
||||
'weight' : get_info('weight', int, -1),
|
||||
}
|
||||
|
||||
def domid_match(domid, info):
|
||||
return domid is None or domid == info['name'] or \
|
||||
domid == str(info['domid'])
|
||||
|
||||
def xm_brief_list(doms):
|
||||
print '%-40s %3s %5s %5s %10s %9s' % \
|
||||
('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)')
|
||||
@@ -1091,10 +1095,6 @@ def xm_sched_sedf(args):
|
||||
print( ("%(name)-32s %(domid)3d %(period)9.1f %(slice)9.1f" +
|
||||
" %(latency)7.1f %(extratime)6d %(weight)6d") % info)
|
||||
|
||||
def domid_match(domid, info):
|
||||
return domid is None or domid == info['name'] or \
|
||||
domid == str(info['domid'])
|
||||
|
||||
# we want to just display current info if no parameters are passed
|
||||
if len(args) == 0:
|
||||
domid = None
|
||||
@@ -1174,27 +1174,43 @@ def xm_sched_credit(args):
|
||||
err(opterr)
|
||||
usage('sched-credit')
|
||||
|
||||
domain = None
|
||||
domid = None
|
||||
weight = None
|
||||
cap = None
|
||||
|
||||
for o, a in opts:
|
||||
if o == "-d":
|
||||
domain = a
|
||||
domid = a
|
||||
elif o == "-w":
|
||||
weight = int(a)
|
||||
elif o == "-c":
|
||||
cap = int(a);
|
||||
|
||||
if domain is None:
|
||||
# place holder for system-wide scheduler parameters
|
||||
err("No domain given.")
|
||||
usage('sched-credit')
|
||||
doms = filter(lambda x : domid_match(domid, x),
|
||||
[parse_doms_info(dom)
|
||||
for dom in getDomains(None, 'running')])
|
||||
|
||||
if weight is None and cap is None:
|
||||
print server.xend.domain.sched_credit_get(domain)
|
||||
# print header if we aren't setting any parameters
|
||||
print '%-33s %-2s %-6s %-4s' % ('Name','ID','Weight','Cap')
|
||||
|
||||
for d in doms:
|
||||
try:
|
||||
info = server.xend.domain.sched_credit_get(d['domid'])
|
||||
except xmlrpclib.Fault:
|
||||
# domain does not support sched-credit?
|
||||
info = {'weight': -1, 'cap': -1}
|
||||
|
||||
info['name'] = d['name']
|
||||
info['domid'] = int(d['domid'])
|
||||
print( ("%(name)-32s %(domid)3d %(weight)6d %(cap)4d") % info)
|
||||
else:
|
||||
result = server.xend.domain.sched_credit_set(domain, weight, cap)
|
||||
if domid is None:
|
||||
# place holder for system-wide scheduler parameters
|
||||
err("No domain given.")
|
||||
usage('sched-credit')
|
||||
|
||||
result = server.xend.domain.sched_credit_set(domid, weight, cap)
|
||||
if result != 0:
|
||||
err(str(result))
|
||||
|
||||
@@ -1244,7 +1260,7 @@ def xm_console(args):
|
||||
if quiet:
|
||||
sys.exit(1)
|
||||
else:
|
||||
raise Exception("Domain is not started")
|
||||
raise xmlrpclib.Fault(0, "Domain '%s' is not started" % dom)
|
||||
|
||||
console.execConsole(domid)
|
||||
|
||||
@@ -1780,7 +1796,8 @@ def _run_cmd(cmd, cmd_name, args):
|
||||
except SystemExit, code:
|
||||
return code == 0, code
|
||||
except XenAPI.Failure, exn:
|
||||
err(str(exn))
|
||||
for line in [''] + wrap(str(exn), 80) + ['']:
|
||||
print >>sys.stderr, line
|
||||
except xmlrpclib.Fault, ex:
|
||||
if ex.faultCode == XendClient.ERROR_INVALID_DOMAIN:
|
||||
err("Domain '%s' does not exist." % ex.faultString)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
# ============================================================================
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of version 2.1 of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation.
|
||||
#
|
||||
# This library 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
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
# ============================================================================
|
||||
# Copyright (c) 2006 XenSource Inc.
|
||||
# ============================================================================
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Xen-xm 3.0\n"
|
||||
"PO-Revision-Date: 2006-12-28 15:43+0000\n"
|
||||
"Last-Translator: Ewan Mellor <ewan@xensource.com>\n"
|
||||
"Language-Team: xen-devel <xen-devel@lists.xensource.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ASCII\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "INTERNAL_ERROR"
|
||||
msgstr "Internal error: %(1)s."
|
||||
|
||||
msgid "MAP_DUPLICATE_KEY"
|
||||
msgstr "This map already contains %(1)s -> %(2)s."
|
||||
|
||||
msgid "MESSAGE_METHOD_UNKNOWN"
|
||||
msgstr "The method %(1)s is unsupported."
|
||||
|
||||
msgid "MESSAGE_PARAMETER_COUNT_MISMATCH"
|
||||
msgstr "The method %(1)s takes %(2)s argument(s) (%(3)s given)."
|
||||
|
||||
msgid "SESSION_AUTHENTICATION_FAILED"
|
||||
msgstr "Permission denied."
|
||||
|
||||
msgid "HOST_CPU_HANDLE_INVALID"
|
||||
msgstr "The host_cpu handle %(1)s is invalid."
|
||||
|
||||
msgid "HOST_HANDLE_INVALID"
|
||||
msgstr "The host handle %(1)s is invalid."
|
||||
|
||||
msgid "SR_HANDLE_INVALID"
|
||||
msgstr "The SR handle %(1)s is invalid."
|
||||
|
||||
msgid "VBD_HANDLE_INVALID"
|
||||
msgstr "The VBD handle %(1)s is invalid."
|
||||
|
||||
msgid "VDI_HANDLE_INVALID"
|
||||
msgstr "The VDI handle %(1)s is invalid."
|
||||
|
||||
msgid "VIF_HANDLE_INVALID"
|
||||
msgstr "The VIF handle %(1)s is invalid."
|
||||
|
||||
msgid "VM_HANDLE_INVALID"
|
||||
msgstr "The VM handle %(1)s is invalid."
|
||||
|
||||
msgid "VTPM_HANDLE_INVALID"
|
||||
msgstr "The VTPM handle %(1)s is invalid."
|
||||
|
||||
msgid "NETWORK_ALREADY_CONNECTED"
|
||||
msgstr "The network you specified already has a PIF attached to it, and so another one may not be attached."
|
||||
|
||||
msgid "VM_BAD_POWER_STATE"
|
||||
msgstr "The VM must be %(2)s to perform the requested operation (it is currently %(3)s)."
|
||||
@@ -52,6 +52,7 @@ def help():
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
opts.reset()
|
||||
args = opts.parse(argv)
|
||||
|
||||
if len(args) != 2:
|
||||
|
||||
@@ -559,14 +559,6 @@ def set_bool(opt, k, v):
|
||||
else:
|
||||
opt.opts.err('Invalid value:' +v)
|
||||
|
||||
def set_u32(opt, k, v):
|
||||
"""Set an option to an u32 value."""
|
||||
try:
|
||||
v = u32(v)
|
||||
except:
|
||||
opt.opts.err('Invalid value: ' + str(v))
|
||||
opt.set(v)
|
||||
|
||||
def set_value(opt, k, v):
|
||||
"""Set an option to a value."""
|
||||
opt.set(v)
|
||||
|
||||
@@ -118,6 +118,7 @@ def main_dom(opts, args):
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
opts.reset()
|
||||
args = opts.parse(argv)
|
||||
if opts.vals.help:
|
||||
return
|
||||
|
||||