mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
Merge xen-api.hg changeset 142:d75413e65ba319d577c764be6c13efa5eae6da1c.
Signed-off-by: Ewan Mellor <ewan@xensource.com>
This commit is contained in:
commit
fb2358c250
128 files changed
+31815
-1612
No files matched your search
@@ -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
|
||||
@@ -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
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 = ''
|
||||
|
||||
@@ -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:])
|
||||
@@ -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'
|
||||
@@ -0,0 +1,7 @@
|
||||
name_label = 'VDI 1'
|
||||
name_description = ''
|
||||
virtual_size = 10 * 1024
|
||||
sector_size = 1024
|
||||
type = 'system'
|
||||
sharable = False
|
||||
read_only = False
|
||||
@@ -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'
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
import types
|
||||
import StringIO
|
||||
|
||||
import sxp
|
||||
from xen.xend import sxp
|
||||
|
||||
class ArgError(StandardError):
|
||||
pass
|
||||
|
||||
@@ -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
@@ -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']
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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/"
|
||||
|
||||
@@ -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)
|
||||
|
||||
+901
-259
File diff suppressed because it is too large.
Load diff
+1214
-1068
File diff suppressed because it is too large.
Load diff
@@ -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')
|
||||
@@ -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
|
||||
|
||||
@@ -22,7 +22,7 @@ import time
|
||||
import types
|
||||
|
||||
from encode import *
|
||||
import sxp
|
||||
from xen.xend import sxp
|
||||
|
||||
from xen.xend import XendRoot
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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
|
||||
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -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())
|
||||
@@ -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
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||