Merge xen-api.hg changeset 142:d75413e65ba319d577c764be6c13efa5eae6da1c.

Signed-off-by: Ewan Mellor <ewan@xensource.com>
This commit is contained in:
Ewan Mellor committed 2006-11-01 10:40:46 +00:00
commit fb2358c250
128 files changed
+31815 -1612

No files matched your search

+160
View File
@@ -0,0 +1,160 @@
XendConfig parameters
=====================
Things that are empty means there is no direct mapping.
In order to make the XendConfig fully backwards compatible, it needs a
representation of all the below parameters. Where both columns have
values, it means we can have a direct translation.
Where the Legacy Config value does not exist, it means we have to make
up the value on whether we supported it or not.
Where the Legacy config value is prefixed with an '!', it means it is
not a direct mapping and needs a translation function.
Where the Xen API config value does not exist, it means we have to add
a parameter outside of the Xen API Configuration to support it.
Xen API Config Legacy Config
-------------- -------------
uuid uuid
power_state !state (and xc_getinfo)
name_label name
name_description
user_version
is_a_template
resident_on
memory_static_min memory
memory_static_max maxmem
memory_actual
memory_dynamic_min
memory_dynamic_max
vcpus_policy !set_credit/set_sedf
vcpus_params !set_credit/set_sedf
vcpus_number vcpus
vcpus_utilisation
vcpus_features_required
vcpus_features_can_use
vcpus_features_force_on
vcpus_features_force_off
actions_after_shutdown on_poweroff
actions_after_reboot on_reboot
actions_after_suspend
actions_after_crash on_crash
vifs !(devices.vifs)
- uuid dev.uuid
- name
- type vif.type
- device
- network
- vm
- MAC vif.mac
- MTU
- io_read_kbs !vif.rate
- io_write_kbs !vif.rate
vif.bridge
vif.script
vif.ip
vif.vifname (backend name)
vbds !(devices.vbds)
- uuid
- vm
- vdi
- device vbd.uname
- mode !vbd.mode
- driver vbd.driver
- io_read_kbs
- io_write_kbs
tpm_instance tpm.instance
tpm_backend tpm.backend
bios_boot image.boot?
platform_std_VGA image.stdvga
platform_serial image.serial
platform_localtime !localtime (bool)
platform_clock_offset
platform_enable_audio !image.soundhw (bool)
builder (sxp root name)
boot_method
kernel_kernel kernel.kernel
kernel_initrd kernel.ramdisk
kernel_args !kernel.args
grub_cmdline bootloader_args
PCI_bus
tools_version
otherConfig
- image image (see image.*)
- shadow_memory shadow_memory
- security security
- vcpu_avail vcpu_avail
- features features
- on_xend_stop on_xend_stop
- on_xend_start on_xend_start
- start_time start_time
- cpus cpus (?)
max_vcpu_id
>> only from xc
- online_vcpus xc.online_vcpus
- status xc.status
- cpu_time xc.cpu_time
- shutdown_reason xc.shutdown_reason
- up_time xc.uptime
- crashed xc.crashed
- dying xc.dying
- shutdown xc.shutdown
image.type (linux or hvm)
image.root
image.ip
image.nographic
image.vnc
image.sdl
image.vncdisplay
image.vncunused
image.hvm.device_model
image.hvm.display
image.hvm.xauthority
image.hvm.vncconsole
image.hvm.pae
image.hvm.acpi (also in image.devices)
image.hvm.apic
image.hvm.devices.boot
image.hvm.devices.fda
image.hvm.devices.fdb
image.hvm.devices.soundhw
image.hvm.devices.isa
image.hvm.devices.vcpus?
image.hvm.devices.acpi
image.hvm.devices.usb
image.hvm.devices.usbdevice
dev.backend
dev.dom
dev.id
pci.domain
pci.bus
pci.slot
pci.func
pciquirk.pci_ids
pciquirk.pci_config_space_fields
pciquirk.unconstrained_dev_ids
irq.irq
- vcpu (probably not needed, only in XM and generated dynamically)
vcpu.number
vcpu.online
vcpu.blocked
vcpu.running
vcpu.cpu_time
vcpu.cpu
vcpu.cpumap
+117
View File
@@ -0,0 +1,117 @@
Map of all supported SXP configuration options
----------------------------------------------
uuid
vcpus
maxmem
memory
name
on_poweroff
on_reboot
on_crash
bootloader
kernel_kernel
kernel_initrd
kernel_args
localtime
shadow_memory
security
ssidref (deprecated)
vcpu_avail
cpu_weight (deprecated)
bootloader_args
features
on_xend_stop
on_xend_start
start_time
cpu (deprecated)
cpus
(xc getinfo)
domid
online_vcpus
status
cpu_time
shutdown_reason
(xm list --long)
up_time
image
- kernel
- ramdisk
- args
- ip
- root
(configVNC)
- nographic
- vnc
- sdl
- vncdisplay
- vncunused
(HVM)
- device_model
- display
- xauthority
- vncconsole
- pae
- acpi
- apic
(parseDeviceModel)
- boot
- fda
- fdb
- soundhw
- localtime
- serial
- stdvga
- isa
- vcpus
- acpi
- usb
- usbdevice
(all devices)
- backend
- dom
- id
- uuid
vbd
- uname
- dev (ioemu:, .. etc)
- mode (r, w. w!)
vif
- type
- mac
- bridge
- model
- rate
- vifname
- script
- ip
pci
- domain
- bus
- slot
- func
(Xen 2.0)
- dev
io
- from
- to
tpm
- pref_instance
- instance
pciquirk
- pci_ids
- pci_config_space_fields
- unconstrained_dev_ids
irq
- irq
+49
View File
@@ -0,0 +1,49 @@
Xen API Test
============
xapi.py is a simple command line tool to test the functionality of a
domain lifecycle supporting, Xen API talking version of Xend.
Creating a VM is slightly more work under the Xen API. The differences
with this and xm is:
1. None of the devices are created during vm-create. You must use
vbd-create and vif-create to attach a new device to the VM.
2. VM's that are created using vm-create will not start by
default. You must use vm-start to "start" the domain.
3. VM's that are created using vm-create will not be removed on
shutdown. You must remove it using vm-delete.
Example Configuration Files
---------------------------
xapi.py uses a simple python configuration file similar to xm in the
face of the lack of any other reasonable format.
All the fields are directly mapped to the arguments that are in the
Xen API constructore for the respective classes.
xapi.domcfg.py: example configuration for a paravirtualised domain.
xapi.vbdcfg.py: example configuration for a file based block device.
xapi.vifcfg.py: example configuration for a simple bridged network
device.
Example Session
---------------
xapi.py vm-list
xapi.py vm-create xapi.domcfg.py
xapi.py vbd-create <DomainName> xapi.vbdcfg.py
xapi.py vif-create <DomainName> xapi.vifcfg.py
Notes
-----
Currently lacking:
1. Any real authentication. XendAuthSessions need to be filled in with
a proper authentication implementation either using PAM or other
means.
+136
View File
@@ -0,0 +1,136 @@
Xend Lifecycle/XenAPI Implementation Changes
============================================
Summary of what has changed in this branch of Xend:
Managed Domains
---------------
The concept of managed domains is that Xend now has the ability to
manage the lifecycle of a domain from when it is created to being
shutdown.
XendDomain
~~~~~~~~~~
In order to support managed domains, XendDomain has been modified to
keep the configuration in /var/lib/xend/domains/.
The configuration is stored in SXP format so that it can be easily
loaded by the current Xend. In the future, we may switch to an XML
format similar to how XenAPI defines a VM configuration.
TODO: There are still places where the device configuration or VM
configuration can be altered but the managed domain does not save it.
XendDomainInfo
~~~~~~~~~~~~~~
XendDomainInfo has changed to support this mode of operation,
especially with domain construction and assumptions about the domain
when it shuts down.
All configuration option parsing and validation has been moved from
XendDomainInfo to XendConfig. The purpose is so that we can abstract
away the knowledge of SXP in XendDomainInfo. The goal is to do away
with the bulky way of accessing SXP in Xend and moving that all to a
more pythonic interface.
The DevController stuff at the end of XendDomainInfo has also been
moved to XendDevices because now it is needed in both XendConfig and
XendDomainInfo.
Many of the constants are moved to XendConstants which reduces the
amount of recursive or scoped imports that occur in the code.
XendConfig
~~~~~~~~~~
XendConfig is the beginnings of an interface for configuration options
so that other parts of Xend do not need to know what format the
configuration in. It can accept configuration passed in as parsed SXP
format, python filename or a Xen API struct.
It is a subclass of a python dictionary, and hence access to its
functions are via the __getitem__ accessor.
TODO: Define a proper interface to the XendConfig which is based on
either the Xen API or some other flexible format.
XMLRPCServer
~~~~~~~~~~~~
Changes to the busy loop in here and SrvServer so that the daemon
shuts down cleanly. This also allows us to catch the shutdown and
perform maintanence tasks on the domains.
Replacing xendomains init.d script
==================================
Some work has gone into catching Xend's shutdown so that we can do the
same tasks that xendomains init.d script does but natively in Xend.
For instance, a new configuration option, 'on_xend_start' and
'on_xend_stop' will allow domains that are managed by Xend to start up
when Xend starts, and correspondingly stop when Xend stops.
Xen API
=======
The new Xen API gives a standard interface to creating, configuring,
controlling and destroying VMs and the virtual devices that belong to
it.
It also introduces the concept of Storage Repositories (SR) which are
factories for creating disk images.
XendDomain
~~~~~~~~~~
XendDomain has now separated the section for the Legacy XM XMLRPC API
and the new Xen API.
Since many things have a UUID, these are stored and represented as
close to the existing configuration.
XendDomainInfo
~~~~~~~~~~~~~~
XendDomainInfo now supports UUIDs being assigned to devices and the
domain itself. It will preserve the UUID for managed domains.
A number of new functions are now in XendDomainInfo to provide an
interface to devices.
XendNode
~~~~~~~~
Represents the Host class in the Xen API and also contains an
incomplete representation of the physical CPUs availabel for the host.
XendAuthSessions
~~~~~~~~~~~~~~~~
An abstract authenticator for the Xen API. Currently it is an empty
implementation with rudimentary support for users. The plan is the add
PAM based authentication.
XendAPI
~~~~~~~
The guts of the Xen API implementation. Implements all the supported
functionality of the Xen API by placing calls to the relevent objects
like XendDomain and XendDomanInfo.
The initialisation of the XendAPI object will actually install a
number of validation decorators in order to ensure the input is
correct. It is using some features of introspection and
metaprogramming in Python to reduce the amount of replication in the
code.
XMLRPCServer
~~~~~~~~~~~~
The XMLRPC Server will support both the new Xen API and the old XM
XMLRPC API. The support is clearly marked in the code.
+39
View File
@@ -0,0 +1,39 @@
#
# VM Configuration for Xen API
#
name_label = 'GentooAPI'
name_description = 'Gentoo VM via API'
user_version = 1
is_a_template = False
memory_static_max = 32
memory_dynamic_max = 32
memory_dynamic_min = 32
memory_static_min = 32
VCPUs_policy = ''
VCPUs_params = ''
VCPUS_features_required = ''
VCPUs_features_can_use = ''
VCPUs_features_force_on = ''
VCPUs_features_force_off = ''
actions_after_shutdown = 'destroy'
actions_after_reboot = 'restart'
actions_after_suspend = 'destroy'
actions_after_crash = 'restart'
TPM_instance = ''
TPM_backend = ''
bios_boot = ''
platform_std_VGA = False
platform_serial = ''
platform_localtime = False
platform_clock_offset = False
platform_enable_audio = False
builder = ''
boot_method = '' # this will remove the kernel/initrd ??
kernel_kernel = '/boot/vmlinuz-2.6.16.29-xen'
kernel_initrd = '/root/initrd-2.6.16.29-xen.img'
kernel_args = 'root=/dev/sda1 ro'
grub_cmdline = ''
PCI_bus = ''
other_config = ''
+492
View File
@@ -0,0 +1,492 @@
#!/usr/bin/python
#============================================================================
# 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 Ltd.
#============================================================================
from xen.util.xmlrpclib2 import ServerProxy
from optparse import *
from pprint import pprint
from types import DictType
from getpass import getpass
MB = 1024 * 1024
HOST_INFO_FORMAT = '%-20s: %-50s'
VM_LIST_FORMAT = '%(name_label)-18s %(memory_actual)-5s %(vcpus_number)-5s'\
' %(power_state)-10s %(uuid)-36s'
SR_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(physical_size)-10s' \
'%(type)-10s'
VDI_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(virtual_size)-8s '\
'%(sector_size)-8s'
COMMANDS = {
'host-info': ('', 'Get Xen Host Info'),
'sr-list': ('', 'List all SRs'),
'vbd-create': ('<domname> <pycfg> [opts]',
'Create VBD attached to domname'),
'vdi-create': ('<pycfg> [opts]', 'Create a VDI'),
'vdi-list' : ('', 'List all VDI'),
'vdi-rename': ('<vdi_uuid> <new_name>', 'Rename VDI'),
'vdi-delete': ('<vdi_uuid>', 'Delete VDI'),
'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'),
'vm-create': ('<pycfg>', 'Create VM with python config'),
'vm-destroy': ('<domname>', 'Delete VM'),
'vm-list': ('[--long]', 'List all domains.'),
'vm-name': ('<uuid>', 'Name of UUID.'),
'vm-shutdown': ('<name> [opts]', 'Shutdown VM with name'),
'vm-start': ('<name>', 'Start VM with name'),
'vm-uuid': ('<name>', 'UUID of a domain by name.'),
}
OPTIONS = {
'vm-list': [(('-l', '--long'),
{'action':'store_true',
'help':'List all properties of VMs'})
],
'vm-shutdown': [(('-f', '--force'), {'help': 'Shutdown Forcefully',
'action': 'store_true'})],
'vdi-create': [(('--name-label',), {'help': 'Name for VDI'}),
(('--description',), {'help': 'Description for VDI'}),
(('--sector-size',), {'type': 'int',
'help': 'Sector size'}),
(('--virtual-size',), {'type': 'int',
'help': 'Size of VDI in sectors'}),
(('--type',), {'choices': ['system', 'user', 'ephemeral'],
'help': 'VDI type'}),
(('--sharable',), {'action': 'store_true',
'help': 'VDI sharable'}),
(('--read-only',), {'action': 'store_true',
'help': 'Read only'})],
'vbd-create': [(('--VDI',), {'help': 'UUID of VDI to attach to.'}),
(('--mode',), {'choices': ['RO', 'RW'],
'help': 'device mount mode'}),
(('--driver',), {'choices':['paravirtualised', 'ioemu'],
'help': 'Driver for VBD'}),
(('--device',), {'help': 'Device name on guest domain'}),
(('--image',), {'help': 'Location of drive image.'})]
}
class OptionError(Exception):
pass
class XenAPIError(Exception):
pass
#
# Extra utility functions
#
class IterableValues(Values):
"""Better interface to the list of values from optparse."""
def __iter__(self):
for opt, val in self.__dict__.items():
if opt[0] == '_' or callable(val):
continue
yield opt, val
def parse_args(cmd_name, args, set_defaults = False):
argstring, desc = COMMANDS[cmd_name]
parser = OptionParser(usage = 'xapi %s %s' % (cmd_name, argstring),
description = desc)
if cmd_name in OPTIONS:
for optargs, optkwds in OPTIONS[cmd_name]:
parser.add_option(*optargs, **optkwds)
if set_defaults:
default_values = parser.get_default_values()
defaults = IterableValues(default_values.__dict__)
else:
defaults = IterableValues()
(opts, extraargs) = parser.parse_args(args = list(args),
values = defaults)
return opts, extraargs
def execute(fn, *args):
result = fn(*args)
if type(result) != DictType:
raise TypeError("Function returned object of type: %s" %
str(type(result)))
if 'Value' not in result:
raise XenAPIError(*result['ErrorDescription'])
return result['Value']
_initialised = False
_server = None
_session = None
def _connect(*args):
global _server, _session, _initialised
if not _initialised:
_server = ServerProxy('httpu:///var/run/xend/xmlrpc.sock')
login = raw_input("Login: ")
password = getpass()
creds = (login, password)
_session = execute(_server.session.login_with_password, *creds)
_initialised = True
return (_server, _session)
def _stringify(adict):
return dict([(k, str(v)) for k, v in adict.items()])
def _read_python_cfg(filename):
cfg = {}
execfile(filename, {}, cfg)
return cfg
def resolve_vm(server, session, vm_name):
vm_uuid = execute(server.VM.get_by_name_label, session, vm_name)
if not vm_uuid:
return None
else:
return vm_uuid[0]
#
# Actual commands
#
def xapi_host_info(*args):
server, session = _connect()
hosts = execute(server.host.get_all, session)
for host in hosts: # there is only one, but ..
hostinfo = execute(server.host.get_record, session, host)
print HOST_INFO_FORMAT % ('Name', hostinfo['name_label'])
print HOST_INFO_FORMAT % ('Version', hostinfo['software_version'])
print HOST_INFO_FORMAT % ('CPUs', len(hostinfo['host_CPUs']))
print HOST_INFO_FORMAT % ('VMs', len(hostinfo['resident_VMs']))
print HOST_INFO_FORMAT % ('UUID', host)
def xapi_vm_uuid(*args):
if len(args) < 1:
raise OptionError("No domain name specified")
server, session = _connect()
vm_uuid = resolve_vm(server, session, args[0])
print vm_uuid
def xapi_vm_name(*args):
if len(args) < 1:
raise OptionError("No UUID specified")
server, session = _connect()
vm_name = execute(server.VM.get_name_label, session, args[0])
print vm_name
def xapi_vm_list(*args):
opts, args = parse_args('vm-list', args, set_defaults = True)
is_long = opts and opts.long
server, session = _connect()
vm_uuids = execute(server.VM.get_all, session)
if not is_long:
print VM_LIST_FORMAT % {'name_label':'Name',
'memory_actual':'Mem',
'vcpus_number': 'VCPUs',
'power_state': 'State',
'uuid': 'UUID'}
for uuid in vm_uuids:
vm_info = execute(server.VM.get_record, session, uuid)
if is_long:
vbds = vm_info['vbds']
vifs = vm_info['vifs']
vif_infos = []
vbd_infos = []
for vbd in vbds:
vbd_info = execute(server.VBD.get_record, session, vbd)
vbd_infos.append(vbd_info)
for vif in vifs:
vif_info = execute(server.VIF.get_record, session, vif)
vif_infos.append(vif_info)
vm_info['vbds'] = vbd_infos
vm_info['vifs'] = vif_infos
pprint(vm_info)
else:
print VM_LIST_FORMAT % _stringify(vm_info)
def xapi_vm_create(*args):
if len(args) < 1:
raise OptionError("Configuration file not specified")
filename = args[0]
cfg = _read_python_cfg(filename)
print 'Creating VM from %s ..' % filename
server, session = _connect()
uuid = execute(server.VM.create, session, cfg)
print 'Done. (%s)' % uuid
print uuid
def xapi_vm_destroy(*args):
if len(args) < 1:
raise OptionError("No domain name specified.")
server, session = _connect()
vm_uuid = resolve_vm(server, session, args[0])
print 'Destroying VM %s (%s)' % (args[0], vm_uuid)
success = execute(server.VM.destroy, session, vm_uuid)
print 'Done.'
def xapi_vm_start(*args):
if len(args) < 1:
raise OptionError("No Domain name specified.")
server, session = _connect()
vm_uuid = resolve_vm(server, session, args[0])
print 'Starting VM %s (%s)' % (args[0], vm_uuid)
success = execute(server.VM.start, session, vm_uuid)
print 'Done.'
def xapi_vm_shutdown(*args):
opts, args = parse_args("vm-shutdown", args, set_defaults = True)
if len(args) < 1:
raise OptionError("No Domain name specified.")
server, session = _connect()
vm_uuid = resolve_vm(server, session, args[0])
if opts.force:
print 'Forcefully shutting down VM %s (%s)' % (args[0], vm_uuid)
success = execute(server.VM.hard_shutdown, session, vm_uuid)
else:
print 'Shutting down VM %s (%s)' % (args[0], vm_uuid)
success = execute(server.VM.clean_shutdown, session, vm_uuid)
print 'Done.'
def xapi_vbd_create(*args):
opts, args = parse_args('vbd-create', args)
if len(args) < 2:
raise OptionError("Configuration file and domain not specified")
domname = args[0]
if len(args) > 1:
filename = args[1]
cfg = _read_python_cfg(filename)
else:
cfg = {}
for opt, val in opts:
cfg[opt] = val
print 'Creating VBD ...',
server, session = _connect()
vm_uuid = resolve_vm(server, session, domname)
cfg['VM'] = vm_uuid
vbd_uuid = execute(server.VBD.create, session, cfg)
print 'Done. (%s)' % vbd_uuid
def xapi_vif_create(*args):
if len(args) < 2:
raise OptionError("Configuration file not specified")
domname = args[0]
filename = args[1]
cfg = _read_python_cfg(filename)
print 'Creating VIF from %s ..' % filename
server, session = _connect()
vm_uuid = resolve_vm(server, session, domname)
cfg['VM'] = vm_uuid
vif_uuid = execute(server.VIF.create, session, cfg)
print 'Done. (%s)' % vif_uuid
def xapi_vdi_list(*args):
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
def xapi_sr_list(*args):
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
def xapi_vdi_create(*args):
opts, args = parse_args('vdi-create', args)
if len(args) > 0:
cfg = _read_python_cfg(args[0])
else:
cfg = {}
for opt, val in opts:
cfg[opt] = val
server, session = _connect()
srs = execute(server.SR.get_all, session)
sr = srs[0]
cfg['SR'] = sr
size = (cfg['virtual_size'] * cfg['sector_size'])/MB
print 'Creating VDI of size: %dMB ..' % size,
uuid = execute(server.VDI.create, session, cfg)
print 'Done. (%s)' % uuid
def xapi_vdi_delete(*args):
server, session = _connect()
if len(args) < 1:
raise OptionError('Not enough arguments')
vdi_uuid = args[0]
print 'Deleting VDI %s' % vdi_uuid
result = execute(server.VDI.destroy, session, vdi_uuid)
print 'Done.'
def xapi_vdi_rename(*args):
server, session = _connect()
if len(args) < 2:
raise OptionError('Not enough arguments')
vdi_uuid = 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 'Done.'
#
# Command Line Utils
#
import cmd
import shlex
class XenAPICmd(cmd.Cmd):
def __init__(self, server, session):
cmd.Cmd.__init__(self)
self.server = server
self.session = session
self.prompt = ">>> "
def default(self, line):
words = shlex.split(line)
if len(words) > 0:
cmd_name = words[0].replace('-', '_')
func_name = 'xapi_%s' % cmd_name
func = globals().get(func_name)
if func:
try:
args = tuple(words[1:])
func(*args)
return True
except SystemExit:
return False
except OptionError, e:
print 'Error:', str(e)
return False
except Exception, e:
import traceback
traceback.print_exc()
return False
print '*** Unknown command: %s' % words[0]
return False
def do_EOF(self, line):
print
sys.exit(0)
def do_help(self, line):
usage(print_usage = False)
def emptyline(self):
pass
def postcmd(self, stop, line):
return False
def precmd(self, line):
words = shlex.split(line)
if len(words) > 0:
words0 = words[0].replace('-', '_')
return ' '.join([words0] + words[1:])
else:
return line
def shell():
server, session = _connect()
x = XenAPICmd(server, session)
x.cmdloop('Xen API Prompt. Type "help" for a list of functions')
def usage(command = None, print_usage = True):
if not command:
if print_usage:
print 'Usage: xapi <subcommand> [options] [args]'
print
print 'Subcommands:'
print
sorted_commands = sorted(COMMANDS.keys())
for command in sorted_commands:
args, description = COMMANDS[command]
print '%-16s %-40s' % (command, description)
print
else:
parse_args(command, ['-h'])
def main(args):
if len(args) < 1 or args[0] in ('-h', '--help', 'help'):
usage()
sys.exit(1)
subcmd = args[0]
subcmd_func_name = 'xapi_' + subcmd.replace('-', '_')
subcmd_func = globals().get(subcmd_func_name, None)
if subcmd == 'shell':
shell()
elif not subcmd_func or not callable(subcmd_func):
print 'Error: Unable to find subcommand \'%s\'' % subcmd
usage()
sys.exit(1)
if '-h' in args[1:] or '--help' in args[1:]:
usage(subcmd)
sys.exit(1)
try:
subcmd_func(*args[1:])
except XenAPIError, e:
print 'Error: %s' % str(e.args[1])
sys.exit(2)
except OptionError, e:
print 'Error: %s' % e
sys.exit(0)
if __name__ == "__main__":
import sys
main(sys.argv[1:])
+12
View File
@@ -0,0 +1,12 @@
#
# Virtual Block Device (VBD) Xen API Configuration
#
# Note: There is a non-API field here called "image" which is a backwards
# compat addition so you can mount to old images.
#
VDI = ''
device = 'sda1'
mode = 'RW'
driver = 'paravirtualised'
image = 'file:/root/gentoo.amd64.img'
+7
View File
@@ -0,0 +1,7 @@
name_label = 'VDI 1'
name_description = ''
virtual_size = 10 * 1024
sector_size = 1024
type = 'system'
sharable = False
read_only = False
+10
View File
@@ -0,0 +1,10 @@
#
# Virtual Network Interface Configuration for the Xen API
#
name = ''
type = 'paravirtualised'
#device = 'eth0' # this is the dom0 device, not domU!
network = '' # ignored
MAC = ''
MTU = '1500'
+38 -18
View File
@@ -73,7 +73,7 @@ static PyObject *pyxc_domain_create(XcObject *self,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef };
static char *kwd_list[] = { "dom", "ssidref", "handle", NULL };
static char *kwd_list[] = { "domid", "ssidref", "handle", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "|iiO", kwd_list,
&dom, &ssidref, &pyhandle))
@@ -144,7 +144,7 @@ static PyObject *pyxc_vcpu_setaffinity(XcObject *self,
uint64_t cpumap = ~0ULL;
PyObject *cpulist = NULL;
static char *kwd_list[] = { "dom", "vcpu", "cpumap", NULL };
static char *kwd_list[] = { "domid", "vcpu", "cpumap", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|iO", kwd_list,
&dom, &vcpu, &cpulist) )
@@ -171,7 +171,7 @@ static PyObject *pyxc_domain_setcpuweight(XcObject *self,
uint32_t dom;
float cpuweight = 1;
static char *kwd_list[] = { "dom", "cpuweight", NULL };
static char *kwd_list[] = { "domid", "cpuweight", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|f", kwd_list,
&dom, &cpuweight) )
@@ -256,7 +256,7 @@ static PyObject *pyxc_domain_getinfo(XcObject *self,
PyList_SetItem(pyhandle, j, PyInt_FromLong(info[i].handle[j]));
info_dict = Py_BuildValue("{s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i,s:i"
",s:l,s:L,s:l,s:i,s:i}",
"dom", info[i].domid,
"domid", info[i].domid,
"online_vcpus", info[i].nr_online_vcpus,
"max_vcpu_id", info[i].max_vcpu_id,
"dying", info[i].dying,
@@ -291,7 +291,7 @@ static PyObject *pyxc_vcpu_getinfo(XcObject *self,
int rc, i;
uint64_t cpumap;
static char *kwd_list[] = { "dom", "vcpu", NULL };
static char *kwd_list[] = { "domid", "vcpu", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i|i", kwd_list,
&dom, &vcpu) )
@@ -334,7 +334,7 @@ static PyObject *pyxc_linux_build(XcObject *self,
unsigned long store_mfn = 0;
unsigned long console_mfn = 0;
static char *kwd_list[] = { "dom", "store_evtchn",
static char *kwd_list[] = { "domid", "store_evtchn",
"console_evtchn", "image",
/* optional */
"ramdisk", "cmdline", "flags",
@@ -375,9 +375,9 @@ static PyObject *pyxc_hvm_build(XcObject *self,
int apic = 0;
unsigned long store_mfn = 0;
static char *kwd_list[] = { "dom", "store_evtchn", "memsize", "image",
"vcpus", "pae", "acpi", "apic",
NULL };
static char *kwd_list[] = { "domid", "store_evtchn",
"memsize", "image", "vcpus", "pae", "acpi", "apic",
NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiisiiii", kwd_list,
&dom, &store_evtchn, &memsize,
&image, &vcpus, &pae, &acpi, &apic) )
@@ -397,7 +397,7 @@ static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self,
uint32_t dom, remote_dom;
int port;
static char *kwd_list[] = { "dom", "remote_dom", NULL };
static char *kwd_list[] = { "domid", "remote_dom", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "ii", kwd_list,
&dom, &remote_dom) )
@@ -416,7 +416,7 @@ static PyObject *pyxc_physdev_pci_access_modify(XcObject *self,
uint32_t dom;
int bus, dev, func, enable, ret;
static char *kwd_list[] = { "dom", "bus", "dev", "func", "enable", NULL };
static char *kwd_list[] = { "domid", "bus", "dev", "func", "enable", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiiii", kwd_list,
&dom, &bus, &dev, &func, &enable) )
@@ -557,7 +557,7 @@ static PyObject *pyxc_sedf_domain_set(XcObject *self,
uint32_t domid;
uint64_t period, slice, latency;
uint16_t extratime, weight;
static char *kwd_list[] = { "dom", "period", "slice",
static char *kwd_list[] = { "domid", "period", "slice",
"latency", "extratime", "weight",NULL };
if( !PyArg_ParseTupleAndKeywords(args, kwds, "iLLLhh", kwd_list,
@@ -586,7 +586,7 @@ static PyObject *pyxc_sedf_domain_get(XcObject *self, PyObject *args)
return PyErr_SetFromErrno(xc_error);
return Py_BuildValue("{s:i,s:L,s:L,s:L,s:i,s:i}",
"domain", domid,
"domid", domid,
"period", period,
"slice", slice,
"latency", latency,
@@ -647,6 +647,15 @@ static PyObject *pyxc_shadow_mem_control(PyObject *self,
return Py_BuildValue("i", mbarg);
}
static PyObject *pyxc_sched_id_get(XcObject *self) {
int sched_id;
if (xc_sched_id(self->xc_handle, &sched_id) != 0)
return PyErr_SetFromErrno(xc_error);
return Py_BuildValue("i", sched_id);
}
static PyObject *pyxc_sched_credit_domain_set(XcObject *self,
PyObject *args,
PyObject *kwds)
@@ -654,7 +663,7 @@ static PyObject *pyxc_sched_credit_domain_set(XcObject *self,
uint32_t domid;
uint16_t weight;
uint16_t cap;
static char *kwd_list[] = { "dom", "weight", "cap", NULL };
static char *kwd_list[] = { "domid", "weight", "cap", NULL };
static char kwd_type[] = "I|HH";
struct xen_domctl_sched_credit sdom;
@@ -714,7 +723,7 @@ static PyObject *pyxc_domain_memory_increase_reservation(XcObject *self,
unsigned int extent_order = 0 , address_bits = 0;
unsigned long nr_extents;
static char *kwd_list[] = { "dom", "mem_kb", "extent_order", "address_bits", NULL };
static char *kwd_list[] = { "domid", "mem_kb", "extent_order", "address_bits", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "il|ii", kwd_list,
&dom, &mem_kb, &extent_order, &address_bits) )
@@ -739,7 +748,7 @@ static PyObject *pyxc_domain_ioport_permission(XcObject *self,
uint32_t dom;
int first_port, nr_ports, allow_access, ret;
static char *kwd_list[] = { "dom", "first_port", "nr_ports", "allow_access", NULL };
static char *kwd_list[] = { "domid", "first_port", "nr_ports", "allow_access", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iiii", kwd_list,
&dom, &first_port, &nr_ports, &allow_access) )
@@ -762,7 +771,7 @@ static PyObject *pyxc_domain_irq_permission(PyObject *self,
uint32_t dom;
int pirq, allow_access, ret;
static char *kwd_list[] = { "dom", "pirq", "allow_access", NULL };
static char *kwd_list[] = { "domid", "pirq", "allow_access", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "iii", kwd_list,
&dom, &pirq, &allow_access) )
@@ -785,7 +794,7 @@ static PyObject *pyxc_domain_iomem_permission(PyObject *self,
uint32_t dom;
unsigned long first_pfn, nr_pfns, allow_access, ret;
static char *kwd_list[] = { "dom", "first_pfn", "nr_pfns", "allow_access", NULL };
static char *kwd_list[] = { "domid", "first_pfn", "nr_pfns", "allow_access", NULL };
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "illi", kwd_list,
&dom, &first_pfn, &nr_pfns, &allow_access) )
@@ -976,6 +985,12 @@ static PyMethodDef pyxc_methods[] = {
" vcpus [int, 1]: Number of Virtual CPUS in domain.\n\n"
"Returns: [int] 0 on success; -1 on error.\n" },
{ "sched_id_get",
(PyCFunction)pyxc_sched_id_get,
METH_NOARGS, "\n"
"Get the current scheduler type in use.\n"
"Returns: [int] sched_id.\n" },
{ "sedf_domain_set",
(PyCFunction)pyxc_sedf_domain_set,
METH_KEYWORDS, "\n"
@@ -1242,6 +1257,11 @@ PyMODINIT_FUNC initxc(void)
Py_INCREF(xc_error);
PyModule_AddObject(m, "Error", xc_error);
/* Expose some libxc constants to Python */
PyModule_AddIntConstant(m, "XEN_SCHEDULER_SEDF", XEN_SCHEDULER_SEDF);
PyModule_AddIntConstant(m, "XEN_SCHEDULER_CREDIT", XEN_SCHEDULER_CREDIT);
}
-1
View File
@@ -21,7 +21,6 @@ def blkdev_name_to_number(name):
try:
return os.stat(n).st_rdev
except Exception, ex:
log.debug("exception looking up device number for %s: %s", name, ex)
pass
if re.match( '/dev/sd[a-p]([1-9]|1[0-5])?', n):
+26 -10
View File
@@ -21,11 +21,11 @@ An enhanced XML-RPC client/server interface for Python.
"""
import string
import types
import fcntl
from types import *
from httplib import HTTPConnection, HTTP
from xmlrpclib import Transport
from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
import SocketServer
import xmlrpclib, socket, os, stat
@@ -40,6 +40,23 @@ except ImportError:
# package.
ssh_enabled = False
#
# Convert all integers to strings as described in the Xen API
#
def stringify(value):
if isinstance(value, IntType) and not isinstance(value, BooleanType):
return str(value)
elif isinstance(value, DictType):
for k, v in value.items():
value[k] = stringify(v)
return value
elif isinstance(value, (TupleType, ListType)):
return [stringify(v) for v in value]
else:
return value
# A new ServerProxy that also supports httpu urls. An http URL comes in the
# form:
@@ -81,18 +98,18 @@ class HTTPUnixConnection(HTTPConnection):
class HTTPUnix(HTTP):
_connection_class = HTTPUnixConnection
class UnixTransport(Transport):
class UnixTransport(xmlrpclib.Transport):
def request(self, host, handler, request_body, verbose=0):
self.__handler = handler
return Transport.request(self, host, '/RPC2', request_body, verbose)
return xmlrpclib.Transport.request(self, host, '/RPC2',
request_body, verbose)
def make_connection(self, host):
return HTTPUnix(self.__handler)
# See _marshalled_dispatch below.
def conv_string(x):
if (isinstance(x, types.StringType) or
isinstance(x, unicode)):
if isinstance(x, StringTypes):
s = string.replace(x, "'", r"\047")
exec "s = '" + s + "'"
return s
@@ -134,7 +151,7 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
allow_reuse_address = True
def __init__(self, addr, requestHandler=XMLRPCRequestHandler,
logRequests=1):
logRequests = 1):
SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests)
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
@@ -169,8 +186,7 @@ class TCPXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer):
# to transmit the string using Python encoding.
# Thanks to David Mertz <mertz@gnosis.cx> for the trick (buried
# in xml_pickle.py).
if (isinstance(response, types.StringType) or
isinstance(response, unicode)):
if isinstance(response, StringTypes):
response = repr(response)[1:-1]
response = (response,)
@@ -201,7 +217,7 @@ class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
class UnixXMLRPCServer(TCPXMLRPCServer):
address_family = socket.AF_UNIX
def __init__(self, addr, logRequests):
def __init__(self, addr, logRequests = 1):
parent = os.path.dirname(addr)
if os.path.exists(parent):
os.chown(parent, os.geteuid(), os.getegid())
+1 -1
View File
@@ -18,7 +18,7 @@
import types
import StringIO
import sxp
from xen.xend import sxp
class ArgError(StandardError):
pass
+1 -1
View File
@@ -22,7 +22,7 @@
import sys
import types
import StringIO
import sxp
from xen.xend import sxp
class PrettyItem:
File diff suppressed because it is too large. Load diff
+75
View File
@@ -0,0 +1,75 @@
#============================================================================
# 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 Ltd.
#============================================================================
#
# Xen API Enums
#
XEN_API_VM_POWER_STATE = [
'Halted',
'Paused',
'Running',
'Suspended',
'ShuttingDown',
'Unknown'
]
XEN_API_VM_POWER_STATE_HALTED = 0
XEN_API_VM_POWER_STATE_PAUSED = 1
XEN_API_VM_POWER_STATE_RUNNING = 2
XEN_API_VM_POWER_STATE_SUSPENDED = 3
XEN_API_VM_POWER_STATE_SHUTTINGDOWN = 4
XEN_API_VM_POWER_STATE_UNKNOWN = 5
XEN_API_CPU_FEATURE = [
'FPU', 'VME', 'DE', 'PSE', 'TSC', 'MSR', 'PAE'
'MCE', 'CX8', 'APIC', 'SEP', 'MTRR', 'PGE', 'MCA',
'CMOV', 'PAT', 'PSE36', 'PN', 'CLFLSH', 'DTES',
'ACPI', 'MMX', 'FXCR', 'XMM', 'XMM2', 'SELFSNOOP',
'HT', 'ACC', 'IA64', 'SYSCALL', 'MP', 'NX', 'MMXEXT',
'LM', '3DNOWEXT', '3DNOW', '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',
'XTPR', 'XSTORE', 'XSTORE_EN', 'XCRYPT', 'XCRYPT_EN',
'LAHF_LM', 'CMP_LEGACY'
]
XEN_API_ON_NORMAL_EXIT = [
'destroy',
'restart',
]
XEN_API_ON_CRASH_BEHAVIOUR = [
'destroy',
'coredump_and_destroy',
'restart',
'coredump_and_restart',
'preserve',
'rename_restart'
]
XEN_API_BOOT_TYPE = [
'bios',
'grub',
'kernel_external',
'kernel_internal'
]
XEN_API_VBD_MODE = ['RO', 'RW']
XEN_API_VDI_TYPE = ['system', 'user', 'ephemeral']
XEN_API_DRIVER_TYPE = ['ioemu', 'paravirtualised']
+137
View File
@@ -0,0 +1,137 @@
#============================================================================
# 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 Ltd.
#============================================================================
import time
from xen.xend import uuid
from xen.xend.XendError import *
from xen.xend.XendLogging import log
try:
import PAM
except ImportError:
log.warn("python-pam is required for XenAPI support.")
class XendAuthSessions:
"""Keeps track of Xen API Login Sessions using PAM.
Note: Login sessions are not valid across instances of Xend.
"""
def __init__(self):
self.sessions = {}
def init(self):
pass
def login_with_password(self, username, password):
"""Returns a session UUID if valid, otherwise raises an error.
@raises XendError: If login fails.
@rtype: string
@return: Session UUID
"""
if self.is_authorized(username, password):
new_session = uuid.createString()
self.sessions[new_session] = (username, time.time())
return new_session
raise XendError("Login failed")
def logout(self, session):
"""Delete session of it exists."""
if self.is_session_valid(session):
del self.sessions[session]
def is_session_valid(self, session):
"""Returns true is session is valid."""
if type(session) == type(str()):
return (session in self.sessions)
return False
def is_authorized(self, username, password):
"""Returns true is a user is authorised via PAM.
Note: We use the 'login' PAM stack rather than inventing
our own.
@rtype: boolean
"""
pam_auth = None
try:
pam_auth = PAM.pam()
except NameError:
# if PAM doesn't exist, let's ignore it
return False
pam_auth.start("login")
pam_auth.set_item(PAM.PAM_USER, username)
def _pam_conv(auth, query_list, user_data):
resp = []
for i in range(len(query_list)):
query, qtype = query_list[i]
if qtype == PAM.PAM_PROMPT_ECHO_ON:
resp.append((username, 0))
elif qtype == PAM.PAM_PROMPT_ECHO_OFF:
resp.append((password, 0))
else:
return None
return resp
pam_auth.set_item(PAM.PAM_CONV, _pam_conv)
try:
pam_auth.authenticate()
pam_auth.acct_mgmt()
except PAM.error, resp:
return False
except Exception, e:
log.warn("Error with PAM: %s" % str(e))
return False
else:
return True
def get_user(self, session):
try:
return self.sessions[session][0]
except (KeyError, IndexError):
return None
def instance():
"""Singleton constructor. Use this instead of the class constructor.
"""
global inst
try:
inst
except:
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
+3 -3
View File
@@ -14,8 +14,8 @@
import os, select, errno
import random
import sxp
import shlex
from xen.xend import sxp
from XendLogging import log
from XendError import VmError
@@ -38,7 +38,7 @@ def bootloader(blexec, disk, quiet = 0, blargs = None, imgcfg = None):
raise VmError(msg)
while True:
fifo = "/var/lib/xen/xenbl.%s" %(random.randint(0, 32000),)
fifo = "/var/lib/xen/xenbl.%s" % random.randint(0, 32000)
if not os.path.exists(fifo):
break
os.mkfifo(fifo, 0600)
@@ -48,7 +48,7 @@ def bootloader(blexec, disk, quiet = 0, blargs = None, imgcfg = None):
args = [ blexec ]
if quiet:
args.append("-q")
args.append("--output=%s" %(fifo,))
args.append("--output=%s" % fifo)
if blargs is not None:
args.extend(shlex.split(blargs))
args.append(disk)
+21 -19
View File
@@ -8,21 +8,17 @@
import os
import re
import string
import sxp
import threading
from struct import pack, unpack, calcsize
from xen.util.xpopen import xPopen3
import xen.util.auxbin
import xen.lowlevel.xc
import balloon
from XendError import XendError
from XendLogging import log
from XendDomainInfo import DEV_MIGRATE_STEP1, DEV_MIGRATE_STEP2
from XendDomainInfo import DEV_MIGRATE_STEP3
from xen.xend import balloon, sxp
from xen.xend.XendError import XendError
from xen.xend.XendLogging import log
from xen.xend.XendConstants import *
SIGNATURE = "LinuxGuestRecord"
XC_SAVE = "xc_save"
@@ -43,13 +39,13 @@ def write_exact(fd, buf, errmsg):
def read_exact(fd, size, errmsg):
buf = ''
while size != 0:
str = os.read(fd, size)
if not len(str):
readstr = os.read(fd, size)
if not len(readstr):
log.error("read_exact: EOF trying to read %d (buf='%s')" % \
(size, buf))
raise XendError(errmsg)
size = size - len(str)
buf = buf + str
size = size - len(readstr)
buf = buf + readstr
return buf
@@ -63,7 +59,9 @@ def save(fd, dominfo, network, live, dst):
# Rename the domain temporarily, so that we don't get a name clash if this
# domain is migrating (live or non-live) to the local host. Doing such a
# thing is useful for debugging.
dominfo.setName('migrating-' + domain_name)
#
# FIXME: I don't think this is such a good idea - atse@xensource.com
#dominfo.setName('migrating-' + domain_name)
try:
dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP1, domain_name)
@@ -104,14 +102,14 @@ def save(fd, dominfo, network, live, dst):
except Exception, exn:
log.exception("Save failed on domain %s (%d).", domain_name,
dominfo.getDomid())
try:
dominfo.setName(domain_name)
except:
log.exception("Failed to reset the migrating domain's name")
#try:
# dominfo.setName(domain_name)
#except:
# log.exception("Failed to reset the migrating domain's name")
raise Exception, exn
def restore(xd, fd):
def restore(xd, fd, dominfo = None):
signature = read_exact(fd, len(SIGNATURE),
"not a valid guest state file: signature read")
if signature != SIGNATURE:
@@ -131,7 +129,11 @@ def restore(xd, fd):
vmconfig = p.get_val()
dominfo = xd.restore_(vmconfig)
if dominfo:
dominfo.update(XendConfig(sxp = vmconfig), refresh = False)
dominfo.resume()
else:
dominfo = xd.restore_(vmconfig)
store_port = dominfo.getStorePort()
console_port = dominfo.getConsolePort()
+884
View File
@@ -0,0 +1,884 @@
#===========================================================================
# 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 Ltd
#============================================================================
import re
import time
from xen.xend import sxp
from xen.xend import uuid
from xen.xend.XendError import VmError
from xen.xend.XendDevices import XendDevices
from xen.xend.XendLogging import log
from xen.xend.PrettyPrint import prettyprintstring
"""
XendConfig API
XendConfig will try to mirror as closely the Xen API VM Struct
providing a backwards compatibility mode for SXP dumping, loading.
XendConfig is a subclass of the python dict in order to emulate the
previous behaviour of the XendDomainInfo.info dictionary. However,
the new dictionary also exposes a set of attributes that implement
the Xen API VM configuration interface.
Example:
>>> cfg = XendConfig(cfg = dict_from_xc_domain_getinfo)
>>> cfg.name_label
Domain-0
>>> cfg['name']
Domain-0
>>> cfg.kernel_kernel
/boot/vmlinuz-xen
>>> cfg.kernel_initrd
/root/initrd
>>> cfg.kernel_args
root=/dev/sda1 ro
>>> cfg['image']
(linux
(kernel /boot/vmlinuz-xen)
(ramdisk /root/initrd)
(root '/dev/sda1 ro'))
>>>
Internally, XendConfig will make sure changes via the old 'dict'
interface get reflected, if possible, to the attribute store.
It does this by overriding __setitem__, __getitem__, __hasitem__,
__getattr__, __setattr__, __hasattr__.
What this means is that as code is moved from the SXP interface to
the Xen API interface, we can spot unported code by tracing calls
to __getitem__ and __setitem__.
"""
LEGACY_CFG_TO_XENAPI_CFG = {
'uuid': 'uuid',
'vcpus': 'vcpus_number',
'maxmem': 'memory_static_max',
'memory': 'memory_static_min',
'name': 'name_label',
'on_poweroff': 'actions_after_shutdown',
'on_reboot': 'actions_after_reboot',
'on_crash': 'actions_after_crash',
'bootloader': 'boot_method',
'kernel_kernel': 'kernel_kernel',
'kernel_initrd': 'kernel_initrd',
'kernel_args': 'kernel_args',
}
XENAPI_CFG_CUSTOM_TRANSLATE = [
'vifs',
'vbds',
]
XENAPI_HVM_CFG = {
'platform_std_vga': 'std-vga',
'platform_serial' : 'serial',
'platform_localtime': 'localtime',
'platform_enable_audio': 'soundhw',
}
XENAPI_UNSUPPORTED_IN_LEGACY_CFG = [
'name_description',
'user_version',
'is_a_template',
'memory_dynamic_min',
'memory_dynamic_max',
'memory_actual',
'vcpus_policy',
'vcpus_params',
'vcpus_features_required',
'vcpus_features_can_use',
'vcpus_features_force_on',
'vcpus_features_force_off',
'actions_after_suspend',
'bios_boot',
'platform_std_vga',
'platform_serial',
'platform_localtime',
'platform_clock_offset',
'platform_enable_audio',
'builder',
'grub_cmdline',
'pci_bus',
'otherconfig'
]
# configuration params that need to be converted to ints
# since the XMLRPC transport for Xen API does not use
# 32 bit ints but string representation of 64 bit ints.
XENAPI_INT_CFG = [
'user_version',
'vcpus_number',
'memory_static_min',
'memory_static_max',
'memory_dynamic_min',
'memory_dynamic_max',
'tpm_instance',
'tpm_backend',
]
##
## Xend Configuration Parameters
##
# All parameters of VMs that may be configured on-the-fly, or at start-up.
VM_CONFIG_ENTRIES = [
('name', str),
('on_crash', str),
('on_poweroff', str),
('on_reboot', str),
('on_xend_start', str),
('on_xend_stop', str),
]
# All entries written to the store. This is VM_CONFIG_ENTRIES, plus those
# entries written to the store that cannot be reconfigured on-the-fly.
VM_STORE_ENTRIES = [
('uuid', str),
('vcpus', int),
('vcpu_avail', int),
('memory', int),
('maxmem', int),
('start_time', float),
]
VM_STORED_ENTRIES = VM_CONFIG_ENTRIES + VM_STORE_ENTRIES
# Configuration entries that we expect to round-trip -- be read from the
# config file or xc, written to save-files (i.e. through sxpr), and reused as
# config on restart or restore, all without munging. Some configuration
# entries are munged for backwards compatibility reasons, or because they
# don't come out of xc in the same form as they are specified in the config
# file, so those are handled separately.
ROUNDTRIPPING_CONFIG_ENTRIES = [
('uuid', str),
('vcpus', int),
('vcpu_avail', int),
('cpu_weight', float),
('memory', int),
('shadow_memory', int),
('maxmem', int),
('bootloader', str),
('bootloader_args', str),
('features', str),
('localtime', int),
]
ROUNDTRIPPING_CONFIG_ENTRIES += VM_CONFIG_ENTRIES
## Static Configuration
STATIC_CONFIG_ENTRIES = [
('cpu', int),
('cpus', str),
('image', list),
('security', list), # TODO: what if null?
]
DEPRECATED_ENTRIES = [
('restart', str),
]
##
## Config Choices
##
CONFIG_RESTART_MODES = ('restart', 'destroy', 'preserve', 'rename-restart')
CONFIG_OLD_DOM_STATES = ('running', 'blocked', 'paused', 'shutdown',
'crashed', 'dying')
##
## Defaults
##
def DEFAULT_VCPUS(info):
if 'max_vcpu_id' in info: return int(info['max_vcpu_id']) + 1
else: return 1
DEFAULT_CONFIGURATION = (
('uuid', lambda info: uuid.createString()),
('name', lambda info: 'Domain-' + info['uuid']),
('on_poweroff', lambda info: 'destroy'),
('on_reboot', lambda info: 'restart'),
('on_crash', lambda info: 'restart'),
('features', lambda info: ''),
('memory', lambda info: 0),
('shadow_memory',lambda info: 0),
('maxmem', lambda info: 0),
('bootloader', lambda info: None),
('bootloader_args', lambda info: None),
('backend', lambda info: []),
('device', lambda info: {}),
('image', lambda info: None),
('security', lambda info: []),
('on_xend_start', lambda info: 'ignore'),
('on_xend_stop', lambda info: 'ignore'),
('cpus', lambda info: []),
('cpu_cap', lambda info: 0),
('cpu_weight', lambda info: 256),
('vcpus', lambda info: DEFAULT_VCPUS(info)),
('online_vcpus', lambda info: info['vcpus']),
('max_vcpu_id', lambda info: info['vcpus']-1),
('vcpu_avail', lambda info: (1<<info['vcpus'])-1),
# New for Xen API
('kernel_kernel', lambda info: ''),
('kernel_initrd', lambda info: ''),
('kernel_args', lambda info: ''),
)
class XendConfigError(VmError):
def __str__(self):
return 'Invalid Configuration: %s' % str(self.value)
##
## XendConfig SXP Config Compat
##
class XendSXPConfig:
def get_domid(self):
pass
def get_handle(self):
return self['uuid']
##
## XendConfig Class (an extended dictionary)
##
class XendConfig(dict):
""" Generic Configuration Parser accepting SXP, Python or XML.
This is a dictionary-like object that is populated.
@ivar legacy: dictionary holding legacy xen domain info
@ivar xenapi: dictionary holding xen api config info
"""
def __init__(self, filename = None, fd = None,
sxp = None, xml = None, pycfg = None, xenapi_vm = None,
cfg = {}):
"""Constructor. Provide either the filename, fd or sxp.
@keyword filename: filename of an SXP file
@keyword fd: file descriptor of an SXP file
@keyword sxp: a list of list of a parsed SXP
@keyword xml: an XML tree object
@keyword xenapi_vm: a struct passed from an XMLRPC call (Xen API)
@keyword cfg: a dictionary of configuration (eg. from xc)
"""
format = 'unknown'
self.xenapi = {}
if filename and not fd:
fd = open(filename, 'r')
if fd:
format = self._detect_format(fd)
if fd:
if format == 'sxp':
sxp = self._read_sxp(fd)
elif format == 'python' and filename != None:
pycfg = self._read_python(filename)
elif format == 'python' and filename == None:
raise XendConfigError("Python files must be passed as a "
"filename rather than file descriptor.")
elif format == 'xml':
xml = self._read_xml(fd)
else:
raise XendConfigError("Unable to determine format of file")
if sxp:
cfg = self._populate_from_sxp(sxp)
if xml:
cfg = self._populate_from_xml(xml)
if pycfg:
cfg = self._populate_from_python_config(pycfg)
if xenapi_vm:
cfg = self._populate_from_xenapi_vm(xenapi_vm)
if cfg:
self.update(cfg)
if xenapi_vm:
self.xenapi.update(xenapi_vm)
log.debug('XendConfig: %s' % str(self))
self.validate()
#
# Xen API Attribute Access
#
def __getattr__(self, name):
try:
return dict.__getattr__(self, name)
except AttributeError:
try:
return self.__dict__['xenapi'][name]
except KeyError:
raise AttributeError("XendConfig Xen API has no attribute "
"'%s'" % name)
def __setattr__(self, name, value):
try:
return dict.__setattr__(self, name, value)
except AttributeError:
self.xenapi[name] = value
#self.set_legacy_api_with_xen_api_value(name, value)
def __delattr__(self, name):
try:
dict.__delattr__(self, name)
except AttributeError:
del self.xenapi[name]
#self.del_legacy_api_with_xen_api_key(name)
"""
#
# Legacy API Attribute Access
#
def __getitem__(self, key):
try:
return self.legacy[key]
except KeyError:
raise AttributeError, "XendConfig Legacy has no attribute '%s'"\
% key
def __setitem__(self, key, value):
self.legacy[key] = value
self.set_xen_api_with_legacy_api_value(key, value)
def __delitem__(self, key):
del self.legacy[key]
self.del_xen_api_with_legacy_api_key(key)
"""
def _detect_format(self, fd):
"""Detect the format of the configuration passed.
@param fd: file descriptor of contents to detect
@rtype: string, 'sxp', 'xml', 'python' or 'unknown'
"""
format = 'unknown'
fd.seek(0)
for line in fd:
stripped = line.strip()
if stripped:
if re.search(r'^\(', stripped):
format = 'sxp'
elif re.search(r'^\<?xml', stripped):
format = 'xml'
else:
format = 'python'
break
fd.seek(0)
return format
def _read_sxp(self, fd):
""" Read and parse SXP (from SXP to list of lists)
@rtype: list of lists.
"""
try:
parsed = sxp.parse(fd)[0]
return parsed
except:
raise
return None
def _read_xml(self, fd):
"""TODO: Read and parse XML (from XML to dict)
@rtype: dict
"""
raise NotImplementedError
def _read_python(self, filename):
"""Read and parse python module that represents the config.
@rtype: dict
"""
cfg_globals = {}
execfile(filename, cfg_globals, {})
return cfg_globals
def _populate_from_sxp(self, parsed):
""" Populate this XendConfig using the parsed SXP.
@rtype: dictionary
"""
cfg = {}
# First step is to convert deprecated options to
# current equivalents.
restart = sxp.child_value(parsed, 'restart')
if restart:
if restart == 'onreboot':
cfg['on_poweroff'] = 'destroy'
cfg['on_reboot'] = 'restart'
cfg['on_crash'] = 'destroy'
elif restart == 'always':
for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
cfg[opt] = 'restart'
elif restart == 'never':
for opt in ('on_poweroff', 'on_reboot', 'on_crash'):
cfg[opt] = 'never'
else:
log.warn('Ignoring unrecognised value for deprecated option:'
'restart = \'%s\'', restart)
# Only extract options we know about.
all_params = VM_CONFIG_ENTRIES + ROUNDTRIPPING_CONFIG_ENTRIES + \
STATIC_CONFIG_ENTRIES
for key, typeconv in all_params:
val = sxp.child_value(parsed, key)
if val:
try:
cfg[key] = typeconv(val)
except ValueError:
pass
# Manually extract other complex configuration
# options.
cfg['backend'] = []
for c in sxp.children(parsed, 'backend'):
cfg['backend'].append(sxp.name(sxp.child0(c)))
cfg['device'] = {}
for dev in sxp.children(parsed, 'device'):
config = sxp.child0(dev)
dev_type = sxp.name(config)
dev_info = {}
for opt, val in config[1:]:
dev_info[opt] = val
log.debug("XendConfig: reading device: %s" % dev_info)
# create uuid if it doesn't
dev_uuid = dev_info.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
cfg['device'][dev_uuid] = (dev_type, dev_info)
#cfg['device'].append((sxp.name(config), config))
# Extract missing data from configuration entries
if 'image' in cfg:
image_vcpus = sxp.child_value(cfg['image'], 'vcpus')
if image_vcpus is not None:
try:
if 'vcpus' not in cfg:
cfg['vcpus'] = int(image_vcpus)
elif cfg['vcpus'] != int(image_vcpus):
cfg['vcpus'] = int(image_vcpus)
log.warn('Overriding vcpus from %d to %d using image'
'vcpus value.', cfg['vcpus'])
except ValueError, e:
raise XendConfigError('integer expeceted: %s: %s' %
str(cfg['image']), e)
# Deprecated cpu configuration
if 'cpu' in cfg:
if 'cpus' in cfg:
cfg['cpus'] = "%s,%s" % (str(cfg['cpu']), cfg['cpus'])
else:
cfg['cpus'] = str(cfg['cpu'])
# convert 'cpus' string to list of ints
# 'cpus' supports a list of ranges (0-3), seperated by
# commas, and negation, (^1).
# Precedence is settled by order of the string:
# "0-3,^1" -> [0,2,3]
# "0-3,^1,1" -> [0,1,2,3]
try:
if 'cpus' in cfg:
cpus = []
for c in cfg['cpus'].split(','):
if c.find('-') != -1:
(x, y) = c.split('-')
for i in range(int(x), int(y)+1):
cpus.append(int(i))
else:
# remove this element from the list
if c[0] == '^':
cpus = [x for x in cpus if x != int(c[1:])]
else:
cpus.append(int(c))
cfg['cpus'] = cpus
except ValueError, e:
raise XendConfigError('cpus = %s: %s' % (cfg['cpus'], e))
# Parse image SXP outside of image.py
# - used to be only done in image.py
if 'image' in cfg:
cfg['kernel_kernel'] = sxp.child_value(cfg['image'], 'kernel','')
cfg['kernel_initrd'] = sxp.child_value(cfg['image'], 'ramdisk','')
kernel_args = sxp.child_value(cfg['image'], 'args', '')
# attempt to extract extra arguments from SXP config
arg_ip = sxp.child_value(cfg['image'], 'ip')
if arg_ip: kernel_args += ' ip=%s' % arg_ip
arg_root = sxp.child_value(cfg['image'], 'root')
if arg_root: kernel_args += ' root=%s' % arg_root
cfg['kernel_args'] = kernel_args
# TODO: get states
old_state = sxp.child_value(parsed, 'state')
if old_state:
for i in range(len(CONFIG_OLD_DOM_STATES)):
cfg[CONFIG_OLD_DOM_STATES[i]] = (old_state[i] != '-')
# Xen API extra cfgs
# ------------------
cfg['vif_refs'] = []
cfg['vbd_refs'] = []
cfg['vtpm_refs'] = []
for dev_uuid, (dev_type, dev_info) in cfg['device'].items():
if dev_type == 'vif':
cfg['vif_refs'].append(dev_uuid)
elif dev_type in ('vbd','tap'):
cfg['vbd_refs'].append(dev_uuid)
elif dev_type == 'vtpm':
cfg['vtpm_refs'].append(dev_uuid)
return cfg
def _populate_from_xenapi_vm(self, xenapi_vm):
cfg = {}
for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
try:
if apikey in XENAPI_INT_CFG:
cfg[cfgkey] = int(xenapi_vm[apikey])
else:
cfg[cfgkey] = xenapi_vm[apikey]
except KeyError:
pass
# Reconstruct image SXP
# TODO: get rid of SXP altogether from here
sxp_image = ['linux']
if xenapi_vm['kernel_kernel']:
sxp_image.append(['kernel', xenapi_vm['kernel_kernel']])
if xenapi_vm['kernel_initrd']:
sxp_image.append(['ramdisk', xenapi_vm['kernel_initrd']])
if xenapi_vm['kernel_args']:
sxp_image.append(['args', xenapi_vm['kernel_args']])
cfg['image'] = prettyprintstring(sxp_image)
# make sure device structures are there.
if 'device' not in cfg:
cfg['device'] = {}
if 'vif_refs' not in cfg:
cfg['vif_refs'] = []
if 'vbd_refs' not in cfg:
cfg['vbd_refs'] = []
if 'vtpm_refs' not in cfg:
cfg['vtpm_refs'] = []
return cfg
def _sync_xen_api_from_legacy_api(self):
""" Sync all the attributes that is supported by the Xen API
from the legacy API configuration.
"""
for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
if cfgkey in self:
self.xenapi[apikey] = self[cfgkey]
def _sync_legacy_api_from_xen_api(self):
for cfgkey, apikey in LEGACY_CFG_TO_XENAPI_CFG.items():
if apikey in self.xenapi:
self[cfgkey] = self.xenapi[apikey]
def _populate_from_xml(self, parsed_xml):
raise NotImplementedError
def _populate_from_python_config(self, parsed_py):
raise NotImplementedError
def get_sxp(self, domain = None, ignore_devices = False, ignore = []):
""" Get SXP representation of this config object.
Incompat: removed store_mfn, console_mfn
@keyword domain: (optional) XendDomainInfo to get extra information
from such as domid and running devices.
@type domain: XendDomainInfo
@keyword ignore: (optional) list of 'keys' that we do not want
to export.
@type ignore: list of strings
@rtype: list of list (SXP representation)
"""
sxpr = ['domain']
# TODO: domid/dom is the same thing but called differently
# depending if it is from xenstore or sxpr.
if domain.getDomid() != None:
sxpr.append(['domid', domain.getDomid()])
for cfg, typefunc in ROUNDTRIPPING_CONFIG_ENTRIES:
if cfg in self:
if self[cfg] != None:
sxpr.append([cfg, self[cfg]])
if 'image' in self:
sxpr.append(['image', self['image']])
if 'security' in self:
sxpr.append(['security', self['security']])
if 'shutdown_reason' in self:
sxpr.append(['shutdown_reason', self['shutdown_reason']])
if 'cpu_time' in self:
sxpr.append(['cpu_time', self['cpu_time']/1e9])
sxpr.append(['online_vcpus', self['online_vcpus']])
if 'start_time' in self:
uptime = time.time() - self['start_time']
sxpr.append(['up_time', str(uptime)])
sxpr.append(['start_time', str(self['start_time'])])
sxpr.append(['on_xend_start', self.get('on_xend_start', 'ignore')])
sxpr.append(['on_xend_stop', self.get('on_xend_stop', 'ignore')])
sxpr.append(['status', domain.state])
# Marshall devices (running or from configuration)
if not ignore_devices:
for cls in XendDevices.valid_devices():
found = False
# figure if there is a device that is running
if domain:
try:
controller = domain.getDeviceController(cls)
configs = controller.configurations()
for config in configs:
sxpr.append(['device', config])
found = True
except:
log.exception("dumping sxp from device controllers")
pass
# if we didn't find that device, check the existing config
# for a device in the same class
if not found:
for dev_type, dev_info in self.all_devices_sxpr():
if dev_type == cls:
sxpr.append(['device', dev_info])
return sxpr
def validate(self):
""" Validate the configuration and fill in missing configuration
with defaults.
"""
# Fill in default values
for key, default_func in DEFAULT_CONFIGURATION:
if key not in self:
self[key] = default_func(self)
# Basic sanity checks
if 'image' in self and isinstance(self['image'], str):
self['image'] = sxp.from_string(self['image'])
if 'security' in self and isinstance(self['security'], str):
self['security'] = sxp.from_string(self['security'])
if self['memory'] == 0 and 'mem_kb' in self:
self['memory'] = (self['mem_kb'] + 1023)/1024
if self['memory'] <= 0:
raise XendConfigError('Invalid memory size: %s' %
str(self['memory']))
self['maxmem'] = max(self['memory'], self['maxmem'])
# Verify devices
for d_uuid, (d_type, d_info) in self['device'].items():
if d_type not in XendDevices.valid_devices():
raise XendConfigError('Invalid device (%s)' % d_type)
# Verify restart modes
for event in ('on_poweroff', 'on_reboot', 'on_crash'):
if self[event] not in CONFIG_RESTART_MODES:
raise XendConfigError('Invalid restart event: %s = %s' % \
(event, str(self[event])))
# Verify that {vif,vbd}_refs are here too
if 'vif_refs' not in self:
self['vif_refs'] = []
if 'vbd_refs' not in self:
self['vbd_refs'] = []
if 'vtpm_refs' not in self:
self['vtpm_refs'] = []
def device_add(self, dev_type, cfg_sxp = None, cfg_xenapi = None):
if dev_type not in XendDevices.valid_devices():
raise XendConfigError("XendConfig: %s not a valid device type" %
dev_type)
if cfg_sxp == None and cfg_xenapi == None:
raise XendConfigError("XendConfig: device_add requires some "
"config.")
if cfg_sxp:
log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
if cfg_xenapi:
log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
if cfg_sxp:
dev_info = {}
try:
for opt, val in cfg_sxp[1:]:
dev_info[opt] = val
except ValueError:
pass # SXP has no options for this device
# create uuid if it doesn't exist
dev_uuid = dev_info.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
self['device'][dev_uuid] = (dev_type, dev_info)
if dev_type in ('vif', 'vbd'):
self['%s_refs' % dev_type].append(dev_uuid)
elif dev_type in ('tap',):
self['vbd_refs'].append(dev_uuid)
return dev_uuid
if cfg_xenapi:
dev_info = {}
if dev_type == 'vif':
if cfg_xenapi.get('MAC'): # don't add if blank
dev_info['mac'] = cfg_xenapi.get('MAC')
# vifname is the name on the guest, not dom0
# TODO: we don't have the ability to find that out or
# change it from dom0
#if cfg_xenapi.get('device'): # don't add if blank
# dev_info['vifname'] = cfg_xenapi.get('device')
if cfg_xenapi.get('type'):
dev_info['type'] = cfg_xenapi.get('type')
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
self['device'][dev_uuid] = (dev_type, dev_info)
self['vif_refs'].append(dev_uuid)
return dev_uuid
elif dev_type == 'vbd':
dev_info['uname'] = cfg_xenapi.get('image', None)
dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
if cfg_xenapi.get('mode') == 'RW':
dev_info['mode'] = 'w'
else:
dev_info['mode'] = 'r'
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
self['device'][dev_uuid] = (dev_type, dev_info)
self['vbd_refs'].append(dev_uuid)
return dev_uuid
elif dev_type == 'vtpm':
if cfg_xenapi.get('type'):
dev_info['type'] = cfg_xenapi.get('type')
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
self['device'][dev_uuid] = (dev_type, dev_info)
self['vtpm_refs'].append(dev_uuid)
return dev_uuid
elif dev_type == 'tap':
dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
if cfg_xenapi.get('mode') == 'RW':
dev_info['mode'] = 'w'
else:
dev_info['mode'] = 'r'
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
dev_info['uuid'] = dev_uuid
self['device'][dev_uuid] = (dev_type, dev_info)
self['vbd_refs'].append(dev_uuid)
return dev_uuid
return ''
def device_sxpr(self, dev_uuid = None, dev_type = None, dev_info = None):
"""Get Device SXPR by either giving the device UUID or (type, config).
@rtype: list of lists
@return: device config sxpr
"""
sxpr = []
if dev_uuid != None and dev_uuid in self['device']:
dev_type, dev_info = self['device'][dev_uuid]
if dev_type == None or dev_info == None:
raise XendConfigError("Required either UUID or device type and "
"configuration dictionary.")
sxpr.append(dev_type)
config = [(opt, val) for opt, val in dev_info.items() \
if opt != 'type']
sxpr += config
return sxpr
def all_devices_sxpr(self):
sxprs = []
for dev_type, dev_info in self['device'].values():
sxpr = self.device_sxpr(dev_type = dev_type, dev_info = dev_info)
sxprs.append((dev_type, sxpr))
return sxprs
#
# debugging
#
if __name__ == "__main__":
pass
+96
View File
@@ -0,0 +1,96 @@
#============================================================================
# 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 Ltd.
#============================================================================
from xen.xend.XendAPIConstants import *
#
# Shutdown codes and reasons.
#
DOMAIN_POWEROFF = 0
DOMAIN_REBOOT = 1
DOMAIN_SUSPEND = 2
DOMAIN_CRASH = 3
DOMAIN_HALT = 4
DOMAIN_SHUTDOWN_REASONS = {
DOMAIN_POWEROFF: "poweroff",
DOMAIN_REBOOT : "reboot",
DOMAIN_SUSPEND : "suspend",
DOMAIN_CRASH : "crash",
DOMAIN_HALT : "halt"
}
restart_modes = [
"restart",
"destroy",
"preserve",
"rename-restart"
]
DOM_STATES = [
'halted',
'paused',
'running',
'suspended',
'shutdown',
'unknown',
]
DOM_STATE_HALTED = XEN_API_VM_POWER_STATE_HALTED
DOM_STATE_PAUSED = XEN_API_VM_POWER_STATE_PAUSED
DOM_STATE_RUNNING = XEN_API_VM_POWER_STATE_RUNNING
DOM_STATE_SUSPENDED = XEN_API_VM_POWER_STATE_SUSPENDED
DOM_STATE_SHUTDOWN = XEN_API_VM_POWER_STATE_SHUTTINGDOWN
DOM_STATE_UNKNOWN = XEN_API_VM_POWER_STATE_UNKNOWN
DOM_STATES_OLD = [
'running',
'blocked',
'paused',
'shutdown',
'crashed',
'dying'
]
STATE_DOM_OK = 1
STATE_DOM_SHUTDOWN = 2
SHUTDOWN_TIMEOUT = 30.0
ZOMBIE_PREFIX = 'Zombie-'
"""Minimum time between domain restarts in seconds."""
MINIMUM_RESTART_TIME = 20
RESTART_IN_PROGRESS = 'xend/restart_in_progress'
#
# Device migration stages (eg. XendDomainInfo, XendCheckpoint, server.tpmif)
#
DEV_MIGRATE_TEST = 0
DEV_MIGRATE_STEP1 = 1
DEV_MIGRATE_STEP2 = 2
DEV_MIGRATE_STEP3 = 3
#
# Xenstore Constants
#
XS_VMROOT = "/vm/"
+68
View File
@@ -0,0 +1,68 @@
#===========================================================================
# 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 Ltd
#============================================================================
#
# A collection of DevControllers
#
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif
from xen.xend.server.BlktapController import BlktapController
class XendDevices:
""" An ugly halfway point between the module local device name
to class map we used to have in XendDomainInfo and something
slightly more managable.
This class should contain all the functions that have to do
with managing devices in Xend. Right now it is only a factory
function.
"""
controllers = {
'vbd': blkif.BlkifController,
'vif': netif.NetifController,
'vtpm': tpmif.TPMifController,
'pci': pciif.PciController,
'ioports': iopif.IOPortsController,
'irq': irqif.IRQController,
'usb': usbif.UsbifController,
'tap': BlktapController,
}
#@classmethod
def valid_devices(cls):
return cls.controllers.keys()
valid_devices = classmethod(valid_devices)
#@classmethod
def make_controller(cls, name, domain):
"""Factory function to make device controllers per domain.
@param name: device class name in L{VALID_DEVICES}
@type name: String
@param domain: domain this controller is handling devices for.
@type domain: XendDomainInfo
@return: DevController of class 'name' or None
@rtype: subclass of DevController
"""
if name in cls.controllers.keys():
cls.controllers[name].deviceClass = name
return cls.controllers[name](domain)
return None
make_controller = classmethod(make_controller)
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+15 -1
View File
@@ -34,6 +34,20 @@ class XendError(Fault):
class VmError(XendError):
"""Vm construction error."""
pass
XEND_ERROR_AUTHENTICATION_FAILED = ('ELUSER', 'Authentication Failed')
XEND_ERROR_SESSION_INVALID = ('EPERMDENIED', 'Session Invalid')
XEND_ERROR_DOMAIN_INVALID = ('EINVALIDDOMAIN', 'Domain Invalid')
XEND_ERROR_HOST_INVALID = ('EINVALIDHOST', 'Host Invalid')
XEND_ERROR_HOST_RUNNING = ('EHOSTRUNNING', 'Host is still Running')
XEND_ERROR_HOST_CPU_INVALID = ('EHOSTCPUINVALID', 'Host CPU Invalid')
XEND_ERROR_UNSUPPORTED = ('EUNSUPPORTED', 'Method Unsupported')
XEND_ERROR_VM_INVALID = ('EVMINVALID', 'VM Invalid')
XEND_ERROR_VBD_INVALID = ('EVBDINVALID', 'VBD Invalid')
XEND_ERROR_VIF_INVALID = ('EVIFINVALID', 'VIF Invalid')
XEND_ERROR_VTPM_INVALID = ('EVTPMINVALID', 'VTPM Invalid')
XEND_ERROR_VDI_INVALID = ('EVDIINVALID', 'VDI Invalid')
XEND_ERROR_SR_INVALID = ('ESRINVALID', 'SR Invalid')
XEND_ERROR_TODO = ('ETODO', 'Lazy Programmer Error')
+111 -8
View File
@@ -13,22 +13,40 @@
# 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.
#============================================================================
"""Handler for node operations.
Has some persistent state:
- logs
- notification urls
"""
import os
import socket
import xen.lowlevel.xc
from xen.xend import uuid
from xen.xend.XendError import XendError
from xen.xend.XendStorageRepository import XendStorageRepository
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()
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
def shutdown(self):
return 0
@@ -39,6 +57,78 @@ class XendNode:
def notify(self, _):
return 0
#
# Ref validation
#
def is_valid_host(self, host_ref):
return (host_ref == self.uuid)
def is_valid_cpu(self, cpu_ref):
return (cpu_ref in self.cpus)
#
# Storage Repo
#
def get_sr(self):
return self.sr
#
# Host Functions
#
def xen_version(self):
info = self.xc.xeninfo()
from xen import VERSION
return {'Xen': '%(xen_major)d.%(xen_minor)d' % info,
'Xend': VERSION}
def get_name(self):
return self.name
def set_name(self, new_name):
self.name = new_name
#
# Host CPU Functions
#
def get_host_cpu_by_uuid(self, host_cpu_uuid):
if host_cpu_uuid in self.cpus:
return host_cpu_uuid
raise XendError('Invalid CPU UUID')
def get_host_cpu_refs(self):
return self.cpus.keys()
def get_host_cpu_uuid(self, host_cpu_ref):
if host_cpu_ref in self.cpus:
return host_cpu_ref
else:
raise XendError('Invalid CPU Reference')
def get_host_cpu_features(self, host_cpu_ref):
try:
return self.cpus[host_cpu_ref]['features']
except KeyError:
raise XendError('Invalid CPU Reference')
def get_host_cpu_number(self, host_cpu_ref):
try:
return self.cpus[host_cpu_ref]['number']
except KeyError:
raise XendError('Invalid CPU Reference')
def get_host_cpu_load(self, host_cpu_ref):
return 0.0
#
# Getting host information.
#
def info(self):
return (self.nodeinfo() + self.physinfo() + self.xeninfo() +
self.xendinfo())
@@ -98,6 +188,19 @@ class XendNode:
def xendinfo(self):
return [['xend_config_format', 2]]
# dictionary version of *info() functions to get rid of
# SXPisms.
def nodeinfo_dict(self):
return dict(self.nodeinfo())
def xendinfo_dict(self):
return dict(self.xendinfo())
def xeninfo_dict(self):
return dict(self.xeninfo())
def physinfo_dict(self):
return dict(self.physinfo())
def info_dict(self):
return dict(self.info())
def instance():
global inst
+1 -1
View File
@@ -22,7 +22,7 @@ import time
import types
from encode import *
import sxp
from xen.xend import sxp
from xen.xend import XendRoot
+18 -10
View File
@@ -30,12 +30,8 @@ import os.path
import string
import sys
import osdep
import XendLogging
from XendError import XendError
import sxp
from xen.xend import sxp, osdep, XendLogging
from xen.xend.XendError import XendError
class XendRoot:
"""Root of the management classes."""
@@ -102,6 +98,9 @@ class XendRoot:
"""Default interface to listen for VNC connections on"""
xend_vnc_listen_default = '127.0.0.1'
"""Default session storage path."""
xend_domains_path_default = '/var/lib/xend/domains'
components = {}
def __init__(self):
@@ -196,15 +195,18 @@ class XendRoot:
return self.get_config_bool("xend-http-server", self.xend_http_server_default)
def get_xend_tcp_xmlrpc_server(self):
return self.get_config_bool("xend-tcp-xmlrpc-server", self.xend_tcp_xmlrpc_server_default)
return self.get_config_bool("xend-tcp-xmlrpc-server",
self.xend_tcp_xmlrpc_server_default)
def get_xend_unix_xmlrpc_server(self):
return self.get_config_bool("xend-unix-xmlrpc-server", self.xend_unix_xmlrpc_server_default)
return self.get_config_bool("xend-unix-xmlrpc-server",
self.xend_unix_xmlrpc_server_default)
def get_xend_relocation_server(self):
"""Get the flag indicating whether xend should run a relocation server.
"""
return self.get_config_bool("xend-relocation-server", self.xend_relocation_server_default)
return self.get_config_bool("xend-relocation-server",
self.xend_relocation_server_default)
def get_xend_port(self):
"""Get the port xend listens at for its HTTP interface.
@@ -214,7 +216,8 @@ class XendRoot:
def get_xend_relocation_port(self):
"""Get the port xend listens at for connection to its relocation server.
"""
return self.get_config_int('xend-relocation-port', self.xend_relocation_port_default)
return self.get_config_int('xend-relocation-port',
self.xend_relocation_port_default)
def get_xend_relocation_hosts_allow(self):
return self.get_config_value("xend-relocation-hosts-allow",
@@ -246,6 +249,11 @@ class XendRoot:
"""
return self.get_config_value("xend-unix-path", self.xend_unix_path_default)
def get_xend_domains_path(self):
""" Get the path for persistent domain configuration storage
"""
return self.get_config_value("xend-domains-path", self.xend_domains_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,381 @@
#!/usr/bin/python
#============================================================================
# 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 Ltd.
#============================================================================
#
# The default QCOW Xen API Storage Repository
#
import os
import commands
import threading
from xen.xend import uuid
from xen.xend.XendError import XendError
from xen.xend.XendVDI import *
XEND_STORAGE_MAX_IGNORE = -1
XEND_STORAGE_DIR = "/var/lib/xend/storage/"
XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
DF_COMMAND = "df -lPk"
QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create %d %s"
KB = 1024
MB = 1024 *1024
class DeviceInvalidError(Exception):
pass
class XendStorageRepository:
"""A simple file backed QCOW Storage Repository.
This class exposes the interface to create VDI's via the
Xen API. The backend is a file-backed QCOW format that is stored
in XEND_STORAGE_DIR or any that is specified in the constructor.
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):
"""
@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
@ivar storage_free: storage space free for this repository
@ivar images: mapping of all the images.
@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.lock = threading.RLock()
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:
# TODO: log warning
pass
return uuid.createString()
def _refresh(self):
"""Internal function that refreshes the state of the disk and
updates the list of images available.
"""
self.lock.acquire()
try:
# create directory if /var/lib/xend/storage does not exist
if not os.path.exists(XEND_STORAGE_DIR):
os.makedirs(XEND_STORAGE_DIR)
os.chmod(XEND_STORAGE_DIR, 0700)
# scan the directory and populate self.images
total_used = 0
seen_images = []
for filename in os.listdir(XEND_STORAGE_DIR):
if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
image_uuid = filename[:-5]
seen_images.append(image_uuid)
# 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)
if cfg_path and os.path.exists(cfg_path):
vdi.load_config(cfg_path)
self.images[image_uuid] = vdi
total_used += qcow_size
# remove images that aren't valid
for image_uuid in self.images.keys():
if image_uuid not in seen_images:
try:
os.unlink(self.images[image_uuid].qcow_path)
except OSError:
pass
del self.images[image_uuid]
# 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:
self.storage_free = self._get_free_space()
finally:
self.lock.release()
def _get_df(self):
"""Returns the output of 'df' in a dictionary where the keys
are the Linux device numbers, and the values are it's corresponding
free space in bytes
@rtype: dictionary
"""
df = commands.getoutput(DF_COMMAND)
devnum_free = {}
for line in df.split('\n')[1:]:
words = line.split()
mount_point = words[-1]
dev_no = os.stat(mount_point).st_dev
free_kb = int(words[3])
devnum_free[dev_no] = free_kb * KB
return devnum_free
def _get_free_space(self):
"""Returns the amount of free space in bytes available in the storage
partition. Note that this may not be used if the storage repository
is initialised with a maximum size in storage_max.
@rtype: int
"""
df = self._get_df()
devnum = os.stat(self.storage_dir).st_dev
if df.has_key(devnum):
return df[devnum]
raise DeviceInvalidError("Device not found for storage path: %s" %
self.storage_dir)
def _has_space_available_for(self, size_bytes):
"""Returns whether there is enough space for an image in the
partition which the storage_dir resides on.
@rtype: bool
"""
if self.storage_max != -1:
return self.storage_free
bytes_free = self._get_free_space()
try:
if size_bytes < bytes_free:
return True
except DeviceInvalidError:
pass
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
@rtype: string
@return: uuid
@raises XendError: If an error occurs.
"""
self.lock.acquire()
try:
if not self._has_space_available_for(desired_size_bytes):
raise XendError("Not enough space")
image_uuid = uuid.createString()
qcow_path = os.path.join(XEND_STORAGE_DIR,
XEND_STORAGE_QCOW_FILENAME % image_uuid)
if qcow_path and os.path.exists(qcow_path):
raise XendError("Image with same UUID alreaady exists:" %
image_uuid)
cmd = QCOW_CREATE_COMMAND % (desired_size_bytes/MB, qcow_path)
rc, output = commands.getstatusoutput(cmd)
if rc != 0:
# cleanup the image file
os.unlink(qcow_path)
raise XendError("Failed to create QCOW Image: %s" % output)
self._refresh()
return image_uuid
finally:
self.lock.release()
def destroy_image(self, image_uuid):
"""Destroy an image that is managed by this storage repository.
@param image_uuid: Image UUID
@type image_uuid: String
@rtype: String
"""
self.lock.acquire()
try:
if image_uuid in self.images:
# TODO: check if it is being used?
qcow_path = self.images[image_uuid].qcow_path
cfg_path = self.images[image_uuid].cfg_path
try:
os.unlink(qcow_path)
if cfg_path and os.path.exists(cfg_path):
os.unlink(cfg_path)
except OSError:
# TODO: log warning
pass
del self.images[image_uuid]
return True
finally:
self.lock.release()
return False
def list_images(self):
""" List all the available images by UUID.
@rtype: list of strings.
@return: list of UUIDs
"""
self.lock.acquire()
try:
return self.images.keys()
finally:
self.lock.release()
def free_space_bytes(self):
"""Returns the amount of available space in KB.
@rtype: int
"""
self.lock.acquire()
try:
return self.storage_free
finally:
self.lock.release()
def total_space_bytes(self):
"""Returns the total usable space of the storage repo in KB.
@rtype: int
"""
self.lock.acquire()
try:
if self.storage_max != XEND_STORAGE_MAX_IGNORE:
return self.storage_max
else:
return self.free_space_bytes() + self.used_space_bytes()
finally:
self.lock.release()
def used_space_bytes(self):
"""Returns the total amount of space used by this storage repository.
@rtype: int
"""
self.lock.acquire()
try:
total_used = 0
for val in self.images.values():
total_used += val.physical_utilisation
return total_used
finally:
self.lock.release()
def is_valid_vdi(self, vdi_uuid):
return (vdi_uuid in self.images)
def create_image(self, vdi_struct):
image_uuid = None
try:
sector_count = int(vdi_struct.get('virtual_size', 0))
sector_size = int(vdi_struct.get('sector_size', 1024))
size_bytes = (sector_count * sector_size)
image_uuid = self._create_image_files(size_bytes)
image = self.images[image_uuid]
image_cfg = {
'sector_size': sector_size,
'virtual_size': sector_count,
'type': vdi_struct.get('type', 'system'),
'name_label': vdi_struct.get('name_label', ''),
'name_description': vdi_struct.get('name_description', ''),
'sharable': bool(vdi_struct.get('sharable', False)),
'read_only': bool(vdi_struct.get('read_only', False)),
}
# load in configuration from vdi_struct
image.load_config_dict(image_cfg)
# save configuration to file
cfg_filename = XEND_STORAGE_VDICFG_FILENAME % image_uuid
cfg_path = os.path.join(XEND_STORAGE_DIR, cfg_filename)
image.save_config(cfg_path)
except Exception, e:
# cleanup before raising exception
if image_uuid:
self.destroy_image(image_uuid)
raise
return image_uuid
def xen_api_get_by_label(self, label):
self.lock.acquire()
try:
for image_uuid, val in self.images.values():
if val.name_label == label:
return image_uuid
return None
finally:
self.lock.release()
def xen_api_get_by_uuid(self, image_uuid):
self.lock.acquire()
try:
return self.images.get(image_uuid)
finally:
self.lock.release()
# remove everything below this line!!
if __name__ == "__main__":
xsr = XendStorageRepository()
print 'Free Space: %d MB' % (xsr.free_space_bytes()/MB)
print "Create Image:",
print xsr._create_image_files(10 * MB)
print 'Delete all images:'
for image_uuid in xsr.list_images():
print image_uuid,
xsr._destroy_image_files(image_uuid)
print
+155
View File
@@ -0,0 +1,155 @@
#!/usr/bin/python
#============================================================================
# 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 Ltd.
#============================================================================
#
# Representation of a Xen API VDI
#
import os
from xen.util.xmlrpclib2 import stringify
from xmlrpclib import dumps, loads
KB = 1024
MB = 1024 * 1024
class AutoSaveObject(object):
def __init__(self):
self.cfg_path = None
self.auto_save = True
object
def save_config(self, cfg_file = None):
raise NotImplementedError()
def __setattr__(self, name, value):
"""A very simple way of making sure all attribute changes are
flushed to disk.
"""
object.__setattr__(self, name, value)
if name != 'auto_save' and getattr(self, 'auto_save', False):
self.save_config()
class XendVDI(AutoSaveObject):
"""Generic Xen API compatible VDI representation.
@cvar SAVED_CFG: list of configuration attributes to save.
@cvar SAVED_CFG_INT: list of configurations that should be ints.
"""
SAVED_CFG = ['name_label',
'name_description',
'sector_size',
'virtual_size',
'physical_utilisation',
'parent',
'children',
'sharable',
'read_only']
SAVED_CFG_INT = ['sector_size', 'virtual_size', 'physical_utilisation']
def __init__(self, uuid, sr_uuid):
self.uuid = uuid
self.sr_uuid = sr_uuid
self.name_label = ""
self.name_description = ""
self.sector_size = 1024
self.virtual_size = 0
self.physical_utilisation = 0
self.parent = None
self.children = []
self.sharable = False
self.read_only = False
self.type = "system"
def load_config_dict(self, cfg):
"""Loads configuration into the object from a dict.
@param cfg: configuration dict
@type cfg: dict
"""
self.auto_save = False
for key in self.SAVED_CFG:
if key in cfg:
if key in self.SAVED_CFG_INT:
setattr(self, key, int(cfg[key]))
else:
setattr(self, key, cfg[key])
self.auto_save = True
def load_config(self, cfg_path):
"""Loads configuration from an XMLRPC parameter format.
@param cfg_path: configuration file path
@type cfg_path: type
@rtype: bool
@return: Successful or not.
"""
try:
cfg, _ = loads(open(cfg_path).read())
cfg = cfg[0]
self.load_config_dict(cfg)
self.cfg_path = cfg_path
except IOError, e:
return False
return True
def save_config(self, cfg_path = None):
"""Saves configuration at give path in XMLRPC parameter format.
If cfg_path is not give, it defaults to the where the VDI
configuration as loaded if it load_config was called.
@keyword cfg_path: optional configuration file path
@rtype: bool
@return: Successful or not.
"""
try:
if not cfg_path and not self.cfg_path:
return False
if not cfg_path:
cfg_path = self.cfg_path
cfg = {}
for key in self.SAVED_CFG:
try:
cfg[key] = getattr(self, key)
except AttributeError:
pass
open(cfg_path, 'w').write(dumps((stringify(cfg),),
allow_none = True))
except IOError, e:
return False
return True
class XendQCOWVDI(XendVDI):
def __init__(self, uuid, sr_uuid, qcow_path, cfg_path, vsize, psize):
XendVDI.__init__(self, uuid, sr_uuid)
self.auto_save = False
self.qcow_path = qcow_path
self.cfg_path = cfg_path
self.physical_utilisation = psize
self.virtual_size = vsize
self.sector_size = 512
self.auto_save = True
+7 -42
View File
@@ -189,7 +189,7 @@ class LinuxImageHandler(ImageHandler):
store_evtchn = self.vm.getStorePort()
console_evtchn = self.vm.getConsolePort()
log.debug("dom = %d", self.vm.getDomid())
log.debug("domid = %d", self.vm.getDomid())
log.debug("image = %s", self.kernel)
log.debug("store_evtchn = %d", store_evtchn)
log.debug("console_evtchn = %d", console_evtchn)
@@ -198,7 +198,7 @@ class LinuxImageHandler(ImageHandler):
log.debug("vcpus = %d", self.vm.getVCpuCount())
log.debug("features = %s", self.vm.getFeatures())
return xc.linux_build(dom = self.vm.getDomid(),
return xc.linux_build(domid = self.vm.getDomid(),
image = self.kernel,
store_evtchn = store_evtchn,
console_evtchn = console_evtchn,
@@ -218,7 +218,7 @@ class PPC_LinuxImageHandler(LinuxImageHandler):
store_evtchn = self.vm.getStorePort()
console_evtchn = self.vm.getConsolePort()
log.debug("dom = %d", self.vm.getDomid())
log.debug("domid = %d", self.vm.getDomid())
log.debug("image = %s", self.kernel)
log.debug("store_evtchn = %d", store_evtchn)
log.debug("console_evtchn = %d", console_evtchn)
@@ -229,7 +229,7 @@ class PPC_LinuxImageHandler(LinuxImageHandler):
devtree = FlatDeviceTree.build(self)
return xc.linux_build(dom = self.vm.getDomid(),
return xc.linux_build(domid = self.vm.getDomid(),
image = self.kernel,
store_evtchn = store_evtchn,
console_evtchn = console_evtchn,
@@ -272,7 +272,7 @@ class HVMImageHandler(ImageHandler):
def buildDomain(self):
store_evtchn = self.vm.getStorePort()
log.debug("dom = %d", self.vm.getDomid())
log.debug("domid = %d", self.vm.getDomid())
log.debug("image = %s", self.kernel)
log.debug("store_evtchn = %d", store_evtchn)
log.debug("memsize = %d", self.vm.getMemoryTarget() / 1024)
@@ -282,9 +282,8 @@ class HVMImageHandler(ImageHandler):
log.debug("apic = %d", self.apic)
self.register_shutdown_watch()
self.register_reboot_feature_watch()
return xc.hvm_build(dom = self.vm.getDomid(),
return xc.hvm_build(domid = self.vm.getDomid(),
image = self.kernel,
store_evtchn = store_evtchn,
memsize = self.vm.getMemoryTarget() / 1024,
@@ -417,7 +416,6 @@ class HVMImageHandler(ImageHandler):
def destroy(self):
self.unregister_shutdown_watch();
self.unregister_reboot_feature_watch();
if not self.pid:
return
os.kill(self.pid, signal.SIGKILL)
@@ -446,7 +444,7 @@ class HVMImageHandler(ImageHandler):
""" watch call back on node control/shutdown,
if node changed, this function will be called
"""
from xen.xend.XendDomainInfo import shutdown_reasons
from xen.xend.XendConstants import DOMAIN_SHUTDOWN_REASONS
xd = xen.xend.XendDomain.instance()
vm = xd.domain_lookup( self.vm.getDomid() )
@@ -460,39 +458,6 @@ class HVMImageHandler(ImageHandler):
return 1 # Keep watching
def register_reboot_feature_watch(self):
""" add xen store watch on control/feature-reboot """
self.rebootFeatureWatch = xswatch(self.vm.dompath + "/control/feature-reboot", \
self.hvm_reboot_feature)
log.debug("hvm reboot feature watch registered")
def unregister_reboot_feature_watch(self):
"""Remove the watch on the control/feature-reboot, if any. Nothrow
guarantee."""
try:
if self.rebootFeatureWatch:
self.rebootFeatureWatch.unwatch()
except:
log.exception("Unwatching hvm reboot feature watch failed.")
self.rebootFeatureWatch = None
log.debug("hvm reboot feature watch unregistered")
def hvm_reboot_feature(self, _):
""" watch call back on node control/feature-reboot,
if node changed, this function will be called
"""
xd = xen.xend.XendDomain.instance()
vm = xd.domain_lookup( self.vm.getDomid() )
status = vm.readDom('control/feature-reboot')
log.debug("hvm_reboot_feature fired, module status=%s", status)
if status == '1':
self.unregister_shutdown_watch()
return 1 # Keep watching
class IA64_HVM_ImageHandler(HVMImageHandler):
ostype = "hvm"
+35 -14
View File
@@ -88,9 +88,9 @@ class DevController:
xd = xen.xend.XendDomain.instance()
backdom_name = sxp.child_value(config, 'backend')
if backdom_name is None:
backdom = xen.xend.XendDomain.PRIV_DOMAIN
backdom = xen.xend.XendDomain.DOM0_ID
else:
bd = xd.domain_lookup_by_name_or_id_nr(backdom_name)
bd = xd.domain_lookup_nr(backdom_name)
backdom = bd.getDomid()
count = 0
while True:
@@ -141,7 +141,6 @@ class DevController:
def waitForDevices(self):
log.debug("Waiting for devices %s.", self.deviceClass)
return map(self.waitForDevice, self.deviceIDs())
@@ -221,13 +220,15 @@ class DevController:
"""@return an s-expression giving the current configuration of the
specified device. This would be suitable for giving to {@link
#createDevice} in order to recreate that device."""
backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")
if backdomid is None:
raise VmError("Device %s not connected" % devid)
return [self.deviceClass, ['backend', int(backdomid)]]
configDict = self.getDeviceConfiguration(devid)
sxpr = [self.deviceClass]
for key, val in configDict.items():
if type(val) == type(list()):
for v in val:
sxpr.append([key, v])
else:
sxpr.append([key, val])
return sxpr
def sxprs(self):
"""@return an s-expression describing all the devices of this
@@ -243,6 +244,25 @@ class DevController:
'id', devid]]
def getDeviceConfiguration(self, devid):
"""Returns the configuration of a device.
@note: Similar to L{configuration} except it returns a dict.
@return: dict
"""
backdomid = xstransact.Read(self.frontendPath(devid), "backend-id")
if backdomid is None:
raise VmError("Device %s not connected" % devid)
return {'backend': int(backdomid)}
def getAllDeviceConfigurations(self):
all_configs = {}
for devid in self.deviceIDs():
config_dict = self.getDeviceConfiguration(devid)
all_configs[devid] = config_dict
return all_configs
## protected:
def getDeviceDetails(self, config):
@@ -387,7 +407,7 @@ class DevController:
backdom_name = sxp.child_value(config, 'backend')
if backdom_name:
backdom = xd.domain_lookup_by_name_or_id_nr(backdom_name)
backdom = xd.domain_lookup_nr(backdom_name)
else:
backdom = xd.privilegedDomain()
@@ -451,9 +471,10 @@ class DevController:
def backendRoot(self):
import xen.xend.XendDomain
from xen.xend.xenstore.xsutil import GetDomainPath
backdom = xen.xend.XendDomain.PRIV_DOMAIN
return "%s/backend/%s/%s" % (GetDomainPath(backdom), self.deviceClass, self.vm.getDomid())
from xen.xend.xenstore.xsutil import GetDomainPath
backdom = xen.xend.XendDomain.DOM0_ID
return "%s/backend/%s/%s" % (GetDomainPath(backdom),
self.deviceClass, self.vm.getDomid())
def frontendMiscPath(self):
return "%s/device-misc/%s" % (self.vm.getDomainPath(),
+10
View File
@@ -205,6 +205,8 @@ class Daemon:
sig)
else:
self.run(w and os.fdopen(w, 'w') or None)
# if we reach here, the child should quit.
os._exit(0)
return ret
@@ -297,9 +299,17 @@ class Daemon:
log.info("Xend changeset: %s.", xinfo['xen_changeset'])
del xc
try:
from xen import VERSION
log.info("Xend version: %s", VERSION)
except ImportError:
log.info("Xend version: Unknown.")
relocate.listenRelocation()
servers = SrvServer.create()
servers.start(status)
del servers
except Exception, ex:
print >>sys.stderr, 'Exception starting xend:', ex
if XEND_DEBUG:
+1 -1
View File
@@ -39,7 +39,7 @@ class SrvDomainDir(SrvDir):
self.xd = XendDomain.instance()
def domain(self, x):
dom = self.xd.domain_lookup_by_name_or_id(x)
dom = self.xd.domain_lookup(x)
if not dom:
raise XendError('No such domain ' + str(x))
return SrvDomain(dom)
+27 -3
View File
@@ -42,6 +42,7 @@
import fcntl
import time
import signal
from threading import Thread
from xen.web.httpserver import HttpServer, UnixHttpServer
@@ -54,7 +55,6 @@ from xen.web.SrvDir import SrvDir
from SrvRoot import SrvRoot
from XMLRPCServer import XMLRPCServer
xroot = XendRoot.instance()
@@ -66,6 +66,14 @@ class XendServers:
def add(self, server):
self.servers.append(server)
def cleanup(self, signum = 0, frame = None):
log.debug("SrvServer.cleanup()")
for server in self.servers:
try:
server.shutdown()
except:
pass
def start(self, status):
# Running the network script will spawn another process, which takes
# the status fd with it unless we set FD_CLOEXEC. Failing to do this
@@ -100,8 +108,24 @@ class XendServers:
status.write('0')
status.close()
for t in threads:
t.join()
# Prepare to catch SIGTERM (received when 'xend stop' is executed)
# and call each server's cleanup if possible
signal.signal(signal.SIGTERM, self.cleanup)
# Interruptible Thread.join - Python Bug #1167930
# Replaces: for t in threads: t.join()
# Reason: The above will cause python signal handlers to be
# blocked so we're not able to catch SIGTERM in any
# way for cleanup
runningThreads = len([t for t in threads if t.isAlive()])
while runningThreads > 0:
try:
for t in threads:
t.join(1.0)
runningThreads = len([t for t in threads if t.isAlive()])
except:
pass
def create():
root = SrvDir()
+66 -31
View File
@@ -16,19 +16,31 @@
# Copyright (C) 2006 XenSource Ltd.
#============================================================================
import types
import xmlrpclib
from xen.xend import XendDomain, XendDomainInfo, XendNode, \
XendLogging, XendDmesg
from xen.util.xmlrpclib2 import UnixXMLRPCServer, TCPXMLRPCServer
from xen.xend.XendClient import XML_RPC_SOCKET, ERROR_INVALID_DOMAIN
from xen.xend.XendError import *
from xen.xend import XendDomain, XendDomainInfo, XendNode
from xen.xend import XendLogging, XendDmesg
from xen.xend.XendClient import XML_RPC_SOCKET
from xen.xend.XendLogging import log
from types import ListType
from xen.xend.XendAPI import XendAPI
from xen.xend.XendError import XendInvalidDomain
# vcpu_avail is a long and is not needed by the clients. It's far easier
# to just remove it then to try and marshal the long.
def fixup_sxpr(sexpr):
ret = []
for k in sexpr:
if type(k) in (types.ListType, types.TupleType):
if len(k) != 2 or k[0] != 'vcpu_avail':
ret.append(fixup_sxpr(k))
else:
ret.append(k)
return ret
def lookup(domid):
info = XendDomain.instance().domain_lookup_by_name_or_id(domid)
info = XendDomain.instance().domain_lookup_nr(domid)
if not info:
raise XendInvalidDomain(str(domid))
return info
@@ -37,28 +49,16 @@ def dispatch(domid, fn, args):
info = lookup(domid)
return getattr(info, fn)(*args)
# vcpu_avail is a long and is not needed by the clients. It's far easier
# to just remove it then to try and marshal the long.
def fixup_sxpr(sexpr):
ret = []
for k in sexpr:
if type(k) is ListType:
if len(k) != 2 or k[0] != 'vcpu_avail':
ret.append(fixup_sxpr(k))
else:
ret.append(k)
return ret
def domain(domid):
info = lookup(domid)
return fixup_sxpr(info.sxpr())
def domains(detail=1):
def domains(detail=1, full = 0):
if detail < 1:
return XendDomain.instance().list_names()
else:
domains = XendDomain.instance().list_sorted()
return map(lambda dom: fixup_sxpr(dom.sxpr()), domains)
return map(lambda dom: fixup_sxpr(dom.sxpr(not full)), domains)
def domain_create(config):
info = XendDomain.instance().domain_create(config)
@@ -75,8 +75,8 @@ def get_log():
finally:
f.close()
methods = ['device_create', 'device_configure', 'destroyDevice',
'getDeviceSxprs',
methods = ['device_create', 'device_configure',
'destroyDevice','getDeviceSxprs',
'setMemoryTarget', 'setName', 'setVCpuCount', 'shutdown',
'send_sysrq', 'getVCPUInfo', 'waitForDevices',
'getRestartCount']
@@ -84,25 +84,44 @@ methods = ['device_create', 'device_configure', 'destroyDevice',
exclude = ['domain_create', 'domain_restore']
class XMLRPCServer:
def __init__(self, use_tcp=False):
self.ready = False
def __init__(self, use_tcp=False, host = "localhost", port = 8006,
path = XML_RPC_SOCKET):
self.use_tcp = use_tcp
self.port = port
self.host = host
self.path = path
self.ready = False
self.running = True
self.xenapi = XendAPI()
def run(self):
if self.use_tcp:
# bind to something fixed for now as we may eliminate
# tcp support completely.
self.server = TCPXMLRPCServer(("localhost", 8005), logRequests=False)
self.server = TCPXMLRPCServer((self.host, self.port),
logRequests = False)
else:
self.server = UnixXMLRPCServer(XML_RPC_SOCKET, False)
self.server = UnixXMLRPCServer(self.path, logRequests = False)
# Register Xen API Functions
# -------------------------------------------------------------------
# exportable functions are ones that do not begin with '_'
# 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'))
# Legacy deprecated xm xmlrpc api
# --------------------------------------------------------------------
# Functions in XendDomainInfo
for name in methods:
fn = eval("lambda domid, *args: dispatch(domid, '%s', args)"%name)
self.server.register_function(fn, "xend.domain.%s" % name)
# Functions in XendDomain
inst = XendDomain.instance()
for name in dir(inst):
fn = getattr(inst, name)
if name.startswith("domain_") and callable(fn):
@@ -126,4 +145,20 @@ class XMLRPCServer:
self.server.register_introspection_functions()
self.ready = True
self.server.serve_forever()
# Custom runloop so we can cleanup when exiting.
# -----------------------------------------------------------------
try:
self.server.socket.settimeout(1.0)
while self.running:
self.server.handle_request()
finally:
self.cleanup()
def cleanup(self):
log.debug("XMLRPCServer.cleanup()")
def shutdown(self):
self.running = False
self.ready = False
+25 -27
View File
@@ -16,7 +16,6 @@
# Copyright (C) 2005, 2006 XenSource Inc.
#============================================================================
import re
import string
@@ -24,10 +23,8 @@ from xen.util import blkif
from xen.util import security
from xen.xend import sxp
from xen.xend.XendError import VmError
from xen.xend.server.DevController import DevController
class BlkifController(DevController):
"""Block device interface controller. Handles all block devices
for a domain.
@@ -38,12 +35,10 @@ class BlkifController(DevController):
"""
DevController.__init__(self, vm)
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
uname = sxp.child_value(config, 'uname')
dev = sxp.child_value(config, 'dev')
uname = sxp.child_value(config, 'uname', '')
dev = sxp.child_value(config, 'dev', '')
if 'ioemu:' in dev:
(_, dev) = string.split(dev, ':', 1)
@@ -66,11 +61,7 @@ class BlkifController(DevController):
mode = sxp.child_value(config, 'mode', 'r')
if mode not in ('r', 'w', 'w!'):
name = uname
if not name:
name = sxp.child_value(config, 'dev')
raise VmError('Invalid mode \'%s\' for %s. Valid modes are '
'{r,w,w!}' % (mode, name))
raise VmError('Invalid mode')
back = { 'dev' : dev,
'type' : typ,
@@ -78,6 +69,10 @@ class BlkifController(DevController):
'mode' : mode
}
uuid = sxp.child_value(config, 'uuid')
if uuid:
back['uuid'] = uuid
if security.on():
(label, ssidref, policy) = security.get_res_security_details(uname)
back.update({'acm_label' : label,
@@ -112,27 +107,30 @@ class BlkifController(DevController):
(self.deviceClass, devid, config))
def configuration(self, devid):
"""@see DevController.configuration"""
result = DevController.configuration(self, devid)
(dev, typ, params, mode) = self.readBackend(devid,
'dev', 'type', 'params',
'mode')
def getDeviceConfiguration(self, devid):
"""Returns the configuration of a device.
@note: Similar to L{configuration} except it returns a dict.
@return: dict
"""
config = DevController.getDeviceConfiguration(self, devid)
devinfo = self.readBackend(devid, 'dev', 'type', 'params', 'mode',
'uuid')
dev, typ, params, mode, uuid = devinfo
if dev:
(dev_type) = self.readFrontend(devid, 'device-type')
dev_type = self.readFrontend(devid, 'device-type')
if dev_type:
dev += ":" + dev_type
result.append(['dev', dev])
dev += ':' + dev_type
config['dev'] = dev
if typ and params:
result.append(['uname', typ + ":" + params])
config['uname'] = typ +':' + params
if mode:
result.append(['mode', mode])
return result
config['mode'] = mode
if uuid:
config['uuid'] = uuid
return config
def destroyDevice(self, devid):
"""@see DevController.destroyDevice"""
+2 -2
View File
@@ -20,7 +20,7 @@
import types
import xen.lowlevel.xc;
import xen.lowlevel.xc
from xen.xend import sxp
from xen.xend.XendError import VmError
@@ -72,7 +72,7 @@ class IOPortsController(DevController):
raise VmError('ioports: Invalid i/o range: %s - %s' %
(io_from, io_to))
rc = xc.domain_ioport_permission(dom = self.getDomid(),
rc = xc.domain_ioport_permission(domid = self.getDomid(),
first_port = io_from,
nr_ports = io_to - io_from + 1,
allow_access = True)
+1 -1
View File
@@ -23,7 +23,7 @@
import types
import xen.lowlevel.xc;
import xen.lowlevel.xc
from xen.xend import sxp
from xen.xend.XendError import VmError
+19 -19
View File
@@ -26,13 +26,10 @@ import re
from xen.xend import sxp
from xen.xend import XendRoot
from xen.xend.server.DevController import DevController
xroot = XendRoot.instance()
def randomMAC():
"""Generate a random MAC address.
@@ -139,7 +136,6 @@ class NetifController(DevController):
def __init__(self, vm):
DevController.__init__(self, vm)
def getDeviceDetails(self, config):
"""@see DevController.getDeviceDetails"""
@@ -157,6 +153,7 @@ class NetifController(DevController):
mac = sxp.child_value(config, 'mac')
vifname = sxp.child_value(config, 'vifname')
rate = sxp.child_value(config, 'rate')
uuid = sxp.child_value(config, 'uuid')
ipaddr = _get_config_ipaddr(config)
devid = self.allocateDeviceID()
@@ -182,34 +179,37 @@ class NetifController(DevController):
back['vifname'] = vifname
if rate:
back['rate'] = parseRate(rate)
if uuid:
back['uuid'] = uuid
return (devid, back, front)
def configuration(self, devid):
def getDeviceConfiguration(self, devid):
"""@see DevController.configuration"""
result = DevController.configuration(self, devid)
(script, ip, bridge, mac, typ, vifname, rate) = self.readBackend(
devid, 'script', 'ip', 'bridge', 'mac', 'type', 'vifname', 'rate')
result = DevController.getDeviceConfiguration(self, devid)
devinfo = self.readBackend(devid, 'script', 'ip', 'bridge',
'mac', 'type', 'vifname', 'rate', 'uuid')
(script, ip, bridge, mac, typ, vifname, rate, uuid) = devinfo
if script:
result.append(['script',
script.replace(xroot.network_script_dir + os.sep,
"")])
network_script_dir = xroot.network_script_dir + os.sep
result['script'] = script.replace(network_script_dir, "")
if ip:
for i in ip.split(" "):
result.append(['ip', i])
result['ip'] = ip.split(" ")
if bridge:
result.append(['bridge', bridge])
result['bridge'] = bridge
if mac:
result.append(['mac', mac])
result['mac'] = mac
if typ:
result.append(['type', typ])
result['type'] = typ
if vifname:
result.append(['vifname', vifname])
result['vifname'] = vifname
if rate:
result.append(['rate', formatRate(rate)])
result['rate'] = formatRate(rate)
if uuid:
result['uuid'] = uuid
return result
+42 -17
View File
@@ -109,30 +109,55 @@ class PciController(DevController):
return (0, back, {})
def configuration(self, devid):
"""@see DevController.configuration"""
result = DevController.configuration(self, devid)
(num_devs) = self.readBackend(devid, 'num_devs')
def getDeviceConfiguration(self, devid):
result = DevController.getDeviceConfiguration(self, devid)
num_devs = self.readBackend(devid, 'num_devs')
pci_devs = []
for i in range(int(num_devs)):
(dev_config) = self.readBackend(devid, 'dev-%d'%(i))
(dev_config,) = self.readBackend(devid, 'dev-%d'%(i))
pci_match = re.match(r"((?P<domain>[0-9a-fA-F]{1,4})[:,])?" + \
r"(?P<bus>[0-9a-fA-F]{1,2})[:,]" + \
r"(?P<slot>[0-9a-fA-F]{1,2})[.,]" + \
r"(?P<func>[0-9a-fA-F]{1,2})", dev_config)
if pci_match!=None:
pci_dev_info = pci_match.groupdict('0')
result.append( ['dev', \
['domain', '0x'+pci_dev_info['domain']], \
['bus', '0x'+pci_dev_info['bus']], \
['slot', '0x'+pci_dev_info['slot']], \
['func', '0x'+pci_dev_info['func']]])
pci_devs.append({'domain': '0x%(domain)s' % pci_dev_info,
'bus': '0x%(bus)s' % pci_dev_info,
'slot': '0x(slot)s' % pci_dev_info,
'func': '0x(func)s' % pci_dev_info})
result['dev'] = pci_devs
return result
def configuration(self, devid):
"""Returns SXPR for devices on domain.
@note: we treat this dict especially to convert to
SXP because it is not a straight dict of strings."""
configDict = self.getDeviceConfiguration(devid)
sxpr = [self.deviceClass]
# remove devs
devs = configDict.pop('dev', [])
for dev in devs:
dev_sxpr = ['dev']
for dev_item in dev.items():
dev_sxpr.append(list(dev_item))
sxpr.append(dev_sxpr)
for key, val in configDict.items():
if type(val) == type(list()):
for v in val:
sxpr.append([key, v])
else:
sxpr.append([key, val])
return sxpr
def setupDevice(self, domain, bus, slot, func):
""" Attach I/O resources for device to frontend domain
"""
@@ -155,9 +180,9 @@ class PciController(DevController):
PCIQuirk(dev.vendor, dev.device, dev.subvendor, dev.subdevice, domain,
bus, slot, func)
for (start, size) in dev.ioports:
for (start, size) in dev.ioports:
log.debug('pci: enabling ioport 0x%x/0x%x'%(start,size))
rc = xc.domain_ioport_permission(dom = fe_domid, first_port = start,
rc = xc.domain_ioport_permission(domid = fe_domid, first_port = start,
nr_ports = size, allow_access = True)
if rc<0:
raise VmError(('pci: failed to configure I/O ports on device '+
@@ -171,7 +196,7 @@ class PciController(DevController):
log.debug('pci: enabling iomem 0x%x/0x%x pfn 0x%x/0x%x'% \
(start,size,start_pfn,nr_pfns))
rc = xc.domain_iomem_permission(dom = fe_domid,
rc = xc.domain_iomem_permission(domid = fe_domid,
first_pfn = start_pfn,
nr_pfns = nr_pfns,
allow_access = True)
@@ -181,7 +206,7 @@ class PciController(DevController):
if dev.irq>0:
log.debug('pci: enabling irq %d'%dev.irq)
rc = xc.domain_irq_permission(dom = fe_domid, pirq = dev.irq,
rc = xc.domain_irq_permission(domid = fe_domid, pirq = dev.irq,
allow_access = True)
if rc<0:
raise VmError(('pci: failed to configure irq on device '+
+22 -10
View File
@@ -22,20 +22,17 @@
"""
from xen.xend import sxp
from xen.xend import XendRoot
from xen.xend.XendLogging import log
from xen.xend.XendError import XendError
from xen.xend import XendRoot
from xen.xend.XendDomainInfo import DEV_MIGRATE_TEST
from xen.xend.XendConstants import DEV_MIGRATE_TEST
from xen.xend.server.DevController import DevController
import os
import re
xroot = XendRoot.instance()
class TPMifController(DevController):
"""TPM interface controller. Handles all TPM devices for a domain.
"""
@@ -52,22 +49,37 @@ class TPMifController(DevController):
if inst == -1:
inst = int(sxp.child_value(config, 'instance' , '0'))
typ = sxp.child_value(config, 'type')
uuid = sxp.child_value(config, 'uuid')
log.info("The domain has a TPM with pref. instance %d and devid %d.",
inst, devid)
back = { 'pref_instance' : "%i" % inst,
'resume' : "%s" % (self.vm.getResume()) }
if typ:
back['type'] = typ
if uuid:
back['uuid'] = uuid
front = { 'handle' : "%i" % devid }
return (devid, back, front)
def configuration(self, devid):
def getDeviceConfiguration(self, devid):
"""Returns the configuration of a device"""
result = DevController.getDeviceConfiguration(self, devid)
result = DevController.configuration(self, devid)
instance = self.readBackend(devid, 'instance')
(instance, uuid, type) = \
self.readBackend(devid, 'instance',
'uuid',
'type')
if instance:
result.append(['instance', instance])
result['instance'] = instance
if uuid:
result['uuid'] = uuid
if type:
result['type'] == type
return result
+14 -10
View File
@@ -267,10 +267,14 @@ class Parser:
elif c == 'x':
self.state.fn = self.state_hex
self.state.val = 0
else:
elif c in string.octdigits:
self.state.fn = self.state_octal
self.state.val = 0
self.input_char(c)
else:
# ignore escape if it doesn't match anything we know
self.state.parent.buf += '\\'
self.pop_state()
def state_octal(self, c):
def octaldigit(c):
@@ -375,7 +379,7 @@ def atomp(sxpr):
def show(sxpr, out=sys.stdout):
"""Print an sxpr in bracketed (lisp-style) syntax.
"""
if isinstance(sxpr, types.ListType):
if isinstance(sxpr, (types.ListType, types.TupleType)):
out.write(k_list_open)
i = 0
for x in sxpr:
@@ -393,7 +397,7 @@ def show(sxpr, out=sys.stdout):
def show_xml(sxpr, out=sys.stdout):
"""Print an sxpr in XML syntax.
"""
if isinstance(sxpr, types.ListType):
if isinstance(sxpr, (types.ListType, types.TupleType)):
element = name(sxpr)
out.write('<%s' % element)
for attr in attributes(sxpr):
@@ -416,7 +420,7 @@ def elementp(sxpr, elt=None):
sxpr sxpr
elt element type
"""
return (isinstance(sxpr, types.ListType)
return (isinstance(sxpr, (types.ListType, types.TupleType))
and len(sxpr)
and (None == elt or sxpr[0] == elt))
@@ -432,7 +436,7 @@ def name(sxpr):
val = None
if isinstance(sxpr, types.StringType):
val = sxpr
elif isinstance(sxpr, types.ListType) and len(sxpr):
elif isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr):
val = sxpr[0]
return val
@@ -444,7 +448,7 @@ def attributes(sxpr):
returns attribute list
"""
val = []
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1:
attr = sxpr[1]
if elementp(attr, k_attr_open):
val = attr[1:]
@@ -474,7 +478,7 @@ def children(sxpr, elt=None):
returns children (filtered by elt if specified)
"""
val = []
if isinstance(sxpr, types.ListType) and len(sxpr) > 1:
if isinstance(sxpr, (types.ListType, types.TupleType)) and len(sxpr) > 1:
i = 1
x = sxpr[i]
if elementp(x, k_attr_open):
@@ -563,7 +567,7 @@ def with_id(sxpr, id, val=None):
return s-exp or val
"""
if isinstance(sxpr, types.ListType):
if isinstance(sxpr, (types.ListType, types.TupleType)):
for n in sxpr:
if has_id(n, id):
val = n
@@ -583,7 +587,7 @@ def child_with_id(sxpr, id, val=None):
return s-exp or val
"""
if isinstance(sxpr, types.ListType):
if isinstance(sxpr, (types.ListType, types.TupleType)):
for n in sxpr:
if has_id(n, id):
val = n
@@ -605,7 +609,7 @@ def elements(sxpr, ctxt=None):
yield (sxpr, ctxt)
i = 0
for n in children(sxpr):
if isinstance(n, types.ListType):
if isinstance(n, (types.ListType, types.TupleType)):
# Calling elements() recursively does not generate recursively,
# it just returns a generator object. So we must iterate over it.
for v in elements(n, (i, sxpr, ctxt)):
+6 -4
View File
@@ -54,10 +54,6 @@ def getUuidRandom():
uuidFactory = getUuidRandom
def create():
return uuidFactory()
def toString(u):
return "-".join(["%02x" * 4, "%02x" * 2, "%02x" * 2, "%02x" * 2,
"%02x" * 6]) % tuple(u)
@@ -65,3 +61,9 @@ def toString(u):
def fromString(s):
s = s.replace('-', '')
return [ int(s[i : i + 2], 16) for i in range(0, 32, 2) ]
def create():
return uuidFactory()
def createString():
return toString(create())
+48 -27
View File
@@ -299,7 +299,7 @@ gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,ba
This option may be repeated to add more than one vif.
Specifying vifs will increase the number of interfaces as needed.""")
gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
gopts.var('vtpm', val="instance=INSTANCE,backend=DOM,type=TYPE",
fn=append_value, default=[],
use="""Add a TPM interface. On the backend side use the given
instance as virtual TPM instance. The given number is merely the
@@ -307,7 +307,11 @@ gopts.var('vtpm', val="instance=INSTANCE,backend=DOM",
which instance number will actually be assigned to the domain.
The associtation between virtual machine and the TPM instance
number can be found in /etc/xen/vtpm.db. Use the backend in the
given domain.""")
given domain.
The type parameter can be used to select a specific driver type
that the VM can use. To prevent a fully virtualized domain (HVM)
from being able to access an emulated device model, you may specify
'paravirtualized' here.""")
gopts.var('access_control', val="policy=POLICY,label=LABEL",
fn=append_value, default=[],
@@ -451,6 +455,18 @@ gopts.var('uuid', val='',
addresses for virtual network interfaces. This must be a unique
value across the entire cluster.""")
gopts.var('on_xend_start', val='ignore|start',
fn=set_value, default='ignore',
use='Action to perform when xend starts')
gopts.var('on_xend_stop', val='continue|shutdown|suspend',
fn=set_value, default="ignore",
use="""Behaviour when Xend stops:
- ignore: Domain continues to run;
- shutdown: Domain is shutdown;
- suspend: Domain is suspended;
""")
def err(msg):
"""Print an error to stderr and exit.
"""
@@ -585,27 +601,28 @@ def configure_vtpm(config_devs, vals):
"""Create the config for virtual TPM interfaces.
"""
vtpm = vals.vtpm
vtpm_n = 1
for idx in range(0, vtpm_n):
if idx < len(vtpm):
d = vtpm[idx]
instance = d.get('instance')
if instance == "VTPMD":
instance = "0"
else:
if instance != None:
try:
if int(instance) == 0:
err('VM config error: vTPM instance must not be 0.')
except ValueError:
err('Vm config error: could not parse instance number.')
backend = d.get('backend')
config_vtpm = ['vtpm']
if instance:
config_vtpm.append(['pref_instance', instance])
if backend:
config_vtpm.append(['backend', backend])
config_devs.append(['device', config_vtpm])
if len(vtpm) > 0:
d = vtpm[0]
instance = d.get('instance')
if instance == "VTPMD":
instance = "0"
else:
if instance != None:
try:
if int(instance) == 0:
err('VM config error: vTPM instance must not be 0.')
except ValueError:
err('Vm config error: could not parse instance number.')
backend = d.get('backend')
typ = d.get('type')
config_vtpm = ['vtpm']
if instance:
config_vtpm.append(['pref_instance', instance])
if backend:
config_vtpm.append(['backend', backend])
if typ:
config_vtpm.append(['type', type])
config_devs.append(['device', config_vtpm])
def configure_vifs(config_devs, vals):
@@ -682,8 +699,9 @@ def make_config(vals):
config.append([n, v])
map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
'restart', 'on_poweroff', 'on_reboot', 'on_crash',
'vcpus', 'features'])
'restart', 'on_poweroff',
'on_reboot', 'on_crash', 'vcpus', 'features',
'on_xend_start', 'on_xend_stop'])
if vals.uuid is not None:
config.append(['uuid', vals.uuid])
@@ -1151,8 +1169,11 @@ def config_security_check(config, verbose):
(res_label, res_policy) = security.get_res_label(resource)
if not res_label:
res_label = ""
print " --> res:"+res_label+" ("+res_policy+")"
print " --> dom:"+domain_label+" ("+domain_policy+")"
print " --> res: %s (%s)" % (str(res_label),
str(res_policy))
print " --> dom: %s (%s)" % (str(domain_label),
str(domain_policy))
answer = 0
return answer
+47 -4
View File
@@ -92,6 +92,15 @@ SUBCOMMAND_HELP = {
'unpause' : ('<Domain>', 'Unpause a paused domain.'),
'uptime' : ('[-s] <Domain>', 'Print uptime for a domain.'),
# Life cycle xm commands
'new' : ('<ConfigFile> [options] [vars]',
'Adds a domain to Xend domain management'),
'delete' : ('<DomainName>',
'Remove a domain from Xend domain management.'),
'start' : ('<DomainName>', 'Start a Xend managed domain'),
'resume' : ('<DomainName>', 'Resume a Xend managed domain'),
'suspend' : ('<DomainName>', 'Suspend a Xend maanged domain'),
# less used commands
'dmesg' : ('[-c|--clear]',
@@ -194,6 +203,8 @@ SUBCOMMAND_OPTIONS = {
common_commands = [
"console",
"create",
"new",
"delete",
"destroy",
"dump-core",
"help",
@@ -203,8 +214,11 @@ common_commands = [
"pause",
"reboot",
"restore",
"resume",
"save",
"shutdown",
"start",
"suspend",
"top",
"unpause",
"uptime",
@@ -214,6 +228,8 @@ common_commands = [
domain_commands = [
"console",
"create",
"new",
"delete",
"destroy",
"domid",
"domname",
@@ -226,8 +242,11 @@ domain_commands = [
"reboot",
"rename",
"restore",
"resume",
"save",
"shutdown",
"start",
"suspend",
"sysrq",
"top",
"unpause",
@@ -469,9 +488,9 @@ def xm_restore(args):
def getDomains(domain_names, full = 0):
if domain_names:
return [server.xend.domain(dom) for dom in domain_names]
return [server.xend.domain(dom, full) for dom in domain_names]
else:
return server.xend.domains(1)
return server.xend.domains(1, full)
def xm_list(args):
@@ -683,6 +702,26 @@ def xm_vcpu_list(args):
print format % locals()
def xm_start(args):
arg_check(args, "start", 1)
dom = args[0]
server.xend.domain.start(dom)
def xm_delete(args):
arg_check(args, "delete", 1)
dom = args[0]
server.xend.domain.delete(dom)
def xm_suspend(args):
arg_check(args, "suspend", 1)
dom = args[0]
server.xend.domain.suspend(dom)
def xm_resume(args):
arg_check(args, "resume", 1)
dom = args[0]
server.xend.domain.resume(dom)
def xm_reboot(args):
arg_check(args, "reboot", 1, 3)
from xen.xm import shutdown
@@ -1323,6 +1362,7 @@ commands = {
# xenstat commands
"top": xm_top,
# domain commands
"delete": xm_delete,
"destroy": xm_destroy,
"domid": xm_domid,
"domname": xm_domname,
@@ -1332,8 +1372,10 @@ commands = {
"restore": xm_restore,
"save": xm_save,
"shutdown": xm_shutdown,
"start": xm_start,
"sysrq": xm_sysrq,
"uptime": xm_uptime,
"suspend": xm_suspend,
"list": xm_list,
# memory commands
"mem-max": xm_mem_max,
@@ -1373,13 +1415,14 @@ commands = {
## The commands supported by a separate argument parser in xend.xm.
IMPORTED_COMMANDS = [
'create',
'new',
'migrate',
'labels',
'addlabel',
'cfgbootpolicy',
'makepolicy',
'loadpolicy',
'dumppolicy',
'dumppolicy',
'addlabel',
'rmlabel',
'getlabel',
'dry-run',
+68
View File
@@ -0,0 +1,68 @@
#============================================================================
# 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 Ltd
#============================================================================
import os
import xmlrpclib
from xen.xend import PrettyPrint
from xen.xend import sxp
from xen.xend import XendClient
from xen.xend.XendClient import server
from xen.xm.opts import *
from xen.xm.create import *
def make_unstarted_domain(opts, config):
"""Create an unstarted domain.
@param opts: options
@param config: configuration
"""
try:
server.xend.domain.new(config)
except xmlrpclib.Fault, ex:
import signal
if vncpid:
os.kill(vncpid, signal.SIGKILL)
if ex.faultCode == XendClient.ERROR_INVALID_DOMAIN:
err("the domain '%s' does not exist." % ex.faultString)
else:
err("%s" % ex.faultString)
except Exception, ex:
import signal
if vncpid:
os.kill(vncpid, signal.SIGKILL)
err(str(ex))
def main(argv):
try:
(opts, config) = parseCommandLine(argv)
except StandardError, ex:
err(str(ex))
if not opts:
return
if opts.vals.dryrun:
PrettyPrint.prettyprint(config)
else:
make_unstarted_domain(opts, config)
if __name__ == '__main__':
main(sys.argv)
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.