mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 02:30:03 +01:00
[POWERPC][XEN] Merge with xen-unstable.hg.
Signed-off-by: Hollis Blanchard <hollisb@us.ibm.com>
This commit is contained in:
commit
4c03b0792e
949 files changed
+68821
-30599
No files matched your search
@@ -8,7 +8,7 @@ all: build
|
||||
build:
|
||||
CC="$(CC)" CFLAGS="$(CFLAGS)" python setup.py build
|
||||
if which $(MSGFMT) >/dev/null ; then \
|
||||
for file in `cd xen/xm; find messages -name xen-xm.po`; do \
|
||||
for file in `cd ./xen/xm; find messages -name xen-xm.po`; do \
|
||||
dest=`echo "build/$$file" | \
|
||||
sed -e 's#xen-xm.po#LC_MESSAGES/xen-xm.mo#'`; \
|
||||
mkdir -p `dirname "$$dest"`; \
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
vm_cfg = {
|
||||
'name_label': 'API_HVM',
|
||||
'user_version': 1,
|
||||
'is_a_template': False,
|
||||
'auto_power_on': False, # TODO
|
||||
|
||||
'memory_static_min': 64,
|
||||
'memory_static_max': 128,
|
||||
#'memory_dynamic_min': 64,
|
||||
#'memory_dynamic_max': 128,
|
||||
|
||||
|
||||
'VCPUs_policy': 'credit',
|
||||
'VCPUs_params': {},
|
||||
'VCPUs_number': 2,
|
||||
|
||||
'actions_after_shutdown': 'destroy',
|
||||
'actions_after_reboot': 'restart',
|
||||
'actions_after_crash': 'destroy',
|
||||
|
||||
'PV_bootloader': '',
|
||||
'PV_bootloader_args': '',
|
||||
|
||||
'PV_kernel': '',
|
||||
'PV_ramdisk': '',
|
||||
'PV_args': '',
|
||||
|
||||
'HVM_boot': 'cda',
|
||||
'platform_std_VGA': False,
|
||||
'platform_serial': '',
|
||||
'platform_localtime': False,
|
||||
'platform_clock_offset': False,
|
||||
'platform_enable_audio': False,
|
||||
'PCI_bus': ''
|
||||
}
|
||||
|
||||
local_vdi_cfg = {
|
||||
'name_label': 'gentoo.hvm',
|
||||
'name_description': '',
|
||||
'location': 'file:/root/gentoo.amd64.hvm.img',
|
||||
'virtual_size': 0,
|
||||
'sector_size': 0,
|
||||
'type': 'system',
|
||||
'parent': '',
|
||||
'SR_name': 'Local',
|
||||
'sharable': False,
|
||||
'read_only': False,
|
||||
}
|
||||
|
||||
local_vbd_cfg = {
|
||||
'VDI': '',
|
||||
'VM': '',
|
||||
'device': 'hda',
|
||||
'mode': 'RW',
|
||||
'type': 'disk',
|
||||
'driver': 'ioemu',
|
||||
}
|
||||
|
||||
vif_cfg = {
|
||||
'name': 'API_VIF',
|
||||
'type': 'ioemu',
|
||||
'device': '',
|
||||
'network': '',
|
||||
'MAC': '',
|
||||
'MTU': 1500,
|
||||
}
|
||||
|
||||
console_cfg = {
|
||||
'protocol': 'rfb',
|
||||
'other_config': {'vncunused': 1, 'vncpasswd': 'testing'},
|
||||
}
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
sys.path.append('/usr/lib/python')
|
||||
|
||||
from xapi import connect, execute
|
||||
|
||||
def test_vm_create():
|
||||
server, session = connect()
|
||||
vm_uuid = None
|
||||
local_vdi_uuid = None
|
||||
local_vbd_uuid = None
|
||||
vif_uuid = None
|
||||
|
||||
# List all VMs
|
||||
vm_list = execute(server, 'VM.get_all', (session,))
|
||||
vm_names = []
|
||||
for vm_uuid in vm_list:
|
||||
vm_record = execute(server, 'VM.get_record', (session, vm_uuid))
|
||||
vm_names.append(vm_record['name_label'])
|
||||
|
||||
# Get default SR
|
||||
local_sr_list = execute(server, 'SR.get_by_name_label',
|
||||
(session, local_vdi_cfg['SR_name']))
|
||||
local_sr_uuid = local_sr_list[0]
|
||||
|
||||
# Get default network
|
||||
net_list = execute(server, 'network.get_all', (session,))
|
||||
net_uuid = net_list[0]
|
||||
|
||||
try:
|
||||
# Create a new VM
|
||||
print 'Create VM'
|
||||
vm_uuid = execute(server, 'VM.create', (session, vm_cfg))
|
||||
|
||||
print 'Create VDI'
|
||||
# Create a new VDI (Local)
|
||||
local_vdi_cfg['SR'] = local_sr_uuid
|
||||
local_vdi_uuid = execute(server, 'VDI.create',
|
||||
(session, local_vdi_cfg))
|
||||
|
||||
print 'Create VBD'
|
||||
# Create a new VBD (Local)
|
||||
local_vbd_cfg['VM'] = vm_uuid
|
||||
local_vbd_cfg['VDI'] = local_vdi_uuid
|
||||
local_vbd_uuid = execute(server, 'VBD.create',
|
||||
(session, local_vbd_cfg))
|
||||
|
||||
print 'Craete VIF'
|
||||
# Create a new VIF
|
||||
vif_cfg['network'] = net_uuid
|
||||
vif_cfg['VM'] = vm_uuid
|
||||
vif_uuid = execute(server, 'VIF.create', (session, vif_cfg))
|
||||
|
||||
# Create a console
|
||||
console_cfg['VM'] = vm_uuid
|
||||
console_uuid = execute(server, 'console.create',
|
||||
(session, console_cfg))
|
||||
print console_uuid
|
||||
|
||||
# Start the VM
|
||||
execute(server, 'VM.start', (session, vm_uuid, False))
|
||||
|
||||
time.sleep(30)
|
||||
|
||||
test_suspend = False
|
||||
if test_suspend:
|
||||
print 'Suspending VM..'
|
||||
execute(server, 'VM.suspend', (session, vm_uuid))
|
||||
print 'Suspended VM.'
|
||||
time.sleep(5)
|
||||
print 'Resuming VM ...'
|
||||
execute(server, 'VM.resume', (session, vm_uuid, False))
|
||||
print 'Resumed VM.'
|
||||
|
||||
# Wait for user to say we're good to shut it down
|
||||
while True:
|
||||
destroy = raw_input('destroy VM? ')
|
||||
if destroy[0] in ('y', 'Y'):
|
||||
break
|
||||
|
||||
finally:
|
||||
# Clean up
|
||||
if vif_uuid:
|
||||
execute(server, 'VIF.destroy', (session, vif_uuid))
|
||||
|
||||
if local_vbd_uuid:
|
||||
execute(server, 'VBD.destroy', (session, local_vbd_uuid))
|
||||
if local_vdi_uuid:
|
||||
execute(server, 'VDI.destroy', (session, local_vdi_uuid))
|
||||
|
||||
if vm_uuid:
|
||||
try:
|
||||
execute(server, 'VM.hard_shutdown', (session, vm_uuid))
|
||||
time.sleep(2)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
execute(server, 'VM.destroy', (session, vm_uuid))
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_vm_create()
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
vm_cfg = {
|
||||
'name_label': 'APIVM',
|
||||
'user_version': 1,
|
||||
'is_a_template': False,
|
||||
'auto_power_on': False, # TODO
|
||||
|
||||
'memory_static_min': 64,
|
||||
'memory_static_max': 128,
|
||||
#'memory_dynamic_min': 64,
|
||||
#'memory_dynamic_max': 128,
|
||||
|
||||
|
||||
'VCPUs_policy': 'credit',
|
||||
'VCPUs_params': '',
|
||||
'VCPUs_number': 2,
|
||||
|
||||
'actions_after_shutdown': 'destroy',
|
||||
'actions_after_reboot': 'restart',
|
||||
'actions_after_crash': 'destroy',
|
||||
|
||||
'PV_bootloader': '',
|
||||
'PV_bootloader_args': '',
|
||||
|
||||
'PV_kernel': '/boot/vmlinuz-2.6.18-xenU',
|
||||
'PV_ramdisk': '',
|
||||
'PV_args': 'root=/dev/sda1 ro',
|
||||
|
||||
#'HVM_boot': '',
|
||||
'platform_std_VGA': False,
|
||||
'platform_serial': '',
|
||||
'platform_localtime': False,
|
||||
'platform_clock_offset': False,
|
||||
'platform_enable_audio': False,
|
||||
'PCI_bus': ''
|
||||
}
|
||||
|
||||
vdi_cfg = {
|
||||
'name_label': 'API_VDI',
|
||||
'name_description': '',
|
||||
'virtual_size': 100 * 1024,
|
||||
'sector_size': 1024,
|
||||
'type': 'system',
|
||||
'parent': '',
|
||||
'SR_name': 'QCoW',
|
||||
'sharable': False,
|
||||
'read_only': False,
|
||||
}
|
||||
|
||||
vbd_cfg = {
|
||||
'VDI': '',
|
||||
'VM': '',
|
||||
'device': 'sda2',
|
||||
'mode': 'RW',
|
||||
'type': 'disk',
|
||||
'driver': 'paravirtualised',
|
||||
}
|
||||
|
||||
local_vdi_cfg = {
|
||||
'name_label': 'gentoo.amd64.img',
|
||||
'name_description': '',
|
||||
'location': 'file:/root/gentoo.amd64.img',
|
||||
'virtual_size': 0,
|
||||
'sector_size': 0,
|
||||
'type': 'system',
|
||||
'parent': '',
|
||||
'SR_name': 'Local',
|
||||
'sharable': False,
|
||||
'read_only': False,
|
||||
}
|
||||
|
||||
local_vbd_cfg = {
|
||||
'VDI': '',
|
||||
'VM': '',
|
||||
'device': 'sda1',
|
||||
'mode': 'RW',
|
||||
'type': 'disk',
|
||||
'driver': 'paravirtualised',
|
||||
}
|
||||
|
||||
vif_cfg = {
|
||||
'name': 'API_VIF',
|
||||
'type': 'paravirtualised',
|
||||
'device': '',
|
||||
'network': '',
|
||||
'MAC': '',
|
||||
'MTU': 1500,
|
||||
}
|
||||
|
||||
console_cfg = {
|
||||
'protocol': 'rfb',
|
||||
'other_config': {'vncunused': 1, 'vncpasswd': 'testing'},
|
||||
}
|
||||
|
||||
import sys
|
||||
import time
|
||||
sys.path.append('/usr/lib/python')
|
||||
|
||||
from xapi import connect, execute
|
||||
|
||||
def test_vm_create():
|
||||
server, session = connect()
|
||||
vm_uuid = None
|
||||
vdi_uuid = None
|
||||
local_vdi_uuid = None
|
||||
local_vbd_uuid = None
|
||||
vbd_uuid = None
|
||||
vif_uuid = None
|
||||
|
||||
# List all VMs
|
||||
vm_list = execute(server, 'VM.get_all', (session,))
|
||||
vm_names = []
|
||||
for vm_uuid in vm_list:
|
||||
vm_record = execute(server, 'VM.get_record', (session, vm_uuid))
|
||||
vm_names.append(vm_record['name_label'])
|
||||
|
||||
# Get default SR
|
||||
sr_list = execute(server, 'SR.get_by_name_label', (session,
|
||||
vdi_cfg['SR_name']))
|
||||
sr_uuid = sr_list[0]
|
||||
|
||||
local_sr_list = execute(server, 'SR.get_by_name_label',
|
||||
(session, local_vdi_cfg['SR_name']))
|
||||
local_sr_uuid = local_sr_list[0]
|
||||
|
||||
# Get default network
|
||||
net_list = execute(server, 'network.get_all', (session,))
|
||||
net_uuid = net_list[0]
|
||||
|
||||
try:
|
||||
# Create a new VM
|
||||
vm_uuid = execute(server, 'VM.create', (session, vm_cfg))
|
||||
|
||||
# Create a new VDI
|
||||
vdi_cfg['SR'] = sr_uuid
|
||||
vdi_uuid = execute(server, 'VDI.create', (session, vdi_cfg))
|
||||
|
||||
# Create a VDI backed VBD
|
||||
vbd_cfg['VM'] = vm_uuid
|
||||
vbd_cfg['VDI'] = vdi_uuid
|
||||
vbd_uuid = execute(server, 'VBD.create', (session, vbd_cfg))
|
||||
|
||||
# Create a new VDI (Local)
|
||||
local_vdi_cfg['SR'] = local_sr_uuid
|
||||
local_vdi_uuid = execute(server, 'VDI.create',
|
||||
(session, local_vdi_cfg))
|
||||
|
||||
# Create a new VBD (Local)
|
||||
local_vbd_cfg['VM'] = vm_uuid
|
||||
local_vbd_cfg['VDI'] = local_vdi_uuid
|
||||
local_vbd_uuid = execute(server, 'VBD.create',
|
||||
(session, local_vbd_cfg))
|
||||
|
||||
# Create a new VIF
|
||||
vif_cfg['network'] = net_uuid
|
||||
vif_cfg['VM'] = vm_uuid
|
||||
vif_uuid = execute(server, 'VIF.create', (session, vif_cfg))
|
||||
|
||||
# Create a console
|
||||
console_cfg['VM'] = vm_uuid
|
||||
console_uuid = execute(server, 'console.create',
|
||||
(session, console_cfg))
|
||||
print console_uuid
|
||||
|
||||
# Start the VM
|
||||
execute(server, 'VM.start', (session, vm_uuid, False))
|
||||
|
||||
time.sleep(30)
|
||||
|
||||
test_suspend = False
|
||||
if test_suspend:
|
||||
print 'Suspending VM..'
|
||||
execute(server, 'VM.suspend', (session, vm_uuid))
|
||||
print 'Suspended VM.'
|
||||
time.sleep(5)
|
||||
print 'Resuming VM ...'
|
||||
execute(server, 'VM.resume', (session, vm_uuid, False))
|
||||
print 'Resumed VM.'
|
||||
|
||||
finally:
|
||||
# Wait for user to say we're good to shut it down
|
||||
while True:
|
||||
destroy = raw_input('destroy VM? ')
|
||||
if destroy[0] in ('y', 'Y'):
|
||||
break
|
||||
|
||||
# Clean up
|
||||
if vif_uuid:
|
||||
execute(server, 'VIF.destroy', (session, vif_uuid))
|
||||
|
||||
if local_vbd_uuid:
|
||||
execute(server, 'VBD.destroy', (session, local_vbd_uuid))
|
||||
if local_vdi_uuid:
|
||||
execute(server, 'VDI.destroy', (session, local_vdi_uuid))
|
||||
|
||||
if vbd_uuid:
|
||||
execute(server, 'VBD.destroy', (session, vbd_uuid))
|
||||
if vdi_uuid:
|
||||
execute(server, 'VDI.destroy', (session, vdi_uuid))
|
||||
|
||||
if vm_uuid:
|
||||
try:
|
||||
execute(server, 'VM.hard_shutdown', (session, vm_uuid))
|
||||
time.sleep(2)
|
||||
except:
|
||||
pass
|
||||
|
||||
execute(server, 'VM.destroy', (session, vm_uuid))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_vm_create()
|
||||
|
||||
+452
-133
@@ -17,6 +17,9 @@
|
||||
#============================================================================
|
||||
|
||||
import sys
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
sys.path.append('/usr/lib/python')
|
||||
|
||||
from xen.util.xmlrpclib2 import ServerProxy
|
||||
@@ -25,6 +28,10 @@ from pprint import pprint
|
||||
from types import DictType
|
||||
from getpass import getpass
|
||||
|
||||
# Get default values from the environment
|
||||
SERVER_URI = os.environ.get('XAPI_SERVER_URI', 'http://localhost:9363/')
|
||||
SERVER_USER = os.environ.get('XAPI_SERVER_USER', '')
|
||||
SERVER_PASS = os.environ.get('XAPI_SERVER_PASS', '')
|
||||
|
||||
MB = 1024 * 1024
|
||||
|
||||
@@ -35,12 +42,15 @@ 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'
|
||||
VBD_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(VDI)-8s '\
|
||||
'%(image)-8s'
|
||||
VBD_LIST_FORMAT = '%(device)-6s %(uuid)-36s %(VDI)-8s'
|
||||
TASK_LIST_FORMAT = '%(name_label)-18s %(uuid)-36s %(status)-8s %(progress)-4s'
|
||||
VIF_LIST_FORMAT = '%(name)-8s %(device)-7s %(uuid)-36s %(MAC)-10s'
|
||||
CONSOLE_LIST_FORMAT = '%(uuid)-36s %(protocol)-8s %(location)-32s'
|
||||
|
||||
COMMANDS = {
|
||||
'host-info': ('', 'Get Xen Host Info'),
|
||||
'host-set-name': ('', 'Set host name'),
|
||||
'pif-list': ('', 'List all PIFs'),
|
||||
'sr-list': ('', 'List all SRs'),
|
||||
'vbd-list': ('', 'List all VBDs'),
|
||||
'vbd-create': ('<domname> <pycfg> [opts]',
|
||||
@@ -48,7 +58,7 @@ COMMANDS = {
|
||||
'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'),
|
||||
'vdi-destroy': ('<vdi_uuid>', 'Delete VDI'),
|
||||
'vif-create': ('<domname> <pycfg>', 'Create VIF attached to domname'),
|
||||
'vtpm-create' : ('<domname> <pycfg>', 'Create VTPM attached to domname'),
|
||||
|
||||
@@ -60,9 +70,23 @@ COMMANDS = {
|
||||
'vm-shutdown': ('<name> [opts]', 'Shutdown VM with name'),
|
||||
'vm-start': ('<name>', 'Start VM with name'),
|
||||
'vm-uuid': ('<name>', 'UUID of a domain by name.'),
|
||||
'async-vm-start': ('<name>', 'Start VM asynchronously'),
|
||||
}
|
||||
|
||||
OPTIONS = {
|
||||
'sr-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of SR'})
|
||||
],
|
||||
|
||||
'vdi-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of VDI'})
|
||||
],
|
||||
'vif-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of VIF'})
|
||||
],
|
||||
'vm-list': [(('-l', '--long'),
|
||||
{'action':'store_true',
|
||||
'help':'List all properties of VMs'})
|
||||
@@ -71,25 +95,28 @@ OPTIONS = {
|
||||
'action': 'store_true'})],
|
||||
|
||||
'vdi-create': [(('--name-label',), {'help': 'Name for VDI'}),
|
||||
(('--description',), {'help': 'Description for VDI'}),
|
||||
(('--name-description',), {'help': 'Description for VDI'}),
|
||||
(('--sector-size',), {'type': 'int',
|
||||
'help': 'Sector size'}),
|
||||
'help': 'Sector size',
|
||||
'default': 0}),
|
||||
(('--virtual-size',), {'type': 'int',
|
||||
'default': 0,
|
||||
'help': 'Size of VDI in sectors'}),
|
||||
(('--type',), {'choices': ['system', 'user', 'ephemeral'],
|
||||
'default': 'system',
|
||||
'help': 'VDI type'}),
|
||||
(('--sharable',), {'action': 'store_true',
|
||||
'help': 'VDI sharable'}),
|
||||
(('--read-only',), {'action': 'store_true',
|
||||
'help': 'Read only'})],
|
||||
'help': 'Read only'}),
|
||||
(('--sr',), {})],
|
||||
|
||||
'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.'})]
|
||||
(('--device',), {'help': 'Device name on guest domain'})]
|
||||
|
||||
}
|
||||
|
||||
@@ -130,8 +157,13 @@ def parse_args(cmd_name, args, set_defaults = False):
|
||||
values = defaults)
|
||||
return opts, extraargs
|
||||
|
||||
def execute(fn, *args):
|
||||
result = fn(*args)
|
||||
def execute(server, fn, args, async = False):
|
||||
if async:
|
||||
func = eval('server.Async.%s' % fn)
|
||||
else:
|
||||
func = eval('server.%s' % fn)
|
||||
|
||||
result = func(*args)
|
||||
if type(result) != DictType:
|
||||
raise TypeError("Function returned object of type: %s" %
|
||||
str(type(result)))
|
||||
@@ -142,14 +174,23 @@ def execute(fn, *args):
|
||||
_initialised = False
|
||||
_server = None
|
||||
_session = None
|
||||
def _connect(*args):
|
||||
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)
|
||||
# try without password or default credentials
|
||||
try:
|
||||
_server = ServerProxy(SERVER_URI)
|
||||
_session = execute(_server.session, 'login_with_password',
|
||||
(SERVER_USER, SERVER_PASS))
|
||||
except:
|
||||
login = raw_input("Login: ")
|
||||
password = getpass()
|
||||
creds = (login, password)
|
||||
_server = ServerProxy(SERVER_URI)
|
||||
_session = execute(_server.session, 'login_with_password',
|
||||
creds)
|
||||
|
||||
_initialised = True
|
||||
return (_server, _session)
|
||||
|
||||
@@ -162,14 +203,14 @@ def _read_python_cfg(filename):
|
||||
return cfg
|
||||
|
||||
def resolve_vm(server, session, vm_name):
|
||||
vm_uuid = execute(server.VM.get_by_name_label, 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]
|
||||
|
||||
def resolve_vdi(server, session, vdi_name):
|
||||
vdi_uuid = execute(server.VDI.get_by_name_label, session, vdi_name)
|
||||
vdi_uuid = execute(server, 'VDI.get_by_name_label', (session, vdi_name))
|
||||
if not vdi_uuid:
|
||||
return None
|
||||
else:
|
||||
@@ -179,50 +220,58 @@ def resolve_vdi(server, session, vdi_name):
|
||||
# Actual commands
|
||||
#
|
||||
|
||||
def xapi_host_info(*args):
|
||||
server, session = _connect()
|
||||
hosts = execute(server.host.get_all, session)
|
||||
def xapi_host_info(args, async = False):
|
||||
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)
|
||||
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_host_set_name(*args):
|
||||
for host_cpu_uuid in hostinfo['host_CPUs']:
|
||||
host_cpu = execute(server, 'host_cpu.get_record',
|
||||
(session, host_cpu_uuid))
|
||||
print 'CPU %s Util: %.2f' % (host_cpu['number'],
|
||||
float(host_cpu['utilisation']))
|
||||
|
||||
def xapi_host_set_name(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No hostname specified")
|
||||
|
||||
server, session = _connect()
|
||||
hosts = execute(server.host.get_all, session)
|
||||
server, session = connect()
|
||||
hosts = execute(server, 'host.get_all', (session,))
|
||||
if len(hosts) > 0:
|
||||
execute(server.host.set_name_label, session, hosts[0], args[0])
|
||||
print 'Hostname: %s' % execute(server.host.get_name_label, session,
|
||||
hosts[0])
|
||||
execute(server, 'host.set_name_label', (session, hosts[0], args[0]))
|
||||
print 'Hostname: %s' % execute(server, 'host.get_name_label',
|
||||
(session, hosts[0]))
|
||||
|
||||
def xapi_vm_uuid(*args):
|
||||
def xapi_vm_uuid(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No domain name specified")
|
||||
|
||||
server, session = _connect()
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, args[0])
|
||||
print vm_uuid
|
||||
|
||||
def xapi_vm_name(*args):
|
||||
def xapi_vm_name(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No UUID specified")
|
||||
|
||||
server, session = _connect()
|
||||
vm_name = execute(server.VM.get_name_label, session, args[0])
|
||||
server, session = connect()
|
||||
vm_name = execute(server, 'VM.get_name_label', (session, args[0]))
|
||||
print vm_name
|
||||
|
||||
def xapi_vm_list(*args):
|
||||
def xapi_vm_list(args, async = False):
|
||||
opts, args = parse_args('vm-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
list_only = args
|
||||
|
||||
server, session = _connect()
|
||||
vm_uuids = execute(server.VM.get_all, session)
|
||||
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',
|
||||
@@ -231,7 +280,12 @@ def xapi_vm_list(*args):
|
||||
'uuid': 'UUID'}
|
||||
|
||||
for uuid in vm_uuids:
|
||||
vm_info = execute(server.VM.get_record, session, uuid)
|
||||
vm_info = execute(server, 'VM.get_record', (session, uuid))
|
||||
|
||||
# skip domain if we don't want
|
||||
if list_only and vm_info['name_label'] not in list_only:
|
||||
continue
|
||||
|
||||
if is_long:
|
||||
vbds = vm_info['VBDs']
|
||||
vifs = vm_info['VIFs']
|
||||
@@ -240,13 +294,13 @@ def xapi_vm_list(*args):
|
||||
vbd_infos = []
|
||||
vtpm_infos = []
|
||||
for vbd in vbds:
|
||||
vbd_info = execute(server.VBD.get_record, session, vbd)
|
||||
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_info = execute(server, 'VIF.get_record', (session, vif))
|
||||
vif_infos.append(vif_info)
|
||||
for vtpm in vtpms:
|
||||
vtpm_info = execute(server.VTPM.get_record, session, vtpm)
|
||||
vtpm_info = execute(server, 'VTPM.get_record', (session, vtpm))
|
||||
vtpm_infos.append(vtpm_info)
|
||||
vm_info['VBDs'] = vbd_infos
|
||||
vm_info['VIFs'] = vif_infos
|
||||
@@ -255,7 +309,7 @@ def xapi_vm_list(*args):
|
||||
else:
|
||||
print VM_LIST_FORMAT % _stringify(vm_info)
|
||||
|
||||
def xapi_vm_create(*args):
|
||||
def xapi_vm_create(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("Configuration file not specified")
|
||||
|
||||
@@ -263,49 +317,129 @@ def xapi_vm_create(*args):
|
||||
cfg = _read_python_cfg(filename)
|
||||
|
||||
print 'Creating VM from %s ..' % filename
|
||||
server, session = _connect()
|
||||
uuid = execute(server.VM.create, session, cfg)
|
||||
server, session = connect()
|
||||
uuid = execute(server, 'VM.create', (session, cfg), async = async)
|
||||
print 'Done. (%s)' % uuid
|
||||
print uuid
|
||||
|
||||
def xapi_vm_destroy(*args):
|
||||
def xapi_vm_destroy(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No domain name specified.")
|
||||
|
||||
server, session = _connect()
|
||||
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)
|
||||
success = execute(server, 'VM.destroy', (session, vm_uuid), async = async)
|
||||
print 'Done.'
|
||||
|
||||
|
||||
def xapi_vm_start(*args):
|
||||
def xapi_vm_start(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = _connect()
|
||||
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, False)
|
||||
print 'Done.'
|
||||
success = execute(server, 'VM.start', (session, vm_uuid, False), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_vm_shutdown(*args):
|
||||
def xapi_vm_suspend(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, args[0])
|
||||
print 'Suspending VM %s (%s)' % (args[0], vm_uuid)
|
||||
success = execute(server, 'VM.suspend', (session, vm_uuid), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
|
||||
def xapi_vm_resume(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, args[0])
|
||||
print 'Resuming VM %s (%s)' % (args[0], vm_uuid)
|
||||
success = execute(server, 'VM.resume', (session, vm_uuid, False), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_vm_pause(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, args[0])
|
||||
print 'Pausing VM %s (%s)' % (args[0], vm_uuid)
|
||||
success = execute(server, 'VM.pause', (session, vm_uuid), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_vm_unpause(args, async = False):
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, args[0])
|
||||
print 'Pausing VM %s (%s)' % (args[0], vm_uuid)
|
||||
success = execute(server, 'VM.unpause', (session, vm_uuid), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_task_list(args, async = False):
|
||||
server, session = connect()
|
||||
all_tasks = execute(server, 'task.get_all', (session,))
|
||||
|
||||
print TASK_LIST_FORMAT % {'name_label': 'Task Name',
|
||||
'uuid': 'UUID',
|
||||
'status': 'Status',
|
||||
'progress': '%'}
|
||||
|
||||
for task_uuid in all_tasks:
|
||||
task = execute(server, 'task.get_record', (session, task_uuid))
|
||||
print TASK_LIST_FORMAT % task
|
||||
|
||||
def xapi_task_clear(args, async = False):
|
||||
server, session = connect()
|
||||
all_tasks = execute(server, 'task.get_all', (session,))
|
||||
for task_uuid in all_tasks:
|
||||
success = execute(server, 'task.destroy', (session, task_uuid))
|
||||
print 'Destroyed Task %s' % task_uuid
|
||||
|
||||
def xapi_vm_shutdown(args, async = False):
|
||||
opts, args = parse_args("vm-shutdown", args, set_defaults = True)
|
||||
|
||||
if len(args) < 1:
|
||||
raise OptionError("No Domain name specified.")
|
||||
|
||||
server, session = _connect()
|
||||
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)
|
||||
success = execute(server, 'VM.hard_shutdown', (session, vm_uuid), async = async)
|
||||
else:
|
||||
print 'Shutting down VM %s (%s)' % (args[0], vm_uuid)
|
||||
success = execute(server.VM.clean_shutdown, session, vm_uuid)
|
||||
print 'Done.'
|
||||
success = execute(server, 'VM.clean_shutdown', (session, vm_uuid), async = async)
|
||||
|
||||
def xapi_vbd_create(*args):
|
||||
if async:
|
||||
print 'Task started: %s' % success
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_vbd_create(args, async = False):
|
||||
opts, args = parse_args('vbd-create', args)
|
||||
|
||||
if len(args) < 2:
|
||||
@@ -323,13 +457,16 @@ def xapi_vbd_create(*args):
|
||||
cfg[opt] = val
|
||||
|
||||
print 'Creating VBD ...',
|
||||
server, session = _connect()
|
||||
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
|
||||
vbd_uuid = execute(server, 'VBD.create', (session, cfg), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % vbd_uuid
|
||||
else:
|
||||
print 'Done. (%s)' % vbd_uuid
|
||||
|
||||
def xapi_vif_create(*args):
|
||||
def xapi_vif_create(args, async = False):
|
||||
if len(args) < 2:
|
||||
raise OptionError("Configuration file not specified")
|
||||
|
||||
@@ -338,54 +475,139 @@ def xapi_vif_create(*args):
|
||||
cfg = _read_python_cfg(filename)
|
||||
|
||||
print 'Creating VIF from %s ..' % filename
|
||||
server, session = _connect()
|
||||
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
|
||||
vif_uuid = execute(server, 'VIF.create', (session, cfg), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % vif_uuid
|
||||
else:
|
||||
print 'Done. (%s)' % vif_uuid
|
||||
|
||||
def xapi_vbd_list(*args):
|
||||
server, session = _connect()
|
||||
def xapi_vbd_list(args, async = False):
|
||||
server, session = connect()
|
||||
domname = args[0]
|
||||
|
||||
dom_uuid = resolve_vm(server, session, domname)
|
||||
vbds = execute(server.VM.get_VBDs, session, dom_uuid)
|
||||
vbds = execute(server, 'VM.get_VBDs', (session, dom_uuid))
|
||||
|
||||
print VBD_LIST_FORMAT % {'name_label': 'VDI Label',
|
||||
print VBD_LIST_FORMAT % {'device': 'Device',
|
||||
'uuid' : 'UUID',
|
||||
'VDI': 'VDI',
|
||||
'image': 'Image'}
|
||||
'VDI': 'VDI'}
|
||||
|
||||
for vbd in vbds:
|
||||
vbd_struct = execute(server.VBD.get_record, session, vbd)
|
||||
vbd_struct = execute(server, 'VBD.get_record', (session, vbd))
|
||||
print VBD_LIST_FORMAT % vbd_struct
|
||||
|
||||
|
||||
def xapi_vbd_stats(args, async = False):
|
||||
server, session = connect()
|
||||
domname = args[0]
|
||||
dom_uuid = resolve_vm(server, session, domname)
|
||||
|
||||
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'}
|
||||
vbds = execute(server, 'VM.get_VBDs', (session, dom_uuid))
|
||||
for vbd_uuid in vbds:
|
||||
print execute(server, 'VBD.get_io_read_kbs', (session, vbd_uuid))
|
||||
|
||||
def xapi_vif_list(args, async = False):
|
||||
server, session = connect()
|
||||
opts, args = parse_args('vdi-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server.VDI.get_record, session, vdi)
|
||||
print VDI_LIST_FORMAT % vdi_struct
|
||||
domname = args[0]
|
||||
|
||||
dom_uuid = resolve_vm(server, session, domname)
|
||||
vifs = execute(server, 'VM.get_VIFs', (session, dom_uuid))
|
||||
|
||||
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
|
||||
if not is_long:
|
||||
print VIF_LIST_FORMAT % {'name': 'Name',
|
||||
'device': 'Device',
|
||||
'uuid' : 'UUID',
|
||||
'MAC': 'MAC'}
|
||||
|
||||
for vif in vifs:
|
||||
vif_struct = execute(server, 'VIF.get_record', (session, vif))
|
||||
print VIF_LIST_FORMAT % vif_struct
|
||||
else:
|
||||
for vif in vifs:
|
||||
vif_struct = execute(server, 'VIF.get_record', (session, vif))
|
||||
pprint(vif_struct)
|
||||
|
||||
def xapi_vdi_create(*args):
|
||||
def xapi_console_list(args, async = False):
|
||||
server, session = connect()
|
||||
opts, args = parse_args('vdi-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
domname = args[0]
|
||||
|
||||
dom_uuid = resolve_vm(server, session, domname)
|
||||
consoles = execute(server, 'VM.get_consoles', (session, dom_uuid))
|
||||
|
||||
if not is_long:
|
||||
print CONSOLE_LIST_FORMAT % {'protocol': 'Protocol',
|
||||
'location': 'Location',
|
||||
'uuid': 'UUID'}
|
||||
|
||||
for console in consoles:
|
||||
console_struct = execute(server, 'console.get_record',
|
||||
(session, console))
|
||||
print CONSOLE_LIST_FORMAT % console_struct
|
||||
else:
|
||||
for console in consoles:
|
||||
console_struct = execute(server, 'console.get_record',
|
||||
(session, console))
|
||||
pprint(console_struct)
|
||||
|
||||
|
||||
def xapi_vdi_list(args, async = False):
|
||||
opts, args = parse_args('vdi-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
server, session = connect()
|
||||
vdis = execute(server, 'VDI.get_all', (session,))
|
||||
|
||||
if not is_long:
|
||||
print VDI_LIST_FORMAT % {'name_label': 'VDI Label',
|
||||
'uuid' : 'UUID',
|
||||
'virtual_size': 'Sectors',
|
||||
'sector_size': 'Sector Size'}
|
||||
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server, 'VDI.get_record', (session, vdi))
|
||||
print VDI_LIST_FORMAT % vdi_struct
|
||||
|
||||
else:
|
||||
for vdi in vdis:
|
||||
vdi_struct = execute(server, 'VDI.get_record', (session, vdi))
|
||||
pprint(vdi_struct)
|
||||
|
||||
def xapi_sr_list(args, async = False):
|
||||
opts, args = parse_args('sr-list', args, set_defaults = True)
|
||||
is_long = opts and opts.long
|
||||
|
||||
server, session = connect()
|
||||
srs = execute(server, 'SR.get_all', (session,))
|
||||
if not is_long:
|
||||
print SR_LIST_FORMAT % {'name_label': 'SR Label',
|
||||
'uuid' : 'UUID',
|
||||
'physical_size': 'Size (MB)',
|
||||
'type': 'Type'}
|
||||
|
||||
for sr in srs:
|
||||
sr_struct = execute(server, 'SR.get_record', (session, sr))
|
||||
sr_struct['physical_size'] = int(sr_struct['physical_size'])/MB
|
||||
print SR_LIST_FORMAT % sr_struct
|
||||
else:
|
||||
for sr in srs:
|
||||
sr_struct = execute(server, 'SR.get_record', (session, sr))
|
||||
pprint(sr_struct)
|
||||
|
||||
def xapi_sr_rename(args, async = False):
|
||||
server, session = connect()
|
||||
sr = execute(server, 'SR.get_by_name_label', (session, args[0]))
|
||||
execute(server, 'SR.set_name_label', (session, sr[0], args[1]))
|
||||
|
||||
def xapi_vdi_create(args, async = False):
|
||||
opts, args = parse_args('vdi-create', args)
|
||||
|
||||
if len(args) > 0:
|
||||
@@ -396,58 +618,106 @@ def xapi_vdi_create(*args):
|
||||
for opt, val in opts:
|
||||
cfg[opt] = val
|
||||
|
||||
server, session = _connect()
|
||||
srs = execute(server.SR.get_all, session)
|
||||
server, session = connect()
|
||||
srs = []
|
||||
if cfg.get('SR'):
|
||||
srs = execute(server, 'SR.get_by_name_label', (session, cfg['SR']))
|
||||
else:
|
||||
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
|
||||
uuid = execute(server, 'VDI.create', (session, cfg), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % uuid
|
||||
else:
|
||||
print 'Done. (%s)' % uuid
|
||||
|
||||
|
||||
def xapi_vdi_delete(*args):
|
||||
server, session = _connect()
|
||||
def xapi_vdi_destroy(args, async = False):
|
||||
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.'
|
||||
result = execute(server, 'VDI.destroy', (session, vdi_uuid), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % result
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
def xapi_vdi_rename(*args):
|
||||
server, session = _connect()
|
||||
def xapi_vdi_rename(args, async = False):
|
||||
server, session = connect()
|
||||
if len(args) < 2:
|
||||
raise OptionError('Not enough arguments')
|
||||
|
||||
vdi_uuid = args[0]
|
||||
vdi_uuid = execute(server, 'VDI.get_by_name_label', session, args[0])
|
||||
vdi_name = args[1]
|
||||
print 'Renaming VDI %s to %s' % (vdi_uuid, vdi_name)
|
||||
result = execute(server.VDI.set_name_label, session, vdi_uuid, vdi_name)
|
||||
print 'Done.'
|
||||
|
||||
print 'Renaming VDI %s to %s' % (vdi_uuid[0], vdi_name)
|
||||
result = execute(server, 'VDI.set_name_label',
|
||||
(session, vdi_uuid[0], vdi_name), async = async)
|
||||
if async:
|
||||
print 'Task started: %s' % result
|
||||
else:
|
||||
print 'Done.'
|
||||
|
||||
|
||||
def xapi_vtpm_create(*args):
|
||||
server, session = _connect()
|
||||
|
||||
def xapi_vtpm_create(args, async = False):
|
||||
server, session = connect()
|
||||
domname = args[0]
|
||||
cfg = _read_python_cfg(args[1])
|
||||
|
||||
vm_uuid = resolve_vm(server, session, domname)
|
||||
cfg['VM'] = vm_uuid
|
||||
print "Creating vTPM with cfg = %s" % cfg
|
||||
vtpm_uuid = execute(server.VTPM.create, session, cfg)
|
||||
vtpm_uuid = execute(server, 'VTPM.create', (session, cfg))
|
||||
print "Done. (%s)" % vtpm_uuid
|
||||
vtpm_id = execute(server.VTPM.get_instance, session, vtpm_uuid)
|
||||
print "Has instance number '%s'" % vtpm_id
|
||||
vtpm_be = execute(server.VTPM.get_backend, session, vtpm_uuid)
|
||||
print "Has backend in '%s'" % vtpm_be
|
||||
driver = execute(server.VTPM.get_driver, session, vtpm_uuid)
|
||||
print "Has driver type '%s'" % driver
|
||||
vtpm_rec = execute(server.VTPM.get_record, session, vtpm_uuid)
|
||||
print "Has vtpm record '%s'" % vtpm_rec
|
||||
|
||||
|
||||
def xapi_pif_list(args, async = False):
|
||||
server, session = connect()
|
||||
pif_uuids = execute(server, 'PIF.get_all', (session,))
|
||||
for pif_uuid in pif_uuids:
|
||||
pif = execute(server, 'PIF.get_record', (session, pif_uuid))
|
||||
print pif
|
||||
|
||||
|
||||
def xapi_debug_wait(args, async = False):
|
||||
secs = 10
|
||||
if len(args) > 0:
|
||||
secs = int(args[0])
|
||||
server, session = connect()
|
||||
task_uuid = execute(server, 'debug.wait', (session, secs), async=async)
|
||||
print 'Task UUID: %s' % task_uuid
|
||||
|
||||
def xapi_vm_stat(args, async = False):
|
||||
domname = args[0]
|
||||
|
||||
server, session = connect()
|
||||
vm_uuid = resolve_vm(server, session, domname)
|
||||
vif_uuids = execute(server, 'VM.get_VIFs', (session, vm_uuid))
|
||||
vbd_uuids = execute(server, 'VM.get_VBDs', (session, vm_uuid))
|
||||
vcpus_utils = execute(server, 'VM.get_VCPUs_utilisation',
|
||||
(session, vm_uuid))
|
||||
|
||||
for vcpu_num in sorted(vcpus_utils.keys()):
|
||||
print 'CPU %s : %5.2f%%' % (vcpu_num, vcpus_utils[vcpu_num] * 100)
|
||||
|
||||
for vif_uuid in vif_uuids:
|
||||
vif = execute(server, 'VIF.get_record', (session, vif_uuid))
|
||||
print '%(device)s: rx: %(io_read_kbs)10.2f tx: %(io_write_kbs)10.2f' \
|
||||
% vif
|
||||
for vbd_uuid in vbd_uuids:
|
||||
vbd = execute(server, 'VBD.get_record', (session, vbd_uuid))
|
||||
print '%(device)s: rd: %(io_read_kbs)10.2f wr: %(io_write_kbs)10.2f' \
|
||||
% vbd
|
||||
|
||||
#
|
||||
# Command Line Utils
|
||||
#
|
||||
@@ -465,12 +735,17 @@ class XenAPICmd(cmd.Cmd):
|
||||
words = shlex.split(line)
|
||||
if len(words) > 0:
|
||||
cmd_name = words[0].replace('-', '_')
|
||||
is_async = 'async' in cmd_name
|
||||
if is_async:
|
||||
cmd_name = re.sub('async_', '', cmd_name)
|
||||
|
||||
func_name = 'xapi_%s' % cmd_name
|
||||
func = globals().get(func_name)
|
||||
|
||||
if func:
|
||||
try:
|
||||
args = tuple(words[1:])
|
||||
func(*args)
|
||||
func(args, async = is_async)
|
||||
return True
|
||||
except SystemExit:
|
||||
return False
|
||||
@@ -506,7 +781,7 @@ class XenAPICmd(cmd.Cmd):
|
||||
return line
|
||||
|
||||
def shell():
|
||||
server, session = _connect()
|
||||
server, session = connect()
|
||||
x = XenAPICmd(server, session)
|
||||
x.cmdloop('Xen API Prompt. Type "help" for a list of functions')
|
||||
|
||||
@@ -517,22 +792,70 @@ def usage(command = None, print_usage = True):
|
||||
print
|
||||
print 'Subcommands:'
|
||||
print
|
||||
sorted_commands = sorted(COMMANDS.keys())
|
||||
for command in sorted_commands:
|
||||
args, description = COMMANDS[command]
|
||||
print '%-16s %-40s' % (command, description)
|
||||
|
||||
for func in sorted(globals().keys()):
|
||||
if func.startswith('xapi_'):
|
||||
command = func[5:].replace('_', '-')
|
||||
args, description = COMMANDS.get(command, ('', ''))
|
||||
print '%-16s %-40s' % (command, description)
|
||||
print
|
||||
else:
|
||||
parse_args(command, ['-h'])
|
||||
|
||||
def main(args):
|
||||
|
||||
# poor man's optparse that doesn't abort on unrecognised opts
|
||||
|
||||
if len(args) < 1 or args[0] in ('-h', '--help', 'help'):
|
||||
options = {}
|
||||
remaining = []
|
||||
|
||||
arg_n = 0
|
||||
while args:
|
||||
arg = args.pop(0)
|
||||
|
||||
if arg in ('--help', '-h'):
|
||||
options['help'] = True
|
||||
elif arg in ('--server', '-s') and args:
|
||||
options['server'] = args.pop(0)
|
||||
elif arg in ('--user', '-u') and args:
|
||||
options['user'] = args.pop(0)
|
||||
elif arg in ('--password', '-p') and args:
|
||||
options['password'] = args.pop(0)
|
||||
else:
|
||||
remaining.append(arg)
|
||||
|
||||
# abort here if these conditions are true
|
||||
|
||||
if options.get('help') and not remaining:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
subcmd = args[0]
|
||||
subcmd_func_name = 'xapi_' + subcmd.replace('-', '_')
|
||||
if options.get('help') and remaining:
|
||||
usage(remaining[0])
|
||||
sys.exit(1)
|
||||
|
||||
if not remaining:
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if options.get('server'):
|
||||
# it is ugly to use a global, but it is simple
|
||||
global SERVER_URI
|
||||
SERVER_URI = options['server']
|
||||
|
||||
if options.get('user'):
|
||||
global SERVER_USER
|
||||
SERVER_USER = options['user']
|
||||
|
||||
if options.get('password'):
|
||||
global SERVER_PASS
|
||||
SERVER_PASS = options['password']
|
||||
|
||||
subcmd = remaining[0].replace('-', '_')
|
||||
is_async = 'async' in subcmd
|
||||
if is_async:
|
||||
subcmd = re.sub('async_', '', subcmd)
|
||||
subcmd_func_name = 'xapi_' + subcmd
|
||||
subcmd_func = globals().get(subcmd_func_name, None)
|
||||
|
||||
if subcmd == 'shell':
|
||||
@@ -542,14 +865,10 @@ def main(args):
|
||||
usage()
|
||||
sys.exit(1)
|
||||
|
||||
if '-h' in args[1:] or '--help' in args[1:]:
|
||||
usage(subcmd)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
subcmd_func(*args[1:])
|
||||
subcmd_func(remaining[1:], async = is_async)
|
||||
except XenAPIError, e:
|
||||
print 'Error: %s' % str(e.args[1])
|
||||
print 'Error: %s' % str(e.args[0])
|
||||
sys.exit(2)
|
||||
except OptionError, e:
|
||||
print 'Error: %s' % e
|
||||
|
||||
+12
-1
@@ -30,6 +30,13 @@ xs = Extension("xs",
|
||||
libraries = libraries,
|
||||
sources = [ "xen/lowlevel/xs/xs.c" ])
|
||||
|
||||
scf = Extension("scf",
|
||||
extra_compile_args = extra_compile_args,
|
||||
include_dirs = include_dirs + [ "xen/lowlevel/scf" ],
|
||||
library_dirs = library_dirs,
|
||||
libraries = libraries,
|
||||
sources = [ "xen/lowlevel/scf/scf.c" ])
|
||||
|
||||
acm = Extension("acm",
|
||||
extra_compile_args = extra_compile_args,
|
||||
include_dirs = include_dirs + [ "xen/lowlevel/acm" ],
|
||||
@@ -37,6 +44,10 @@ acm = Extension("acm",
|
||||
libraries = libraries,
|
||||
sources = [ "xen/lowlevel/acm/acm.c" ])
|
||||
|
||||
modules = [ xc, xs, acm ]
|
||||
if os.uname()[0] == 'SunOS':
|
||||
modules.append(scf)
|
||||
|
||||
setup(name = 'xen',
|
||||
version = '3.0',
|
||||
description = 'Xen',
|
||||
@@ -56,7 +67,7 @@ setup(name = 'xen',
|
||||
'xen.xm.tests'
|
||||
],
|
||||
ext_package = "xen.lowlevel",
|
||||
ext_modules = [ xc, xs, acm ]
|
||||
ext_modules = modules
|
||||
)
|
||||
|
||||
os.chdir('logging')
|
||||
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <libscf.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define XEND_FMRI "svc:/system/xen/xend:default"
|
||||
#define XEND_PG "config"
|
||||
|
||||
static PyObject *scf_exc;
|
||||
|
||||
static void *
|
||||
scf_exception(const char *err, const char *value)
|
||||
{
|
||||
int scferr = scf_error();
|
||||
const char *scfstrerr = scf_strerror(scferr);
|
||||
PyObject *obj = Py_BuildValue("(isss)", scferr, err, scfstrerr, value);
|
||||
PyErr_SetObject(scf_exc, obj);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyscf_get_bool(PyObject *o, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = { "name", NULL };
|
||||
scf_simple_prop_t *prop;
|
||||
uint8_t *val;
|
||||
char *name;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
|
||||
return (NULL);
|
||||
|
||||
prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
|
||||
|
||||
if (prop == NULL)
|
||||
return (scf_exception("scf_simple_prop_get() failed", name));
|
||||
|
||||
if ((val = scf_simple_prop_next_boolean(prop)) == NULL)
|
||||
return (scf_exception("scf_simple_prop_next_boolean() failed",
|
||||
name));
|
||||
|
||||
if (*val) {
|
||||
scf_simple_prop_free(prop);
|
||||
Py_INCREF(Py_True);
|
||||
return (Py_True);
|
||||
}
|
||||
|
||||
scf_simple_prop_free(prop);
|
||||
Py_INCREF(Py_False);
|
||||
return (Py_False);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyscf_get_int(PyObject *o, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = { "name", NULL };
|
||||
scf_simple_prop_t *prop;
|
||||
PyObject *obj;
|
||||
int64_t *val;
|
||||
char *name;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
|
||||
return (NULL);
|
||||
|
||||
prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
|
||||
|
||||
if (prop == NULL)
|
||||
return (scf_exception("scf_simple_prop_get() failed", name));
|
||||
|
||||
if ((val = scf_simple_prop_next_integer(prop)) == NULL)
|
||||
return (scf_exception("scf_simple_prop_next_integer() failed",
|
||||
name));
|
||||
|
||||
obj = PyInt_FromLong((long)*val);
|
||||
scf_simple_prop_free(prop);
|
||||
return (obj);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
pyscf_get_string(PyObject *o, PyObject *args, PyObject *kwargs)
|
||||
{
|
||||
static char *kwlist[] = { "name", NULL };
|
||||
scf_simple_prop_t *prop;
|
||||
PyObject *obj;
|
||||
char *name;
|
||||
char *str;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name))
|
||||
return (NULL);
|
||||
|
||||
prop = scf_simple_prop_get(NULL, XEND_FMRI, XEND_PG, name);
|
||||
|
||||
if (prop == NULL)
|
||||
return (scf_exception("scf_simple_prop_get() failed", name));
|
||||
|
||||
if ((str = scf_simple_prop_next_astring(prop)) == NULL) {
|
||||
scf_simple_prop_free(prop);
|
||||
return (scf_exception("scf_simple_prop_next_astring() failed",
|
||||
name));
|
||||
}
|
||||
|
||||
obj = PyString_FromString(str);
|
||||
scf_simple_prop_free(prop);
|
||||
return (obj);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pyscf_get_bool__doc__,
|
||||
"get_bool(name) - get the value of the named boolean property");
|
||||
PyDoc_STRVAR(pyscf_get_int__doc__,
|
||||
"get_int(name) - get the value of the named integer property");
|
||||
PyDoc_STRVAR(pyscf_get_string__doc__,
|
||||
"get_string(name) - get the value of the named string property");
|
||||
|
||||
static struct PyMethodDef pyscf_module_methods[] = {
|
||||
{ "get_bool", (PyCFunction) pyscf_get_bool,
|
||||
METH_VARARGS|METH_KEYWORDS, pyscf_get_bool__doc__ },
|
||||
{ "get_int", (PyCFunction) pyscf_get_int,
|
||||
METH_VARARGS|METH_KEYWORDS, pyscf_get_int__doc__ },
|
||||
{ "get_string", (PyCFunction) pyscf_get_string,
|
||||
METH_VARARGS|METH_KEYWORDS, pyscf_get_string__doc__ },
|
||||
{ NULL, NULL, 0, NULL }
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
initscf(void)
|
||||
{
|
||||
PyObject *m;
|
||||
m = Py_InitModule("scf", pyscf_module_methods);
|
||||
|
||||
scf_exc = PyErr_NewException("scf.error", NULL, NULL);
|
||||
Py_INCREF(scf_exc);
|
||||
PyModule_AddObject(m, "error", scf_exc);
|
||||
PyModule_AddIntConstant(m, "SCF_ERROR_NOT_FOUND", SCF_ERROR_NOT_FOUND);
|
||||
}
|
||||
@@ -160,6 +160,24 @@ static PyObject *pyxc_domain_destroy(XcObject *self, PyObject *args)
|
||||
return dom_op(self, args, xc_domain_destroy);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_domain_shutdown(XcObject *self, PyObject *args)
|
||||
{
|
||||
uint32_t dom, reason;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii", &dom, &reason))
|
||||
return NULL;
|
||||
|
||||
if (xc_domain_shutdown(self->xc_handle, dom, reason) != 0)
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_domain_resume(XcObject *self, PyObject *args)
|
||||
{
|
||||
return dom_op(self, args, xc_domain_resume);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_vcpu_setaffinity(XcObject *self,
|
||||
PyObject *args,
|
||||
@@ -460,6 +478,24 @@ static PyObject *pyxc_evtchn_alloc_unbound(XcObject *self,
|
||||
return PyInt_FromLong(port);
|
||||
}
|
||||
|
||||
static PyObject *pyxc_evtchn_reset(XcObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
uint32_t dom;
|
||||
|
||||
static char *kwd_list[] = { "dom", NULL };
|
||||
|
||||
if ( !PyArg_ParseTupleAndKeywords(args, kwds, "i", kwd_list, &dom) )
|
||||
return NULL;
|
||||
|
||||
if ( xc_evtchn_reset(self->xc_handle, dom) < 0 )
|
||||
return pyxc_error_to_exception();
|
||||
|
||||
Py_INCREF(zero);
|
||||
return zero;
|
||||
}
|
||||
|
||||
static PyObject *pyxc_physdev_pci_access_modify(XcObject *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
@@ -1028,6 +1064,21 @@ static PyMethodDef pyxc_methods[] = {
|
||||
" dom [int]: Identifier of domain to be destroyed.\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "domain_resume",
|
||||
(PyCFunction)pyxc_domain_resume,
|
||||
METH_VARARGS, "\n"
|
||||
"Resume execution of a suspended domain.\n"
|
||||
" dom [int]: Identifier of domain to be resumed.\n\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "domain_shutdown",
|
||||
(PyCFunction)pyxc_domain_shutdown,
|
||||
METH_VARARGS, "\n"
|
||||
"Shutdown a domain.\n"
|
||||
" dom [int, 0]: Domain identifier to use.\n"
|
||||
" reason [int, 0]: Reason for shutdown.\n"
|
||||
"Returns: [int] 0 on success; -1 on error.\n" },
|
||||
|
||||
{ "vcpu_setaffinity",
|
||||
(PyCFunction)pyxc_vcpu_setaffinity,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
@@ -1168,6 +1219,12 @@ static PyMethodDef pyxc_methods[] = {
|
||||
" remote_dom [int]: Remote domain to accept connections from.\n\n"
|
||||
"Returns: [int] Unbound event-channel port.\n" },
|
||||
|
||||
{ "evtchn_reset",
|
||||
(PyCFunction)pyxc_evtchn_reset,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
"Reset all connections.\n"
|
||||
" dom [int]: Domain to reset.\n" },
|
||||
|
||||
{ "physdev_pci_access_modify",
|
||||
(PyCFunction)pyxc_physdev_pci_access_modify,
|
||||
METH_VARARGS | METH_KEYWORDS, "\n"
|
||||
|
||||
@@ -618,6 +618,33 @@ static PyObject *xspy_introduce_domain(XsHandle *self, PyObject *args)
|
||||
return none(result);
|
||||
}
|
||||
|
||||
#define xspy_resume_domain_doc "\n" \
|
||||
"Tell xenstore to clear its shutdown flag for a domain.\n" \
|
||||
"This ensures that a subsequent shutdown will fire the\n" \
|
||||
"appropriate watches.\n" \
|
||||
" dom [int]: domain id\n" \
|
||||
"\n" \
|
||||
"Returns None on success.\n" \
|
||||
"Raises xen.lowlevel.xs.Error on error.\n"
|
||||
|
||||
static PyObject *xspy_resume_domain(XsHandle *self, PyObject *args)
|
||||
{
|
||||
uint32_t dom;
|
||||
|
||||
struct xs_handle *xh = xshandle(self);
|
||||
bool result = 0;
|
||||
|
||||
if (!xh)
|
||||
return NULL;
|
||||
if (!PyArg_ParseTuple(args, "i", &dom))
|
||||
return NULL;
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
result = xs_resume_domain(xh, dom);
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
return none(result);
|
||||
}
|
||||
|
||||
#define xspy_release_domain_doc "\n" \
|
||||
"Tell xenstore to release its channel to a domain.\n" \
|
||||
@@ -789,6 +816,7 @@ static PyMethodDef xshandle_methods[] = {
|
||||
XSPY_METH(transaction_start, METH_NOARGS),
|
||||
XSPY_METH(transaction_end, METH_VARARGS | METH_KEYWORDS),
|
||||
XSPY_METH(introduce_domain, METH_VARARGS),
|
||||
XSPY_METH(resume_domain, METH_VARARGS),
|
||||
XSPY_METH(release_domain, METH_VARARGS),
|
||||
XSPY_METH(close, METH_NOARGS),
|
||||
XSPY_METH(get_domain_path, METH_VARARGS),
|
||||
|
||||
@@ -50,8 +50,7 @@ except ImportError:
|
||||
|
||||
|
||||
def stringify(value):
|
||||
if isinstance(value, float) or \
|
||||
isinstance(value, long) or \
|
||||
if isinstance(value, long) or \
|
||||
(isinstance(value, int) and not isinstance(value, bool)):
|
||||
return str(value)
|
||||
elif isinstance(value, dict):
|
||||
@@ -256,6 +255,7 @@ class UnixXMLRPCRequestHandler(XMLRPCRequestHandler):
|
||||
|
||||
class UnixXMLRPCServer(TCPXMLRPCServer):
|
||||
address_family = socket.AF_UNIX
|
||||
allow_address_reuse = True
|
||||
|
||||
def __init__(self, addr, allowed, xenapi, logRequests = 1):
|
||||
mkdir.parents(os.path.dirname(addr), stat.S_IRWXU, True)
|
||||
|
||||
@@ -294,8 +294,6 @@ class HttpServer:
|
||||
|
||||
backlog = 5
|
||||
|
||||
closed = False
|
||||
|
||||
def __init__(self, root, interface, port=8080):
|
||||
self.root = root
|
||||
self.interface = interface
|
||||
@@ -303,6 +301,7 @@ class HttpServer:
|
||||
# ready indicates when we are ready to begin accept connections
|
||||
# it should be set after a successful bind
|
||||
self.ready = False
|
||||
self.closed = False
|
||||
|
||||
def run(self):
|
||||
self.bind()
|
||||
@@ -317,7 +316,6 @@ class HttpServer:
|
||||
def stop(self):
|
||||
self.close()
|
||||
|
||||
|
||||
def bind(self):
|
||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
flags = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD)
|
||||
@@ -334,9 +332,16 @@ class HttpServer:
|
||||
|
||||
def close(self):
|
||||
self.closed = True
|
||||
self.ready = False
|
||||
# shutdown socket explicitly to allow reuse
|
||||
try:
|
||||
self.socket.shutdown(2)
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
try:
|
||||
self.socket.close()
|
||||
except:
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
def getServerAddr(self):
|
||||
@@ -345,6 +350,9 @@ class HttpServer:
|
||||
def getResource(self, req):
|
||||
return self.root.getRequestResource(req)
|
||||
|
||||
def shutdown(self):
|
||||
self.close()
|
||||
|
||||
|
||||
class UnixHttpServer(HttpServer):
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"""
|
||||
import os
|
||||
|
||||
import XendRoot
|
||||
import XendOptions
|
||||
|
||||
|
||||
def network(op):
|
||||
@@ -30,7 +30,7 @@ def network(op):
|
||||
"""
|
||||
if op not in ['start', 'stop']:
|
||||
raise ValueError('Invalid operation: ' + op)
|
||||
script = XendRoot.instance().get_network_script()
|
||||
script = XendOptions.instance().get_network_script()
|
||||
if script:
|
||||
script.insert(1, op)
|
||||
os.spawnv(os.P_WAIT, script[0], script)
|
||||
+895
-396
File diff suppressed because it is too large.
Load diff
@@ -12,7 +12,7 @@
|
||||
# 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.
|
||||
# Copyright (C) 2006-2007 XenSource Ltd.
|
||||
#============================================================================
|
||||
|
||||
#
|
||||
@@ -24,7 +24,7 @@ XEN_API_VM_POWER_STATE = [
|
||||
'Paused',
|
||||
'Running',
|
||||
'Suspended',
|
||||
'ShuttingDown',
|
||||
'Halted',
|
||||
'Unknown'
|
||||
]
|
||||
|
||||
@@ -35,20 +35,6 @@ 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',
|
||||
@@ -63,13 +49,8 @@ XEN_API_ON_CRASH_BEHAVIOUR = [
|
||||
'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']
|
||||
XEN_API_VBD_TYPE = ['CD', 'Disk']
|
||||
XEN_API_TASK_STATUS_TYPE = ['pending', 'success', 'failure']
|
||||
XEN_API_CONSOLE_PROTOCOL = ['vt100', 'rfb', 'rdp']
|
||||
@@ -21,11 +21,6 @@ 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.
|
||||
|
||||
@@ -80,7 +75,11 @@ class XendAuthSessions:
|
||||
"""
|
||||
pam_auth = None
|
||||
try:
|
||||
import PAM
|
||||
pam_auth = PAM.pam()
|
||||
except ImportError:
|
||||
log.warn("python-pam is required for XenAPI support.")
|
||||
return False
|
||||
except NameError:
|
||||
# if PAM doesn't exist, let's ignore it
|
||||
return False
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
#
|
||||
|
||||
import os, select, errno, stat
|
||||
import os, select, errno, stat, signal
|
||||
import random
|
||||
import shlex
|
||||
from xen.xend import sxp
|
||||
@@ -21,12 +21,15 @@ from xen.util import mkdir
|
||||
from XendLogging import log
|
||||
from XendError import VmError
|
||||
|
||||
def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
import pty, ptsname, termios, fcntl
|
||||
|
||||
def bootloader(blexec, disk, dom, quiet = False, blargs = '', kernel = '',
|
||||
ramdisk = '', kernel_args = ''):
|
||||
"""Run the boot loader executable on the given disk and return a
|
||||
config image.
|
||||
@param blexec Binary to use as the boot loader
|
||||
@param disk Disk to run the boot loader on.
|
||||
@param dom DomainInfo representing the domain being booted.
|
||||
@param quiet Run in non-interactive mode, just booting the default.
|
||||
@param blargs Arguments to pass to the bootloader."""
|
||||
|
||||
@@ -50,9 +53,41 @@ def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
raise
|
||||
break
|
||||
|
||||
child = os.fork()
|
||||
# We need to present the bootloader's tty as a pty slave that xenconsole
|
||||
# can access. Since the bootloader itself needs a pty slave,
|
||||
# we end up with a connection like this:
|
||||
#
|
||||
# xenconsole -- (slave pty1 master) <-> (master pty2 slave) -- bootloader
|
||||
#
|
||||
# where we copy characters between the two master fds, as well as
|
||||
# listening on the bootloader's fifo for the results.
|
||||
|
||||
# Termios runes for very raw access to the pty master fds.
|
||||
attr = [ 0, 0, termios.CS8 | termios.CREAD | termios.CLOCAL,
|
||||
0, 0, 0, [0] * 32 ]
|
||||
|
||||
(m1, s1) = pty.openpty()
|
||||
termios.tcsetattr(m1, termios.TCSANOW, attr)
|
||||
fcntl.fcntl(m1, fcntl.F_SETFL, os.O_NDELAY);
|
||||
os.close(s1)
|
||||
slavename = ptsname.ptsname(m1)
|
||||
dom.storeDom("console/tty", slavename)
|
||||
|
||||
# Release the domain lock here, because we definitely don't want
|
||||
# a stuck bootloader to deny service to other xend clients.
|
||||
from xen.xend import XendDomain
|
||||
domains = XendDomain.instance()
|
||||
domains.domains_lock.release()
|
||||
|
||||
(child, m2) = pty.fork()
|
||||
if (not child):
|
||||
args = [ blexec ]
|
||||
if kernel:
|
||||
args.append("--kernel=%s" % kernel)
|
||||
if ramdisk:
|
||||
args.append("--ramdisk=%s" % ramdisk)
|
||||
if kernel_args:
|
||||
args.append("--args=%s" % kernel_args)
|
||||
if quiet:
|
||||
args.append("-q")
|
||||
args.append("--output=%s" % fifo)
|
||||
@@ -62,12 +97,19 @@ def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
|
||||
try:
|
||||
log.debug("Launching bootloader as %s." % str(args))
|
||||
os.execvp(args[0], args)
|
||||
env = os.environ.copy()
|
||||
env['TERM'] = 'vt100'
|
||||
os.execvpe(args[0], args, env)
|
||||
except OSError, e:
|
||||
print e
|
||||
pass
|
||||
os._exit(1)
|
||||
|
||||
# record that this domain is bootloading
|
||||
dom.bootloader_pid = child
|
||||
|
||||
termios.tcsetattr(m2, termios.TCSANOW, attr)
|
||||
fcntl.fcntl(m2, fcntl.F_SETFL, os.O_NDELAY);
|
||||
while True:
|
||||
try:
|
||||
r = os.open(fifo, os.O_RDONLY)
|
||||
@@ -76,17 +118,53 @@ def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
continue
|
||||
break
|
||||
ret = ""
|
||||
inbuf=""; outbuf="";
|
||||
while True:
|
||||
select.select([r], [], [])
|
||||
s = os.read(r, 1024)
|
||||
ret = ret + s
|
||||
if len(s) == 0:
|
||||
break
|
||||
|
||||
sel = select.select([r, m1, m2], [m1, m2], [])
|
||||
try:
|
||||
if m1 in sel[0]:
|
||||
s = os.read(m1, 1)
|
||||
inbuf += s
|
||||
if m2 in sel[1] and len(inbuf) != 0:
|
||||
os.write(m2, inbuf[0])
|
||||
inbuf = inbuf[1:]
|
||||
except OSError, e:
|
||||
if e.errno == errno.EIO:
|
||||
pass
|
||||
try:
|
||||
if m2 in sel[0]:
|
||||
s = os.read(m2, 1)
|
||||
outbuf += s
|
||||
if m1 in sel[1] and len(outbuf) != 0:
|
||||
os.write(m1, outbuf[0])
|
||||
outbuf = outbuf[1:]
|
||||
except OSError, e:
|
||||
if e.errno == errno.EIO:
|
||||
pass
|
||||
if r in sel[0]:
|
||||
s = os.read(r, 1)
|
||||
ret = ret + s
|
||||
if len(s) == 0:
|
||||
break
|
||||
del inbuf
|
||||
del outbuf
|
||||
os.waitpid(child, 0)
|
||||
os.close(r)
|
||||
os.close(m2)
|
||||
os.close(m1)
|
||||
os.unlink(fifo)
|
||||
|
||||
# Re-acquire the lock to cover the changes we're about to make
|
||||
# when we return to domain creation.
|
||||
domains.domains_lock.acquire()
|
||||
|
||||
if dom.bootloader_pid is None:
|
||||
msg = "Domain was died while the bootloader was running."
|
||||
log.error(msg)
|
||||
raise VmError, msg
|
||||
|
||||
dom.bootloader_pid = None
|
||||
|
||||
if len(ret) == 0:
|
||||
msg = "Boot loader didn't return any data!"
|
||||
log.error(msg)
|
||||
@@ -97,3 +175,12 @@ def bootloader(blexec, disk, quiet = False, blargs = '', kernel = '',
|
||||
pin.input_eof()
|
||||
blcfg = pin.val
|
||||
return blcfg
|
||||
|
||||
|
||||
def bootloader_tidy(dom):
|
||||
if hasattr(dom, "bootloader_pid") and dom.bootloader_pid is not None:
|
||||
pid = dom.bootloader_pid
|
||||
dom.bootloader_pid = None
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
|
||||
|
||||
@@ -22,11 +22,14 @@ from xen.xend.XendConfig import XendConfig
|
||||
from xen.xend.XendConstants import *
|
||||
|
||||
SIGNATURE = "LinuxGuestRecord"
|
||||
QEMU_SIGNATURE = "QemuDeviceModelRecord"
|
||||
dm_batch = 512
|
||||
XC_SAVE = "xc_save"
|
||||
XC_RESTORE = "xc_restore"
|
||||
|
||||
|
||||
sizeof_int = calcsize("i")
|
||||
sizeof_unsigned_int = calcsize("I")
|
||||
sizeof_unsigned_long = calcsize("L")
|
||||
|
||||
|
||||
@@ -69,6 +72,11 @@ def save(fd, dominfo, network, live, dst):
|
||||
"could not write guest state file: config len")
|
||||
write_exact(fd, config, "could not write guest state file: config")
|
||||
|
||||
image_cfg = dominfo.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
|
||||
if hvm:
|
||||
log.info("save hvm domain")
|
||||
# xc_save takes three customization parameters: maxit, max_f, and
|
||||
# flags the last controls whether or not save is 'live', while the
|
||||
# first two further customize behaviour when 'live' save is
|
||||
@@ -76,7 +84,7 @@ def save(fd, dominfo, network, live, dst):
|
||||
# libxenguest; see the comments and/or code in xc_linux_save() for
|
||||
# more information.
|
||||
cmd = [xen.util.auxbin.pathTo(XC_SAVE), str(fd),
|
||||
str(dominfo.getDomid()), "0", "0", str(int(live)) ]
|
||||
str(dominfo.getDomid()), "0", "0", str(int(live) | (int(hvm) << 2)) ]
|
||||
log.debug("[xc_save]: %s", string.join(cmd))
|
||||
|
||||
def saveInputHandler(line, tochild):
|
||||
@@ -90,13 +98,32 @@ def save(fd, dominfo, network, live, dst):
|
||||
log.info("Domain %d suspended.", dominfo.getDomid())
|
||||
dominfo.migrateDevices(network, dst, DEV_MIGRATE_STEP3,
|
||||
domain_name)
|
||||
#send signal to device model for save
|
||||
if hvm:
|
||||
log.info("release_devices for hvm domain")
|
||||
dominfo._releaseDevices(True)
|
||||
tochild.write("done\n")
|
||||
tochild.flush()
|
||||
log.debug('Written done')
|
||||
|
||||
forkHelper(cmd, fd, saveInputHandler, False)
|
||||
|
||||
# put qemu device model state
|
||||
if hvm:
|
||||
write_exact(fd, QEMU_SIGNATURE, "could not write qemu signature")
|
||||
qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(), os.O_RDONLY)
|
||||
while True:
|
||||
buf = os.read(qemu_fd, dm_batch)
|
||||
if len(buf):
|
||||
write_exact(fd, buf, "could not write device model state")
|
||||
else:
|
||||
break
|
||||
os.close(qemu_fd)
|
||||
os.remove("/tmp/xen.qemu-dm.%d" % dominfo.getDomid())
|
||||
|
||||
dominfo.destroyDomain()
|
||||
dominfo.testDeviceComplete()
|
||||
|
||||
try:
|
||||
dominfo.setName(domain_name)
|
||||
except VmError:
|
||||
@@ -109,11 +136,31 @@ def save(fd, dominfo, network, live, dst):
|
||||
except Exception, exn:
|
||||
log.exception("Save failed on domain %s (%s).", domain_name,
|
||||
dominfo.getDomid())
|
||||
|
||||
dominfo._releaseDevices()
|
||||
dominfo.testDeviceComplete()
|
||||
dominfo.testvifsComplete()
|
||||
log.debug("XendCheckpoint.save: devices released")
|
||||
|
||||
dominfo._resetChannels()
|
||||
|
||||
dominfo._removeDom('control/shutdown')
|
||||
dominfo._removeDom('device-misc/vif/nextDeviceID')
|
||||
|
||||
dominfo._createChannels()
|
||||
dominfo._introduceDomain()
|
||||
dominfo._storeDomDetails()
|
||||
|
||||
dominfo._createDevices()
|
||||
log.debug("XendCheckpoint.save: devices created")
|
||||
|
||||
dominfo.resumeDomain()
|
||||
log.debug("XendCheckpoint.save: resumeDomain")
|
||||
|
||||
try:
|
||||
dominfo.setName(domain_name)
|
||||
except:
|
||||
log.exception("Failed to reset the migrating domain's name")
|
||||
raise Exception, exn
|
||||
|
||||
|
||||
def restore(xd, fd, dominfo = None, paused = False):
|
||||
@@ -147,19 +194,45 @@ def restore(xd, fd, dominfo = None, paused = False):
|
||||
assert store_port
|
||||
assert console_port
|
||||
|
||||
nr_pfns = (dominfo.getMemoryTarget() + 3) / 4
|
||||
|
||||
# if hvm, pass mem size to calculate the store_mfn
|
||||
image_cfg = dominfo.info.get('image', {})
|
||||
is_hvm = image_cfg.has_key('hvm')
|
||||
if is_hvm:
|
||||
hvm = dominfo.info['memory_static_min']
|
||||
apic = dominfo.info['image']['hvm'].get('apic', 0)
|
||||
pae = dominfo.info['image']['hvm'].get('pae', 0)
|
||||
log.info("restore hvm domain %d, mem=%d, apic=%d, pae=%d",
|
||||
dominfo.domid, hvm, apic, pae)
|
||||
else:
|
||||
hvm = 0
|
||||
apic = 0
|
||||
pae = 0
|
||||
|
||||
try:
|
||||
l = read_exact(fd, sizeof_unsigned_long,
|
||||
"not a valid guest state file: pfn count read")
|
||||
nr_pfns = unpack("L", l)[0] # native sizeof long
|
||||
if nr_pfns > 16*1024*1024: # XXX
|
||||
max_pfn = unpack("L", l)[0] # native sizeof long
|
||||
|
||||
if max_pfn > 16*1024*1024: # XXX
|
||||
raise XendError(
|
||||
"not a valid guest state file: pfn count out of range")
|
||||
|
||||
balloon.free(xc.pages_to_kib(nr_pfns))
|
||||
shadow = dominfo.info['shadow_memory']
|
||||
log.debug("restore:shadow=0x%x, _static_max=0x%x, _static_min=0x%x, "
|
||||
"nr_pfns=0x%x.", dominfo.info['shadow_memory'],
|
||||
dominfo.info['memory_static_max'],
|
||||
dominfo.info['memory_static_min'], nr_pfns)
|
||||
|
||||
balloon.free(xc.pages_to_kib(nr_pfns) + shadow * 1024)
|
||||
|
||||
shadow_cur = xc.shadow_mem_control(dominfo.getDomid(), shadow)
|
||||
dominfo.info['shadow_memory'] = shadow_cur
|
||||
|
||||
cmd = map(str, [xen.util.auxbin.pathTo(XC_RESTORE),
|
||||
fd, dominfo.getDomid(), nr_pfns,
|
||||
store_port, console_port])
|
||||
fd, dominfo.getDomid(), max_pfn,
|
||||
store_port, console_port, hvm, pae, apic])
|
||||
log.debug("[xc_restore]: %s", string.join(cmd))
|
||||
|
||||
handler = RestoreInputHandler()
|
||||
@@ -169,10 +242,30 @@ def restore(xd, fd, dominfo = None, paused = False):
|
||||
if handler.store_mfn is None or handler.console_mfn is None:
|
||||
raise XendError('Could not read store/console MFN')
|
||||
|
||||
os.read(fd, 1) # Wait for source to close connection
|
||||
dominfo.waitForDevices() # Wait for backends to set up
|
||||
if not paused:
|
||||
dominfo.unpause()
|
||||
|
||||
# get qemu state and create a tmp file for dm restore
|
||||
if is_hvm:
|
||||
qemu_signature = read_exact(fd, len(QEMU_SIGNATURE),
|
||||
"invalid device model signature read")
|
||||
if qemu_signature != QEMU_SIGNATURE:
|
||||
raise XendError("not a valid device model state: found '%s'" %
|
||||
qemu_signature)
|
||||
qemu_fd = os.open("/tmp/xen.qemu-dm.%d" % dominfo.getDomid(),
|
||||
os.O_WRONLY | os.O_CREAT | os.O_TRUNC)
|
||||
while True:
|
||||
buf = os.read(fd, dm_batch)
|
||||
if len(buf):
|
||||
write_exact(qemu_fd, buf,
|
||||
"could not write dm state to tmp file")
|
||||
else:
|
||||
break
|
||||
os.close(qemu_fd)
|
||||
|
||||
|
||||
os.read(fd, 1) # Wait for source to close connection
|
||||
|
||||
dominfo.completeRestore(handler.store_mfn, handler.console_mfn)
|
||||
|
||||
|
||||
+350
-114
@@ -28,7 +28,7 @@ from xen.xend.PrettyPrint import prettyprintstring
|
||||
from xen.xend.XendConstants import DOM_STATE_HALTED
|
||||
|
||||
log = logging.getLogger("xend.XendConfig")
|
||||
log.setLevel(logging.WARN)
|
||||
log.setLevel(logging.DEBUG)
|
||||
|
||||
|
||||
"""
|
||||
@@ -78,6 +78,25 @@ def scrub_password(data):
|
||||
else:
|
||||
return data
|
||||
|
||||
#
|
||||
# CPU fields:
|
||||
#
|
||||
# vcpus_number -- the maximum number of vcpus that this domain may ever have.
|
||||
# aka XendDomainInfo.getVCpuCount().
|
||||
# vcpus -- the legacy configuration name for above.
|
||||
# max_vcpu_id -- vcpus_number - 1. This is given to us by Xen.
|
||||
#
|
||||
# cpus -- the list of pCPUs available to each vCPU.
|
||||
#
|
||||
# vcpu_avail: a bitmap telling the guest domain whether it may use each of
|
||||
# its VCPUs. This is translated to
|
||||
# <dompath>/cpu/<id>/availability = {online,offline} for use
|
||||
# by the guest domain.
|
||||
# online_vpcus -- the number of VCPUs currently up, as reported by Xen. This
|
||||
# is changed by changing vcpu_avail, and waiting for the
|
||||
# domain to respond.
|
||||
#
|
||||
|
||||
|
||||
# Mapping from XendConfig configuration keys to the old
|
||||
# legacy configuration keys that map directly.
|
||||
@@ -106,8 +125,7 @@ XENAPI_HVM_CFG = {
|
||||
'platform_std_vga': 'stdvga',
|
||||
'platform_serial' : 'serial',
|
||||
'platform_localtime': 'localtime',
|
||||
'platform_enable_audio': 'soundhw',
|
||||
'platform_keymap' : 'keymap',
|
||||
'platform_keymap' : 'keymap'
|
||||
}
|
||||
|
||||
# List of XendConfig configuration keys that have no direct equivalent
|
||||
@@ -126,25 +144,21 @@ XENAPI_CFG_TYPES = {
|
||||
'memory_dynamic_min': int,
|
||||
'memory_dynamic_max': int,
|
||||
'memory_actual': int,
|
||||
'cpus': list,
|
||||
'vcpus_policy': str,
|
||||
'vcpus_params': str,
|
||||
'vcpus_params': dict,
|
||||
'vcpus_number': int,
|
||||
'vcpus_features_required': list,
|
||||
'vcpus_features_can_use': list,
|
||||
'vcpus_features_force_on': list,
|
||||
'vcpus_features_force_off': list,
|
||||
'actions_after_shutdown': str,
|
||||
'actions_after_reboot': str,
|
||||
'actions_after_suspend': str,
|
||||
'actions_after_crash': str,
|
||||
'tpm_instance': int,
|
||||
'tpm_backend': int,
|
||||
'PV_bootloader': str,
|
||||
'PV_kernel': str,
|
||||
'PV_ramdisk': str,
|
||||
'PV_args': str,
|
||||
'PV_bootloader_args': str,
|
||||
'HVM_boot': str,
|
||||
'HVM_boot_policy': str,
|
||||
'HVM_boot_params': dict,
|
||||
'platform_std_vga': bool0,
|
||||
'platform_serial': str,
|
||||
'platform_localtime': bool0,
|
||||
@@ -184,7 +198,7 @@ LEGACY_CFG_TYPES = {
|
||||
'uuid': str,
|
||||
'name': str,
|
||||
'vcpus': int,
|
||||
'vcpu_avail': int,
|
||||
'vcpu_avail': long,
|
||||
'memory': int,
|
||||
'shadow_memory': int,
|
||||
'maxmem': int,
|
||||
@@ -231,7 +245,8 @@ LEGACY_IMAGE_CFG = [
|
||||
('sdl', int),
|
||||
('vncdisplay', int),
|
||||
('vncunused', int),
|
||||
('vncpasswd', str),
|
||||
('vncpasswd', str),
|
||||
('vnclisten', str),
|
||||
]
|
||||
|
||||
LEGACY_IMAGE_HVM_CFG = [
|
||||
@@ -248,17 +263,19 @@ LEGACY_IMAGE_HVM_DEVICES_CFG = [
|
||||
('boot', str),
|
||||
('fda', str),
|
||||
('fdb', str),
|
||||
('isa', str),
|
||||
('isa', int),
|
||||
('keymap', str),
|
||||
('localtime', str),
|
||||
('localtime', int),
|
||||
('serial', str),
|
||||
('stdvga', int),
|
||||
('soundhw', str),
|
||||
('usb', str),
|
||||
('usb', int),
|
||||
('usbdevice', str),
|
||||
('vcpus', int),
|
||||
]
|
||||
|
||||
LEGACY_DM = '/usr/lib/xen/bin/qemu-dm'
|
||||
|
||||
##
|
||||
## Config Choices
|
||||
##
|
||||
@@ -326,7 +343,6 @@ class XendConfig(dict):
|
||||
|
||||
def _defaults(self):
|
||||
defaults = {
|
||||
'uuid': uuid.createString(),
|
||||
'name_label': 'Domain-Unnamed',
|
||||
'actions_after_shutdown': 'destroy',
|
||||
'actions_after_reboot': 'restart',
|
||||
@@ -338,7 +354,8 @@ class XendConfig(dict):
|
||||
'PV_ramdisk': '',
|
||||
'PV_args': '',
|
||||
'PV_bootloader_args': '',
|
||||
'HVM_boot': '',
|
||||
'HVM_boot_policy': '',
|
||||
'HVM_boot_params': {},
|
||||
'memory_static_min': 0,
|
||||
'memory_dynamic_min': 0,
|
||||
'shadow_memory': 0,
|
||||
@@ -354,16 +371,14 @@ class XendConfig(dict):
|
||||
'cpu_weight': 256,
|
||||
'cpu_cap': 0,
|
||||
'vcpus_number': 1,
|
||||
'online_vcpus': 1,
|
||||
'max_vcpu_id': 0,
|
||||
'vcpu_avail': 1,
|
||||
'vcpus_params': {},
|
||||
'console_refs': [],
|
||||
'vif_refs': [],
|
||||
'vbd_refs': [],
|
||||
'vtpm_refs': [],
|
||||
'other_config': {},
|
||||
}
|
||||
|
||||
defaults['name_label'] = 'Domain-' + defaults['uuid']
|
||||
return defaults
|
||||
|
||||
def _memory_sanity_check(self):
|
||||
@@ -388,23 +403,31 @@ class XendConfig(dict):
|
||||
event)
|
||||
|
||||
def _vcpus_sanity_check(self):
|
||||
if self.get('vcpus_number') != None:
|
||||
if 'vcpus_number' in self and 'vcpu_avail' not in self:
|
||||
self['vcpu_avail'] = (1 << self['vcpus_number']) - 1
|
||||
|
||||
def _uuid_sanity_check(self):
|
||||
"""Make sure UUID is in proper string format with hyphens."""
|
||||
self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
|
||||
if 'uuid' not in self or not self['uuid']:
|
||||
self['uuid'] = uuid.createString()
|
||||
else:
|
||||
self['uuid'] = uuid.toString(uuid.fromString(self['uuid']))
|
||||
|
||||
def _name_sanity_check(self):
|
||||
if 'name_label' not in self:
|
||||
self['name_label'] = 'Domain-' + self['uuid']
|
||||
|
||||
def validate(self):
|
||||
self._uuid_sanity_check()
|
||||
self._name_sanity_check()
|
||||
self._memory_sanity_check()
|
||||
self._actions_sanity_check()
|
||||
self._vcpus_sanity_check()
|
||||
self._uuid_sanity_check()
|
||||
|
||||
def _dominfo_to_xapi(self, dominfo):
|
||||
self['domid'] = dominfo['domid']
|
||||
self['online_vcpus'] = dominfo['online_vcpus']
|
||||
self['max_vcpu_id'] = dominfo['max_vcpu_id']
|
||||
self['vcpus_number'] = dominfo['max_vcpu_id'] + 1
|
||||
self['memory_dynamic_min'] = (dominfo['mem_kb'] + 1023)/1024
|
||||
self['memory_dynamic_max'] = (dominfo['maxmem_kb'] + 1023)/1024
|
||||
self['cpu_time'] = dominfo['cpu_time']/1e9
|
||||
@@ -475,6 +498,12 @@ class XendConfig(dict):
|
||||
log.warn("Unable to parse key %s: %s: %s" %
|
||||
(key, str(val), e))
|
||||
|
||||
# Compatibility hack -- can go soon.
|
||||
boot_order = sxp.child_value(sxp_cfg, 'HVM_boot')
|
||||
if boot_order:
|
||||
cfg['HVM_boot_policy'] = 'BIOS order'
|
||||
cfg['HVM_boot_params'] = { 'order' : boot_order }
|
||||
|
||||
# Parsing the device SXP's. In most cases, the SXP looks
|
||||
# like this:
|
||||
#
|
||||
@@ -508,8 +537,12 @@ class XendConfig(dict):
|
||||
pci_devs = []
|
||||
for pci_dev in sxp.children(config, 'dev'):
|
||||
pci_dev_info = {}
|
||||
for opt, val in pci_dev[1:]:
|
||||
pci_dev_info[opt] = val
|
||||
for opt_val in pci_dev[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
pci_dev_info[opt] = val
|
||||
except TypeError:
|
||||
pass
|
||||
pci_devs.append(pci_dev_info)
|
||||
|
||||
cfg['devices'][pci_devs_uuid] = (dev_type,
|
||||
@@ -551,7 +584,7 @@ class XendConfig(dict):
|
||||
# "0-3,^1" -> [0,2,3]
|
||||
# "0-3,^1,1" -> [0,1,2,3]
|
||||
try:
|
||||
if 'cpus' in cfg:
|
||||
if 'cpus' in cfg and type(cfg['cpus']) != list:
|
||||
cpus = []
|
||||
for c in cfg['cpus'].split(','):
|
||||
if c.find('-') != -1:
|
||||
@@ -572,7 +605,6 @@ class XendConfig(dict):
|
||||
if 'security' in cfg and isinstance(cfg['security'], str):
|
||||
cfg['security'] = sxp.from_string(cfg['security'])
|
||||
|
||||
# TODO: get states
|
||||
old_state = sxp.child_value(sxp_cfg, 'state')
|
||||
if old_state:
|
||||
for i in range(len(CONFIG_OLD_DOM_STATES)):
|
||||
@@ -632,24 +664,35 @@ class XendConfig(dict):
|
||||
self['memory_dynamic_max'] = self['memory_static_max']
|
||||
self['memory_dynamic_min'] = self['memory_static_min']
|
||||
|
||||
# make sure max_vcpu_id is set correctly
|
||||
self['max_vcpu_id'] = self['vcpus_number'] - 1
|
||||
|
||||
# set device references in the configuration
|
||||
self['devices'] = cfg.get('devices', {})
|
||||
|
||||
self['console_refs'] = []
|
||||
self['vif_refs'] = []
|
||||
self['vbd_refs'] = []
|
||||
self['vtpm_refs'] = []
|
||||
for dev_uuid, (dev_type, dev_info) in self['devices'].items():
|
||||
if dev_type == 'vif':
|
||||
self['vif_refs'].append(dev_uuid)
|
||||
elif dev_type in ('vbd','tap'):
|
||||
self['vbd_refs'].append(dev_uuid)
|
||||
elif dev_type in ('vtpm',):
|
||||
self['vtpm_refs'].append(dev_uuid)
|
||||
|
||||
self['console_refs'] = cfg.get('console_refs', [])
|
||||
self['vif_refs'] = cfg.get('vif_refs', [])
|
||||
self['vbd_refs'] = cfg.get('vbd_refs', [])
|
||||
self['vtpm_refs'] = cfg.get('vtpm_refs', [])
|
||||
|
||||
# coalesce hvm vnc frame buffer with vfb config
|
||||
if self['image']['type'] == 'hvm' and self['image'].get('vnc', 0):
|
||||
# add vfb device if it isn't there already
|
||||
has_rfb = False
|
||||
for console_uuid in self['console_refs']:
|
||||
if self['devices'][console_uuid][1].get('protocol') == 'rfb':
|
||||
has_rfb = True
|
||||
break
|
||||
if self['devices'][console_uuid][0] == 'vfb':
|
||||
has_rfb = True
|
||||
break
|
||||
|
||||
if not has_rfb:
|
||||
dev_config = ['vfb']
|
||||
# copy VNC related params from image config to vfb dev conf
|
||||
for key in ['vncpasswd', 'vncunused', 'vncdisplay',
|
||||
'vnclisten']:
|
||||
if key in self['image']:
|
||||
dev_config.append([key, self['image'][key]])
|
||||
|
||||
self.device_add('vfb', cfg_sxp = dev_config)
|
||||
|
||||
|
||||
def _sxp_to_xapi_unsupported(self, sxp_cfg):
|
||||
"""Read in an SXP configuration object and populate
|
||||
@@ -692,6 +735,7 @@ class XendConfig(dict):
|
||||
val = sxp.child_value(image_sxp, imgkey, None)
|
||||
if val != None:
|
||||
self[apikey] = val
|
||||
self._hvm_boot_params_from_sxp(image_sxp)
|
||||
|
||||
# extract backend value
|
||||
|
||||
@@ -716,13 +760,11 @@ class XendConfig(dict):
|
||||
_set_cfg_if_exists('on_xend_stop')
|
||||
_set_cfg_if_exists('on_xend_start')
|
||||
_set_cfg_if_exists('vcpu_avail')
|
||||
_set_cfg_if_exists('max_vcpu_id') # needed for vcpuDomDetails
|
||||
_set_cfg_if_exists('cpu_weight')
|
||||
_set_cfg_if_exists('cpu_cap')
|
||||
|
||||
# Parse and store runtime configuration
|
||||
_set_cfg_if_exists('start_time')
|
||||
_set_cfg_if_exists('online_vcpus')
|
||||
_set_cfg_if_exists('cpu_time')
|
||||
_set_cfg_if_exists('shutdown_reason')
|
||||
_set_cfg_if_exists('up_time')
|
||||
@@ -738,13 +780,22 @@ class XendConfig(dict):
|
||||
if 'image' in xapi_dict:
|
||||
self['image'].update(xapi_dict['image'])
|
||||
else:
|
||||
hvm = self['HVM_boot'] != ''
|
||||
hvm = self['HVM_boot_policy'] != ''
|
||||
self['image']['type'] = hvm and 'hvm' or 'linux'
|
||||
if hvm:
|
||||
self['image']['hvm'] = {}
|
||||
self['image']['hvm'] = {'devices': {}}
|
||||
for xapi, cfgapi in XENAPI_HVM_CFG.items():
|
||||
self['image']['hvm'][cfgapi] = self[xapi]
|
||||
|
||||
if xapi in self:
|
||||
self['image']['hvm']['devices'][cfgapi] = self[xapi]
|
||||
|
||||
# currently unsupported options
|
||||
self['image']['hvm']['device_model'] = LEGACY_DM
|
||||
self['image']['vnc'] = 0
|
||||
self['image']['hvm']['pae'] = 1
|
||||
|
||||
if self['platform_enable_audio']:
|
||||
self['image']['hvm']['devices']['soundhw'] = 'sb16'
|
||||
|
||||
|
||||
def _get_old_state_string(self):
|
||||
"""Returns the old xm state string.
|
||||
@@ -790,8 +841,6 @@ class XendConfig(dict):
|
||||
else:
|
||||
self[key] = val
|
||||
|
||||
self.validate()
|
||||
|
||||
def to_sxp(self, domain = None, ignore_devices = False, ignore = [],
|
||||
legacy_only = True):
|
||||
""" Get SXP representation of this config object.
|
||||
@@ -815,9 +864,13 @@ class XendConfig(dict):
|
||||
sxpr.append(['domid', domain.getDomid()])
|
||||
|
||||
if not legacy_only:
|
||||
for name in XENAPI_CFG_TYPES.keys():
|
||||
for name, typ in XENAPI_CFG_TYPES.items():
|
||||
if name in self and self[name] not in (None, []):
|
||||
sxpr.append([name, str(self[name])])
|
||||
if typ == dict:
|
||||
s = self[name].items()
|
||||
else:
|
||||
s = str(self[name])
|
||||
sxpr.append([name, s])
|
||||
|
||||
for xenapi, legacy in XENAPI_CFG_TO_LEGACY_CFG.items():
|
||||
if self.has_key(xenapi) and self[xenapi] not in (None, []):
|
||||
@@ -855,14 +908,23 @@ class XendConfig(dict):
|
||||
for cls in XendDevices.valid_devices():
|
||||
found = False
|
||||
|
||||
# figure if there is a device that is running
|
||||
if domain:
|
||||
# figure if there is a dev controller is valid and running
|
||||
if domain and domain.getDomid() != None:
|
||||
try:
|
||||
controller = domain.getDeviceController(cls)
|
||||
configs = controller.configurations()
|
||||
for config in configs:
|
||||
if sxp.name(config) in ('vbd', 'tap'):
|
||||
# The bootable flag is never written to the
|
||||
# store as part of the device config.
|
||||
dev_uuid = sxp.child_value(config, 'uuid')
|
||||
dev_type, dev_cfg = self['devices'][dev_uuid]
|
||||
is_bootable = dev_cfg.get('bootable', 0)
|
||||
config.append(['bootable', int(is_bootable)])
|
||||
|
||||
sxpr.append(['device', config])
|
||||
found = True
|
||||
|
||||
found = True
|
||||
except:
|
||||
log.exception("dumping sxp from device controllers")
|
||||
pass
|
||||
@@ -900,8 +962,7 @@ class XendConfig(dict):
|
||||
if target == None:
|
||||
target = self
|
||||
|
||||
if dev_type not in XendDevices.valid_devices() and \
|
||||
dev_type not in XendDevices.pseudo_devices():
|
||||
if dev_type not in XendDevices.valid_devices():
|
||||
raise XendConfigError("XendConfig: %s not a valid device type" %
|
||||
dev_type)
|
||||
|
||||
@@ -909,10 +970,10 @@ class XendConfig(dict):
|
||||
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:
|
||||
# log.debug("XendConfig.device_add: %s" % str(cfg_sxp))
|
||||
#if cfg_xenapi:
|
||||
# log.debug("XendConfig.device_add: %s" % str(cfg_xenapi))
|
||||
|
||||
if cfg_sxp:
|
||||
if sxp.child0(cfg_sxp) == 'device':
|
||||
@@ -923,20 +984,24 @@ class XendConfig(dict):
|
||||
dev_type = sxp.name(config)
|
||||
dev_info = {}
|
||||
|
||||
try:
|
||||
for opt, val in config[1:]:
|
||||
for opt_val in config[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
dev_info[opt] = val
|
||||
except ValueError:
|
||||
pass # SXP has no options for this device
|
||||
except (TypeError, ValueError): # unpack error
|
||||
pass
|
||||
|
||||
if dev_type == 'vbd':
|
||||
dev_info['bootable'] = 0
|
||||
if dev_info.get('dev', '').startswith('ioemu:'):
|
||||
dev_info['driver'] = 'ioemu'
|
||||
else:
|
||||
dev_info['driver'] = 'paravirtualised'
|
||||
|
||||
# create uuid if it doesn't exist
|
||||
dev_uuid = dev_info.get('uuid', uuid.createString())
|
||||
dev_uuid = dev_info.get('uuid', None)
|
||||
if not dev_uuid:
|
||||
dev_uuid = uuid.createString()
|
||||
dev_info['uuid'] = dev_uuid
|
||||
|
||||
# store dev references by uuid for certain device types
|
||||
@@ -946,17 +1011,53 @@ class XendConfig(dict):
|
||||
if param not in target:
|
||||
target[param] = []
|
||||
if dev_uuid not in target[param]:
|
||||
if dev_type == 'vbd' and not target[param]:
|
||||
# Compat hack -- this is the first disk, so mark it
|
||||
# bootable.
|
||||
dev_info['bootable'] = 1
|
||||
target[param].append(dev_uuid)
|
||||
elif dev_type in ('tap',):
|
||||
elif dev_type == 'tap':
|
||||
if 'vbd_refs' not in target:
|
||||
target['vbd_refs'] = []
|
||||
if dev_uuid not in target['vbd_refs']:
|
||||
if not target['vbd_refs']:
|
||||
# Compat hack -- this is the first disk, so mark it
|
||||
# bootable.
|
||||
dev_info['bootable'] = 1
|
||||
target['vbd_refs'].append(dev_uuid)
|
||||
|
||||
elif dev_type == 'vfb':
|
||||
# Populate other config with aux data that is associated
|
||||
# with vfb
|
||||
|
||||
other_config = {}
|
||||
for key in ['vncunused', 'vncdisplay', 'vnclisten',
|
||||
'vncpasswd', 'type', 'display', 'xauthority',
|
||||
'keymap']:
|
||||
if key in dev_info:
|
||||
other_config[key] = dev_info[key]
|
||||
target['devices'][dev_uuid][1]['other_config'] = other_config
|
||||
|
||||
|
||||
if 'console_refs' not in target:
|
||||
target['console_refs'] = []
|
||||
|
||||
# Treat VFB devices as console devices so they are found
|
||||
# through Xen API
|
||||
if dev_uuid not in target['console_refs']:
|
||||
target['console_refs'].append(dev_uuid)
|
||||
|
||||
elif dev_type == 'console':
|
||||
if 'console_refs' not in target:
|
||||
target['console_refs'] = []
|
||||
if dev_uuid not in target['console_refs']:
|
||||
target['console_refs'].append(dev_uuid)
|
||||
|
||||
return dev_uuid
|
||||
|
||||
if cfg_xenapi:
|
||||
dev_info = {}
|
||||
dev_info = {}
|
||||
dev_uuid = ''
|
||||
if dev_type == 'vif':
|
||||
if cfg_xenapi.get('MAC'): # don't add if blank
|
||||
dev_info['mac'] = cfg_xenapi.get('MAC')
|
||||
@@ -967,22 +1068,28 @@ class XendConfig(dict):
|
||||
# dev_info['vifname'] = cfg_xenapi.get('device')
|
||||
if cfg_xenapi.get('type'):
|
||||
dev_info['type'] = cfg_xenapi.get('type')
|
||||
if cfg_xenapi.get('name'):
|
||||
dev_info['name'] = cfg_xenapi.get('name')
|
||||
|
||||
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
|
||||
dev_uuid = cfg_xenapi.get('uuid', None)
|
||||
if not dev_uuid:
|
||||
dev_uuid = uuid.createString()
|
||||
dev_info['uuid'] = dev_uuid
|
||||
self['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
self['vif_refs'].append(dev_uuid)
|
||||
return dev_uuid
|
||||
target['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
target['vif_refs'].append(dev_uuid)
|
||||
|
||||
elif dev_type in ('vbd', 'tap'):
|
||||
if dev_type == 'vbd':
|
||||
dev_info['uname'] = cfg_xenapi.get('image', '')
|
||||
dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
|
||||
elif dev_type == 'tap':
|
||||
dev_info['uname'] = 'tap:qcow:%s' % cfg_xenapi.get('image')
|
||||
dev_info['dev'] = '%s:disk' % cfg_xenapi.get('device')
|
||||
dev_info['type'] = cfg_xenapi.get('type', 'Disk')
|
||||
if dev_info['type'] == 'CD':
|
||||
old_vbd_type = 'cdrom'
|
||||
else:
|
||||
old_vbd_type = 'disk'
|
||||
|
||||
dev_info['driver'] = cfg_xenapi.get('driver')
|
||||
dev_info['uname'] = cfg_xenapi.get('image', '')
|
||||
dev_info['dev'] = '%s:%s' % (cfg_xenapi.get('device'),
|
||||
old_vbd_type)
|
||||
dev_info['bootable'] = int(cfg_xenapi.get('bootable', 0))
|
||||
dev_info['driver'] = cfg_xenapi.get('driver', '')
|
||||
dev_info['VDI'] = cfg_xenapi.get('VDI', '')
|
||||
|
||||
if cfg_xenapi.get('mode') == 'RW':
|
||||
@@ -990,42 +1097,137 @@ class XendConfig(dict):
|
||||
else:
|
||||
dev_info['mode'] = 'r'
|
||||
|
||||
dev_uuid = cfg_xenapi.get('uuid', uuid.createString())
|
||||
dev_uuid = cfg_xenapi.get('uuid', None)
|
||||
if not dev_uuid:
|
||||
dev_uuid = uuid.createString()
|
||||
dev_info['uuid'] = dev_uuid
|
||||
self['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
self['vbd_refs'].append(dev_uuid)
|
||||
return dev_uuid
|
||||
target['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
target['vbd_refs'].append(dev_uuid)
|
||||
|
||||
elif dev_type in ('vtpm'):
|
||||
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_uuid = cfg_xenapi.get('uuid', None)
|
||||
if not dev_uuid:
|
||||
dev_uuid = uuid.createString()
|
||||
dev_info['uuid'] = dev_uuid
|
||||
self['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
self['vtpm_refs'].append(dev_uuid)
|
||||
return dev_uuid
|
||||
target['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
target['vtpm_refs'].append(dev_uuid)
|
||||
|
||||
elif dev_type == 'console':
|
||||
dev_uuid = cfg_xenapi.get('uuid', None)
|
||||
if not dev_uuid:
|
||||
dev_uuid = uuid.createString()
|
||||
dev_info['uuid'] = dev_uuid
|
||||
dev_info['protocol'] = cfg_xenapi.get('protocol', 'rfb')
|
||||
dev_info['other_config'] = cfg_xenapi.get('other_config', {})
|
||||
if dev_info['protocol'] == 'rfb':
|
||||
# collapse other config into devinfo for things
|
||||
# such as vncpasswd, vncunused, etc.
|
||||
dev_info.update(cfg_xenapi.get('other_config', {}))
|
||||
dev_info['type'] = 'vnc'
|
||||
target['devices'][dev_uuid] = ('vfb', dev_info)
|
||||
target['console_refs'].append(dev_uuid)
|
||||
|
||||
# Finally, if we are a pvfb, we need to make a vkbd
|
||||
# as well that is not really exposed to Xen API
|
||||
vkbd_uuid = uuid.createString()
|
||||
target['devices'][vkbd_uuid] = ('vkbd', {})
|
||||
|
||||
elif dev_info['protocol'] == 'vt100':
|
||||
# if someone tries to create a VT100 console
|
||||
# via the Xen API, we'll have to ignore it
|
||||
# because we create one automatically in
|
||||
# XendDomainInfo._update_consoles
|
||||
raise XendConfigError('Creating vt100 consoles via '
|
||||
'Xen API is unsupported')
|
||||
|
||||
return dev_uuid
|
||||
|
||||
# no valid device to add
|
||||
return ''
|
||||
|
||||
def device_update(self, dev_uuid, cfg_sxp):
|
||||
def console_add(self, protocol, location, other_config = {}):
|
||||
dev_uuid = uuid.createString()
|
||||
if protocol == 'vt100':
|
||||
dev_info = {
|
||||
'uuid': dev_uuid,
|
||||
'protocol': protocol,
|
||||
'location': location,
|
||||
'other_config': other_config,
|
||||
}
|
||||
|
||||
if 'devices' not in self:
|
||||
self['devices'] = {}
|
||||
|
||||
self['devices'][dev_uuid] = ('console', dev_info)
|
||||
self['console_refs'].append(dev_uuid)
|
||||
return dev_info
|
||||
|
||||
return {}
|
||||
|
||||
def console_update(self, console_uuid, key, value):
|
||||
for dev_uuid, (dev_type, dev_info) in self['devices'].items():
|
||||
if dev_uuid == console_uuid:
|
||||
dev_info[key] = value
|
||||
break
|
||||
|
||||
def console_get_all(self, protocol):
|
||||
if protocol == 'vt100':
|
||||
consoles = [dinfo for dtype, dinfo in self['devices'].values()
|
||||
if dtype == 'console']
|
||||
return [c for c in consoles if c.get('protocol') == protocol]
|
||||
|
||||
elif protocol == 'rfb':
|
||||
vfbs = [dinfo for dtype, dinfo in self['devices'].values()
|
||||
if dtype == 'vfb']
|
||||
|
||||
# move all non-console key values to other_config before
|
||||
# returning console config
|
||||
valid_keys = ['uuid', 'location']
|
||||
for vfb in vfbs:
|
||||
other_config = {}
|
||||
for key, val in vfb.items():
|
||||
if key not in valid_keys:
|
||||
other_config[key] = vfb[key]
|
||||
del vfb[key]
|
||||
vfb['other_config'] = other_config
|
||||
vfb['protocol'] = 'rfb'
|
||||
|
||||
return vfbs
|
||||
|
||||
else:
|
||||
return []
|
||||
|
||||
def device_update(self, dev_uuid, cfg_sxp = [], cfg_xenapi = {}):
|
||||
"""Update an existing device with the new configuration.
|
||||
|
||||
@rtype: boolean
|
||||
@return: Returns True if succesfully found and updated a device conf
|
||||
"""
|
||||
if dev_uuid in self['devices']:
|
||||
config = sxp.child0(cfg_sxp)
|
||||
dev_type = sxp.name(config)
|
||||
dev_info = {}
|
||||
if dev_uuid in self['devices'] and cfg_sxp:
|
||||
if sxp.child0(cfg_sxp) == 'device':
|
||||
config = sxp.child0(cfg_sxp)
|
||||
else:
|
||||
config = cfg_sxp
|
||||
|
||||
try:
|
||||
for opt, val in config[1:]:
|
||||
self['devices'][opt] = val
|
||||
except ValueError:
|
||||
pass # SXP has no options for this device
|
||||
|
||||
dev_type, dev_info = self['devices'][dev_uuid]
|
||||
for opt_val in config[1:]:
|
||||
try:
|
||||
opt, val = opt_val
|
||||
dev_info[opt] = val
|
||||
except (TypeError, ValueError):
|
||||
pass # no value for this config option
|
||||
|
||||
self['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
return True
|
||||
|
||||
elif dev_uuid in self['devices'] and cfg_xenapi:
|
||||
dev_type, dev_info = self['devices'][dev_uuid]
|
||||
for key, val in cfg_xenapi.items():
|
||||
dev_info[key] = val
|
||||
self['devices'][dev_uuid] = (dev_type, dev_info)
|
||||
|
||||
return False
|
||||
|
||||
@@ -1045,11 +1247,24 @@ class XendConfig(dict):
|
||||
"configuration dictionary.")
|
||||
|
||||
sxpr.append(dev_type)
|
||||
config = [(opt, val) for opt, val in dev_info.items()]
|
||||
if dev_type in ('console', 'vfb'):
|
||||
config = [(opt, val) for opt, val in dev_info.items()
|
||||
if opt != 'other_config']
|
||||
else:
|
||||
config = [(opt, val) for opt, val in dev_info.items()]
|
||||
|
||||
sxpr += config
|
||||
|
||||
return sxpr
|
||||
|
||||
def ordered_device_refs(self):
|
||||
result = (self['console_refs'] +
|
||||
self['vbd_refs'] +
|
||||
self['vif_refs'] +
|
||||
self['vtpm_refs'])
|
||||
result.extend([u for u in self['devices'].keys() if u not in result])
|
||||
return result
|
||||
|
||||
def all_devices_sxpr(self):
|
||||
"""Returns the SXPR for all devices in the current configuration."""
|
||||
sxprs = []
|
||||
@@ -1058,7 +1273,9 @@ class XendConfig(dict):
|
||||
if 'devices' not in self:
|
||||
return sxprs
|
||||
|
||||
for dev_type, dev_info in self['devices'].values():
|
||||
ordered_refs = self.ordered_device_refs()
|
||||
for dev_uuid in ordered_refs:
|
||||
dev_type, dev_info = self['devices'][dev_uuid]
|
||||
if dev_type == 'pci': # special case for pci devices
|
||||
sxpr = [['uuid', dev_info['uuid']]]
|
||||
for pci_dev_info in dev_info['devs']:
|
||||
@@ -1092,13 +1309,18 @@ class XendConfig(dict):
|
||||
if 'hvm' in self['image']:
|
||||
for arg, conv in LEGACY_IMAGE_HVM_CFG:
|
||||
if self['image']['hvm'].get(arg):
|
||||
image.append([arg, self['image']['hvm'][arg]])
|
||||
image.append([arg, conv(self['image']['hvm'][arg])])
|
||||
|
||||
if 'hvm' in self['image'] and 'devices' in self['image']['hvm']:
|
||||
for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
|
||||
if self['image']['hvm']['devices'].get(arg):
|
||||
image.append([arg,
|
||||
self['image']['hvm']['devices'][arg]])
|
||||
val = self['image']['hvm']['devices'].get(arg)
|
||||
if val != None:
|
||||
try:
|
||||
if conv: val = conv(val)
|
||||
except (ValueError, TypeError):
|
||||
if type(val) == bool: val = int(val)
|
||||
|
||||
image.append([arg, val])
|
||||
|
||||
return image
|
||||
|
||||
@@ -1107,15 +1329,17 @@ class XendConfig(dict):
|
||||
# configuration
|
||||
log.debug("update_with_image_sxp(%s)" % scrub_password(image_sxp))
|
||||
|
||||
# user-specified args must come last: previous releases did this and
|
||||
# some domU kernels rely upon the ordering.
|
||||
kernel_args = sxp.child_value(image_sxp, 'args', '')
|
||||
|
||||
# attempt to extract extra arguments from SXP config
|
||||
arg_ip = sxp.child_value(image_sxp, 'ip')
|
||||
if arg_ip and not re.search(r'ip=[^ ]+', kernel_args):
|
||||
kernel_args += ' ip=%s' % arg_ip
|
||||
kernel_args = 'ip=%s ' % arg_ip + kernel_args
|
||||
arg_root = sxp.child_value(image_sxp, 'root')
|
||||
if arg_root and not re.search(r'root=', kernel_args):
|
||||
kernel_args += ' root=%s' % arg_root
|
||||
kernel_args = 'root=%s ' % arg_root + kernel_args
|
||||
|
||||
if bootloader:
|
||||
self['_temp_using_bootloader'] = '1'
|
||||
@@ -1145,7 +1369,11 @@ class XendConfig(dict):
|
||||
for arg, conv in LEGACY_IMAGE_HVM_DEVICES_CFG:
|
||||
val = sxp.child_value(image_sxp, arg, None)
|
||||
if val != None:
|
||||
image_hvm_devices[arg] = conv(val)
|
||||
try:
|
||||
image_hvm_devices[arg] = conv(val)
|
||||
except (ValueError, TypeError):
|
||||
image_hvm_devices[arg] = val
|
||||
|
||||
|
||||
if image_hvm or image_hvm_devices:
|
||||
image['hvm'] = image_hvm
|
||||
@@ -1157,12 +1385,20 @@ class XendConfig(dict):
|
||||
val = sxp.child_value(image_sxp, imgkey, None)
|
||||
if val != None:
|
||||
type_conv = XENAPI_CFG_TYPES[apikey]
|
||||
if callable(conv):
|
||||
if callable(type_conv):
|
||||
self[apikey] = type_conv(val)
|
||||
else:
|
||||
self[apikey] = val
|
||||
self._hvm_boot_params_from_sxp(image_sxp)
|
||||
|
||||
|
||||
def _hvm_boot_params_from_sxp(self, image_sxp):
|
||||
boot = sxp.child_value(image_sxp, 'boot', None)
|
||||
if boot is not None:
|
||||
self['HVM_boot_policy'] = 'BIOS order'
|
||||
self['HVM_boot_params'] = { 'order' : boot }
|
||||
|
||||
|
||||
|
||||
#
|
||||
# debugging
|
||||
#
|
||||
|
||||
@@ -80,6 +80,7 @@ ZOMBIE_PREFIX = 'Zombie-'
|
||||
MINIMUM_RESTART_TIME = 20
|
||||
|
||||
RESTART_IN_PROGRESS = 'xend/restart_in_progress'
|
||||
LAST_SHUTDOWN_REASON = 'xend/last_shutdown_reason'
|
||||
|
||||
#
|
||||
# Device migration stages (eg. XendDomainInfo, XendCheckpoint, server.tpmif)
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
from xen.xend.server import blkif, netif, tpmif, pciif, iopif, irqif, usbif, vfbif
|
||||
from xen.xend.server.BlktapController import BlktapController
|
||||
from xen.xend.server.ConsoleController import ConsoleController
|
||||
|
||||
|
||||
class XendDevices:
|
||||
""" An ugly halfway point between the module local device name
|
||||
@@ -43,6 +45,7 @@ class XendDevices:
|
||||
'tap': BlktapController,
|
||||
'vfb': vfbif.VfbifController,
|
||||
'vkbd': vfbif.VkbdifController,
|
||||
'console': ConsoleController,
|
||||
}
|
||||
|
||||
#@classmethod
|
||||
@@ -50,11 +53,6 @@ class XendDevices:
|
||||
return cls.controllers.keys()
|
||||
valid_devices = classmethod(valid_devices)
|
||||
|
||||
#@classmethod
|
||||
def pseudo_devices(cls):
|
||||
return ['console']
|
||||
pseudo_devices = classmethod(pseudo_devices)
|
||||
|
||||
#@classmethod
|
||||
def make_controller(cls, name, domain):
|
||||
"""Factory function to make device controllers per domain.
|
||||
|
||||
@@ -32,7 +32,7 @@ import threading
|
||||
import xen.lowlevel.xc
|
||||
|
||||
|
||||
from xen.xend import XendRoot, XendCheckpoint, XendDomainInfo
|
||||
from xen.xend import XendOptions, XendCheckpoint, XendDomainInfo, XendNode
|
||||
from xen.xend.PrettyPrint import prettyprint
|
||||
from xen.xend.XendConfig import XendConfig
|
||||
from xen.xend.XendError import XendError, XendInvalidDomain, VmError
|
||||
@@ -51,7 +51,7 @@ from xen.util import mkdir, security
|
||||
from xen.xend import uuid
|
||||
|
||||
xc = xen.lowlevel.xc.xc()
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
__all__ = [ "XendDomain" ]
|
||||
|
||||
@@ -115,7 +115,6 @@ class XendDomain:
|
||||
|
||||
dom0info['name'] = DOM0_NAME
|
||||
dom0 = XendDomainInfo.recreate(dom0info, True)
|
||||
self._add_domain(dom0)
|
||||
except IndexError:
|
||||
raise XendError('Unable to find Domain 0')
|
||||
|
||||
@@ -172,7 +171,6 @@ class XendDomain:
|
||||
if dom['domid'] != DOM0_ID:
|
||||
try:
|
||||
new_dom = XendDomainInfo.recreate(dom, False)
|
||||
self._add_domain(new_dom)
|
||||
except Exception:
|
||||
log.exception("Failed to create reference to running "
|
||||
"domain id: %d" % dom['domid'])
|
||||
@@ -214,7 +212,7 @@ class XendDomain:
|
||||
@rtype: String
|
||||
@return: Path.
|
||||
"""
|
||||
dom_path = xroot.get_xend_domains_path()
|
||||
dom_path = xoptions.get_xend_domains_path()
|
||||
if domuuid:
|
||||
dom_path = os.path.join(dom_path, domuuid)
|
||||
return dom_path
|
||||
@@ -361,7 +359,7 @@ class XendDomain:
|
||||
|
||||
def _setDom0CPUCount(self):
|
||||
"""Sets the number of VCPUs dom0 has. Retreived from the
|
||||
Xend configuration, L{XendRoot}.
|
||||
Xend configuration, L{XendOptions}.
|
||||
|
||||
@requires: Expects to be protected by domains_lock.
|
||||
@rtype: None
|
||||
@@ -369,7 +367,7 @@ class XendDomain:
|
||||
dom0 = self.privilegedDomain()
|
||||
|
||||
# get max number of vcpus to use for dom0 from config
|
||||
target = int(xroot.get_dom0_vcpus())
|
||||
target = int(xoptions.get_dom0_vcpus())
|
||||
log.debug("number of vcpus to use is %d", target)
|
||||
|
||||
# target == 0 means use all processors
|
||||
@@ -377,7 +375,7 @@ class XendDomain:
|
||||
dom0.setVCpuCount(target)
|
||||
|
||||
|
||||
def _refresh(self):
|
||||
def _refresh(self, refresh_shutdown = True):
|
||||
"""Refresh the domain list. Needs to be called when
|
||||
either xenstore has changed or when a method requires
|
||||
up to date information (like uptime, cputime stats).
|
||||
@@ -393,11 +391,10 @@ class XendDomain:
|
||||
for dom in running:
|
||||
domid = dom['domid']
|
||||
if domid in self.domains:
|
||||
self.domains[domid].update(dom)
|
||||
self.domains[domid].update(dom, refresh_shutdown)
|
||||
elif domid not in self.domains and dom['dying'] != 1:
|
||||
try:
|
||||
new_dom = XendDomainInfo.recreate(dom, False)
|
||||
self._add_domain(new_dom)
|
||||
except VmError:
|
||||
log.exception("Unable to recreate domain")
|
||||
try:
|
||||
@@ -416,10 +413,10 @@ class XendDomain:
|
||||
running_domids = [d['domid'] for d in running if d['dying'] != 1]
|
||||
for domid, dom in self.domains.items():
|
||||
if domid not in running_domids and domid != DOM0_ID:
|
||||
self._remove_domain(dom, domid)
|
||||
self.remove_domain(dom, domid)
|
||||
|
||||
|
||||
def _add_domain(self, info):
|
||||
def add_domain(self, info):
|
||||
"""Add a domain to the list of running domains
|
||||
|
||||
@requires: Expects to be protected by the domains_lock.
|
||||
@@ -434,7 +431,7 @@ class XendDomain:
|
||||
if info.get_uuid() in self.managed_domains:
|
||||
self._managed_domain_register(info)
|
||||
|
||||
def _remove_domain(self, info, domid = None):
|
||||
def remove_domain(self, info, domid = None):
|
||||
"""Remove the domain from the list of running domains
|
||||
|
||||
@requires: Expects to be protected by the domains_lock.
|
||||
@@ -473,7 +470,6 @@ class XendDomain:
|
||||
try:
|
||||
security.refresh_ssidref(config)
|
||||
dominfo = XendDomainInfo.restore(config)
|
||||
self._add_domain(dominfo)
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
@@ -495,7 +491,7 @@ class XendDomain:
|
||||
"""
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self._refresh()
|
||||
self._refresh(refresh_shutdown = False)
|
||||
dom = self.domain_lookup_nr(domid)
|
||||
if not dom:
|
||||
raise XendError("No domain named '%s'." % str(domid))
|
||||
@@ -640,18 +636,18 @@ class XendDomain:
|
||||
self.domains_lock.release()
|
||||
|
||||
def get_dev_property_by_uuid(self, klass, dev_uuid, field):
|
||||
value = None
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
dom = self.get_vm_with_dev_uuid(klass, dev_uuid)
|
||||
if not dom:
|
||||
return None
|
||||
|
||||
value = dom.get_device_property(klass, dev_uuid, field)
|
||||
return value
|
||||
if dom:
|
||||
value = dom.get_dev_property(klass, dev_uuid, field)
|
||||
except ValueError, e:
|
||||
pass
|
||||
|
||||
self.domains_lock.release()
|
||||
|
||||
return None
|
||||
return value
|
||||
|
||||
def is_valid_vm(self, vm_ref):
|
||||
return (self.get_vm_by_uuid(vm_ref) != None)
|
||||
@@ -731,7 +727,7 @@ class XendDomain:
|
||||
|
||||
self.domains_lock.acquire()
|
||||
try:
|
||||
self._refresh()
|
||||
self._refresh(refresh_shutdown = False)
|
||||
|
||||
# active domains
|
||||
active_domains = self.domains.values()
|
||||
@@ -848,7 +844,6 @@ class XendDomain:
|
||||
os.open(chkpath, os.O_RDONLY),
|
||||
dominfo,
|
||||
paused = start_paused)
|
||||
self._add_domain(dominfo)
|
||||
os.unlink(chkpath)
|
||||
except OSError, ex:
|
||||
raise XendError("Failed to read stored checkpoint file")
|
||||
@@ -873,10 +868,10 @@ class XendDomain:
|
||||
self._refresh()
|
||||
|
||||
dominfo = XendDomainInfo.create(config)
|
||||
self._add_domain(dominfo)
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
if XendNode.instance().xenschedinfo() == 'credit':
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
@@ -893,10 +888,10 @@ class XendDomain:
|
||||
self._refresh()
|
||||
|
||||
dominfo = XendDomainInfo.create_from_dict(config_dict)
|
||||
self._add_domain(dominfo)
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
if XendNode.instance().xenschedinfo() == 'credit':
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
return dominfo
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
@@ -950,7 +945,10 @@ class XendDomain:
|
||||
POWER_STATE_NAMES[dominfo.state])
|
||||
|
||||
dominfo.start(is_managed = True)
|
||||
self._add_domain(dominfo)
|
||||
if XendNode.instance().xenschedinfo() == 'credit':
|
||||
self.domain_sched_credit_set(dominfo.getDomid(),
|
||||
dominfo.getWeight(),
|
||||
dominfo.getCap())
|
||||
finally:
|
||||
self.domains_lock.release()
|
||||
dominfo.waitForDevices()
|
||||
@@ -983,7 +981,7 @@ class XendDomain:
|
||||
(dominfo.getName(), dominfo.info.get('uuid')))
|
||||
|
||||
self._managed_domain_unregister(dominfo)
|
||||
self._remove_domain(dominfo)
|
||||
self.remove_domain(dominfo)
|
||||
XendDevices.destroy_device_state(dominfo)
|
||||
except Exception, ex:
|
||||
raise XendError(str(ex))
|
||||
@@ -1164,7 +1162,7 @@ class XendDomain:
|
||||
dominfo.checkLiveMigrateMemory()
|
||||
|
||||
if port == 0:
|
||||
port = xroot.get_xend_relocation_port()
|
||||
port = xoptions.get_xend_relocation_port()
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect((dst, port))
|
||||
@@ -1174,7 +1172,6 @@ class XendDomain:
|
||||
sock.send("receive\n")
|
||||
sock.recv(80)
|
||||
XendCheckpoint.save(sock.fileno(), dominfo, True, live, dst)
|
||||
dominfo.testDeviceComplete()
|
||||
sock.close()
|
||||
|
||||
def domain_save(self, domid, dst):
|
||||
|
||||
@@ -37,15 +37,16 @@ from xen.util import asserts
|
||||
from xen.util.blkif import blkdev_uname_to_file
|
||||
from xen.util import security
|
||||
|
||||
from xen.xend import balloon, sxp, uuid, image, arch
|
||||
from xen.xend import XendRoot, XendNode, XendConfig
|
||||
from xen.xend import balloon, sxp, uuid, image, arch, osdep
|
||||
from xen.xend import XendOptions, XendNode, XendConfig
|
||||
|
||||
from xen.xend.XendConfig import scrub_password
|
||||
from xen.xend.XendBootloader import bootloader
|
||||
from xen.xend.XendBootloader import bootloader, bootloader_tidy
|
||||
from xen.xend.XendError import XendError, VmError
|
||||
from xen.xend.XendDevices import XendDevices
|
||||
from xen.xend.XendTask import XendTask
|
||||
from xen.xend.xenstore.xstransact import xstransact, complete
|
||||
from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain
|
||||
from xen.xend.xenstore.xsutil import GetDomainPath, IntroduceDomain, ResumeDomain
|
||||
from xen.xend.xenstore.xswatch import xswatch
|
||||
from xen.xend.XendConstants import *
|
||||
from xen.xend.XendAPIConstants import *
|
||||
@@ -54,31 +55,12 @@ MIGRATE_TIMEOUT = 30.0
|
||||
BOOTLOADER_LOOPBACK_DEVICE = '/dev/xvdp'
|
||||
|
||||
xc = xen.lowlevel.xc.xc()
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
log = logging.getLogger("xend.XendDomainInfo")
|
||||
#log.setLevel(logging.TRACE)
|
||||
|
||||
|
||||
#
|
||||
# There are a number of CPU-related fields:
|
||||
#
|
||||
# vcpus: the number of virtual CPUs this domain is configured to use.
|
||||
# vcpu_avail: a bitmap telling the guest domain whether it may use each of
|
||||
# its VCPUs. This is translated to
|
||||
# <dompath>/cpu/<id>/availability = {online,offline} for use
|
||||
# by the guest domain.
|
||||
# cpumap: a list of bitmaps, one for each VCPU, giving the physical
|
||||
# CPUs that that VCPU may use.
|
||||
# cpu: a configuration setting requesting that VCPU 0 is pinned to
|
||||
# the specified physical CPU.
|
||||
#
|
||||
# vcpus and vcpu_avail settings persist with the VM (i.e. they are persistent
|
||||
# across save, restore, migrate, and restart). The other settings are only
|
||||
# specific to the domain, so are lost when the VM moves.
|
||||
#
|
||||
|
||||
|
||||
def create(config):
|
||||
"""Creates and start a VM using the supplied configuration.
|
||||
|
||||
@@ -120,7 +102,6 @@ def create_from_dict(config_dict):
|
||||
log.exception('Domain construction failed')
|
||||
vm.destroy()
|
||||
raise
|
||||
|
||||
return vm
|
||||
|
||||
def recreate(info, priv):
|
||||
@@ -206,6 +187,11 @@ def recreate(info, priv):
|
||||
|
||||
vm._registerWatches()
|
||||
vm.refreshShutdown(xeninfo)
|
||||
|
||||
# register the domain in the list
|
||||
from xen.xend import XendDomain
|
||||
XendDomain.instance().add_domain(vm)
|
||||
|
||||
return vm
|
||||
|
||||
|
||||
@@ -402,12 +388,13 @@ class XendDomainInfo:
|
||||
|
||||
if self.state == DOM_STATE_HALTED:
|
||||
try:
|
||||
self._constructDomain()
|
||||
self._initDomain()
|
||||
self._storeVmDetails()
|
||||
self._storeDomDetails()
|
||||
self._registerWatches()
|
||||
self.refreshShutdown()
|
||||
XendTask.log_progress(0, 30, self._constructDomain)
|
||||
XendTask.log_progress(31, 60, self._initDomain)
|
||||
|
||||
XendTask.log_progress(61, 70, self._storeVmDetails)
|
||||
XendTask.log_progress(71, 80, self._storeDomDetails)
|
||||
XendTask.log_progress(81, 90, self._registerWatches)
|
||||
XendTask.log_progress(91, 100, self.refreshShutdown)
|
||||
|
||||
# save running configuration if XendDomains believe domain is
|
||||
# persistent
|
||||
@@ -439,7 +426,7 @@ class XendDomainInfo:
|
||||
|
||||
def shutdown(self, reason):
|
||||
"""Shutdown a domain by signalling this via xenstored."""
|
||||
log.debug('XendDomainInfo.shutdown')
|
||||
log.debug('XendDomainInfo.shutdown(%s)', reason)
|
||||
if self.state in (DOM_STATE_SHUTDOWN, DOM_STATE_HALTED,):
|
||||
raise XendError('Domain cannot be shutdown')
|
||||
|
||||
@@ -451,6 +438,16 @@ class XendDomainInfo:
|
||||
self._removeVm('xend/previous_restart_time')
|
||||
self.storeDom("control/shutdown", reason)
|
||||
|
||||
## shutdown hypercall for hvm domain desides xenstore write
|
||||
image_cfg = self.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
if hvm:
|
||||
for code in DOMAIN_SHUTDOWN_REASONS.keys():
|
||||
if DOMAIN_SHUTDOWN_REASONS[code] == reason:
|
||||
break
|
||||
xc.domain_shutdown(self.domid, code)
|
||||
|
||||
|
||||
def pause(self):
|
||||
"""Pause domain
|
||||
|
||||
@@ -496,7 +493,7 @@ class XendDomainInfo:
|
||||
self._waitForDevice(dev_type, devid)
|
||||
return self.getDeviceController(dev_type).sxpr(devid)
|
||||
|
||||
def device_configure(self, dev_config, devid = None):
|
||||
def device_configure(self, dev_sxp, devid = None):
|
||||
"""Configure an existing device.
|
||||
|
||||
@param dev_config: device configuration
|
||||
@@ -506,19 +503,24 @@ class XendDomainInfo:
|
||||
@return: Returns True if successfully updated device
|
||||
@rtype: boolean
|
||||
"""
|
||||
deviceClass = sxp.name(dev_config)
|
||||
|
||||
# look up uuid of the device
|
||||
dev_control = self.getDeviceController(deviceClass)
|
||||
dev_sxpr = dev_control.sxpr(devid)
|
||||
dev_uuid = sxp.child_value(sxpr, 'uuid')
|
||||
if not dev_uuid:
|
||||
return False
|
||||
|
||||
self.info.device_update(dev_uuid, dev_config)
|
||||
dev_config_dict = self.info['devices'].get(dev_uuid)
|
||||
if dev_config_dict:
|
||||
dev_control.reconfigureDevice(devid, dev_config_dict[1])
|
||||
# convert device sxp to a dict
|
||||
dev_class = sxp.name(dev_sxp)
|
||||
dev_config = {}
|
||||
for opt_val in dev_sxp[1:]:
|
||||
try:
|
||||
dev_config[opt_val[0]] = opt_val[1]
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
# use DevController.reconfigureDevice to change device config
|
||||
dev_control = self.getDeviceController(dev_class)
|
||||
dev_uuid = dev_control.reconfigureDevice(devid, dev_config)
|
||||
|
||||
# update XendConfig with new device info
|
||||
if dev_uuid:
|
||||
self.info.device_update(dev_uuid, dev_sxp)
|
||||
|
||||
return True
|
||||
|
||||
def waitForDevices(self):
|
||||
@@ -546,8 +548,6 @@ class XendDomainInfo:
|
||||
|
||||
return self.getDeviceController(deviceClass).destroyDevice(devid, force)
|
||||
|
||||
|
||||
|
||||
def getDeviceSxprs(self, deviceClass):
|
||||
if self.state == DOM_STATE_RUNNING:
|
||||
return self.getDeviceController(deviceClass).sxprs()
|
||||
@@ -609,7 +609,7 @@ class XendDomainInfo:
|
||||
['name', self.info['name_label']],
|
||||
['vcpu_count', self.info['vcpus_number']]]
|
||||
|
||||
for i in range(0, self.info['max_vcpu_id']+1):
|
||||
for i in range(0, self.info['vcpus_number']):
|
||||
info = xc.vcpu_getinfo(self.domid, i)
|
||||
|
||||
sxpr.append(['vcpu',
|
||||
@@ -682,6 +682,42 @@ class XendDomainInfo:
|
||||
for device in devices:
|
||||
self.info.device_add(device[0], cfg_sxp = device)
|
||||
|
||||
self._update_consoles()
|
||||
|
||||
def _update_consoles(self):
|
||||
if self.domid == None or self.domid == 0:
|
||||
return
|
||||
|
||||
# Update VT100 port if it exists
|
||||
self.console_port = self.readDom('console/port')
|
||||
if self.console_port is not None:
|
||||
serial_consoles = self.info.console_get_all('vt100')
|
||||
if not serial_consoles:
|
||||
cfg = self.info.console_add('vt100', self.console_port)
|
||||
self._createDevice('console', cfg)
|
||||
else:
|
||||
console_uuid = serial_consoles[0].get('uuid')
|
||||
self.info.console_update(console_uuid, 'location',
|
||||
self.console_port)
|
||||
|
||||
|
||||
# Update VNC port if it exists and write to xenstore
|
||||
vnc_port = self.readDom('console/vnc-port')
|
||||
if vnc_port is not None:
|
||||
for dev_uuid, (dev_type, dev_info) in self.info['devices'].items():
|
||||
if dev_type == 'vfb':
|
||||
old_location = dev_info.get('location')
|
||||
listen_host = dev_info.get('vnclisten', 'localhost')
|
||||
new_location = '%s:%s' % (listen_host, str(vnc_port))
|
||||
if old_location == new_location:
|
||||
break
|
||||
|
||||
dev_info['location'] = new_location
|
||||
self.info.device_update(dev_uuid, cfg_xenapi = dev_info)
|
||||
vfb_ctrl = self.getDeviceController('vfb')
|
||||
vfb_ctrl.reconfigureDevice(0, dev_info)
|
||||
break
|
||||
|
||||
#
|
||||
# Function to update xenstore /vm/*
|
||||
#
|
||||
@@ -734,7 +770,7 @@ class XendDomainInfo:
|
||||
'domid': str(self.domid),
|
||||
'vm': self.vmpath,
|
||||
'name': self.info['name_label'],
|
||||
'console/limit': str(xroot.get_console_limit() * 1024),
|
||||
'console/limit': str(xoptions.get_console_limit() * 1024),
|
||||
'memory/target': str(self.info['memory_static_min'] * 1024)
|
||||
}
|
||||
|
||||
@@ -893,8 +929,9 @@ class XendDomainInfo:
|
||||
self._writeDom(self._vcpuDomDetails())
|
||||
else:
|
||||
self.info['vcpus_number'] = vcpus
|
||||
self.info['online_vcpus'] = vcpus
|
||||
xen.xend.XendDomain.instance().managed_config_save(self)
|
||||
log.info("Set VCPU count on domain %s to %d", self.info['name_label'],
|
||||
vcpus)
|
||||
|
||||
def getLabel(self):
|
||||
return security.get_security_info(self.info, 'label')
|
||||
@@ -914,7 +951,7 @@ class XendDomainInfo:
|
||||
return self.info.get('cpu_cap', 0)
|
||||
|
||||
def getWeight(self):
|
||||
return self.info['cpu_weight']
|
||||
return self.info.get('cpu_weight', 256)
|
||||
|
||||
def setResume(self, state):
|
||||
self._resume = state
|
||||
@@ -969,9 +1006,15 @@ class XendDomainInfo:
|
||||
|
||||
log.warn('Domain has crashed: name=%s id=%d.',
|
||||
self.info['name_label'], self.domid)
|
||||
self._writeVm(LAST_SHUTDOWN_REASON, 'crash')
|
||||
|
||||
if xroot.get_enable_dump():
|
||||
self.dumpCore()
|
||||
if xoptions.get_enable_dump():
|
||||
try:
|
||||
self.dumpCore()
|
||||
except XendError:
|
||||
# This error has been logged -- there's nothing more
|
||||
# we can do in this context.
|
||||
pass
|
||||
|
||||
restart_reason = 'crash'
|
||||
self._stateSet(DOM_STATE_HALTED)
|
||||
@@ -988,6 +1031,7 @@ class XendDomainInfo:
|
||||
|
||||
log.info('Domain has shutdown: name=%s id=%d reason=%s.',
|
||||
self.info['name_label'], self.domid, reason)
|
||||
self._writeVm(LAST_SHUTDOWN_REASON, reason)
|
||||
|
||||
self._clearRestart()
|
||||
|
||||
@@ -1162,7 +1206,10 @@ class XendDomainInfo:
|
||||
#
|
||||
|
||||
def dumpCore(self, corefile = None):
|
||||
"""Create a core dump for this domain. Nothrow guarantee."""
|
||||
"""Create a core dump for this domain.
|
||||
|
||||
@raise: XendError if core dumping failed.
|
||||
"""
|
||||
|
||||
try:
|
||||
if not corefile:
|
||||
@@ -1205,16 +1252,26 @@ class XendDomainInfo:
|
||||
|
||||
@raise: VmError for invalid devices
|
||||
"""
|
||||
for (devclass, config) in self.info.get('devices', {}).values():
|
||||
if devclass in XendDevices.valid_devices():
|
||||
ordered_refs = self.info.ordered_device_refs()
|
||||
for dev_uuid in ordered_refs:
|
||||
devclass, config = self.info['devices'][dev_uuid]
|
||||
if devclass in XendDevices.valid_devices():
|
||||
log.info("createDevice: %s : %s" % (devclass, scrub_password(config)))
|
||||
self._createDevice(devclass, config)
|
||||
dev_uuid = config.get('uuid')
|
||||
devid = self._createDevice(devclass, config)
|
||||
|
||||
# store devid in XendConfig for caching reasons
|
||||
if dev_uuid in self.info['devices']:
|
||||
self.info['devices'][dev_uuid][1]['devid'] = devid
|
||||
|
||||
if self.image:
|
||||
self.image.createDeviceModel()
|
||||
|
||||
def _releaseDevices(self):
|
||||
def _releaseDevices(self, suspend = False):
|
||||
"""Release all domain's devices. Nothrow guarantee."""
|
||||
if suspend and self.image:
|
||||
self.image.destroy(suspend)
|
||||
return
|
||||
|
||||
while True:
|
||||
t = xstransact("%s/device" % self.dompath)
|
||||
@@ -1328,6 +1385,9 @@ class XendDomainInfo:
|
||||
# Set maximum number of vcpus in domain
|
||||
xc.domain_max_vcpus(self.domid, int(self.info['vcpus_number']))
|
||||
|
||||
# register the domain in the list
|
||||
from xen.xend import XendDomain
|
||||
XendDomain.instance().add_domain(self)
|
||||
|
||||
def _introduceDomain(self):
|
||||
assert self.domid is not None
|
||||
@@ -1366,7 +1426,7 @@ class XendDomainInfo:
|
||||
# this is done prior to memory allocation to aide in memory
|
||||
# distribution for NUMA systems.
|
||||
if self.info['cpus'] is not None and len(self.info['cpus']) > 0:
|
||||
for v in range(0, self.info['max_vcpu_id']+1):
|
||||
for v in range(0, self.info['vcpus_number']):
|
||||
xc.vcpu_setaffinity(self.domid, v, self.info['cpus'])
|
||||
|
||||
# Use architecture- and image-specific calculations to determine
|
||||
@@ -1380,6 +1440,7 @@ class XendDomainInfo:
|
||||
self.info['shadow_memory'] * 1024,
|
||||
self.info['memory_static_max'] * 1024)
|
||||
|
||||
log.debug("_initDomain:shadow_memory=0x%x, memory_static_max=0x%x, memory_static_min=0x%x.", self.info['shadow_memory'], self.info['memory_static_max'], self.info['memory_static_min'],)
|
||||
# Round shadow up to a multiple of a MiB, as shadow_mem_control
|
||||
# takes MiB and we must not round down and end up under-providing.
|
||||
shadow = ((shadow + 1023) / 1024) * 1024
|
||||
@@ -1425,6 +1486,7 @@ class XendDomainInfo:
|
||||
try:
|
||||
self.unwatchShutdown()
|
||||
self._releaseDevices()
|
||||
bootloader_tidy(self)
|
||||
|
||||
if self.image:
|
||||
try:
|
||||
@@ -1479,6 +1541,16 @@ class XendDomainInfo:
|
||||
self.console_mfn = console_mfn
|
||||
|
||||
self._introduceDomain()
|
||||
image_cfg = self.info.get('image', {})
|
||||
hvm = image_cfg.has_key('hvm')
|
||||
if hvm:
|
||||
self.image = image.create(self,
|
||||
self.info,
|
||||
self.info['image'],
|
||||
self.info['devices'])
|
||||
if self.image:
|
||||
self.image.createDeviceModel(True)
|
||||
self.image.register_shutdown_watch()
|
||||
self._storeDomDetails()
|
||||
self._registerWatches()
|
||||
self.refreshShutdown()
|
||||
@@ -1515,9 +1587,22 @@ class XendDomainInfo:
|
||||
except:
|
||||
log.exception("XendDomainInfo.destroy: xc.domain_destroy failed.")
|
||||
|
||||
from xen.xend import XendDomain
|
||||
XendDomain.instance().remove_domain(self)
|
||||
|
||||
self.cleanupDomain()
|
||||
|
||||
|
||||
def resumeDomain(self):
|
||||
log.debug("XendDomainInfo.resumeDomain(%s)", str(self.domid))
|
||||
|
||||
try:
|
||||
if self.domid is not None:
|
||||
xc.domain_resume(self.domid)
|
||||
ResumeDomain(self.domid)
|
||||
except:
|
||||
log.exception("XendDomainInfo.resume: xc.domain_resume failed on domain %s." % (str(self.domid)))
|
||||
|
||||
#
|
||||
# Channels for xenstore and console
|
||||
#
|
||||
@@ -1533,11 +1618,24 @@ class XendDomainInfo:
|
||||
"""Create an event channel to the domain.
|
||||
"""
|
||||
try:
|
||||
return xc.evtchn_alloc_unbound(domid=self.domid, remote_dom=0)
|
||||
if self.domid != None:
|
||||
return xc.evtchn_alloc_unbound(domid = self.domid,
|
||||
remote_dom = 0)
|
||||
except:
|
||||
log.exception("Exception in alloc_unbound(%d)", self.domid)
|
||||
log.exception("Exception in alloc_unbound(%s)", str(self.domid))
|
||||
raise
|
||||
|
||||
def _resetChannels(self):
|
||||
"""Reset all event channels in the domain.
|
||||
"""
|
||||
try:
|
||||
if self.domid != None:
|
||||
return xc.evtchn_reset(dom = self.domid)
|
||||
except:
|
||||
log.exception("Exception in evtcnh_reset(%s)", str(self.domid))
|
||||
raise
|
||||
|
||||
|
||||
#
|
||||
# Bootloader configuration
|
||||
#
|
||||
@@ -1550,14 +1648,15 @@ class XendDomainInfo:
|
||||
kernel = self.info['PV_kernel']
|
||||
ramdisk = self.info['PV_ramdisk']
|
||||
args = self.info['PV_args']
|
||||
boot = self.info['HVM_boot']
|
||||
boot = self.info['HVM_boot_policy']
|
||||
|
||||
if boot:
|
||||
# HVM booting.
|
||||
self.info['image']['type'] = 'hvm'
|
||||
if not 'devices' in self.info['image']:
|
||||
self.info['image']['devices'] = {}
|
||||
self.info['image']['devices']['boot'] = boot
|
||||
self.info['image']['devices']['boot'] = \
|
||||
self.info['HVM_boot_params'].get('order', 'dc')
|
||||
elif not blexec and kernel:
|
||||
# Boot from dom0. Nothing left to do -- the kernel and ramdisk
|
||||
# will be picked up by image.py.
|
||||
@@ -1565,51 +1664,50 @@ class XendDomainInfo:
|
||||
else:
|
||||
# Boot using bootloader
|
||||
if not blexec or blexec == 'pygrub':
|
||||
blexec = '/usr/bin/pygrub'
|
||||
blexec = osdep.pygrub_path
|
||||
|
||||
blcfg = None
|
||||
for (devtype, devinfo) in self.info.all_devices_sxpr():
|
||||
if not devtype or not devinfo or devtype not in ('vbd', 'tap'):
|
||||
continue
|
||||
disk = None
|
||||
for param in devinfo:
|
||||
if param[0] == 'uname':
|
||||
disk = param[1]
|
||||
break
|
||||
disks = [x for x in self.info['vbd_refs']
|
||||
if self.info['devices'][x][1]['bootable']]
|
||||
|
||||
if disk is None:
|
||||
continue
|
||||
fn = blkdev_uname_to_file(disk)
|
||||
mounted = devtype == 'tap' and not os.stat(fn).st_rdev
|
||||
if not disks:
|
||||
msg = "Had a bootloader specified, but no disks are bootable"
|
||||
log.error(msg)
|
||||
raise VmError(msg)
|
||||
|
||||
devinfo = self.info['devices'][disks[0]]
|
||||
devtype = devinfo[0]
|
||||
disk = devinfo[1]['uname']
|
||||
|
||||
fn = blkdev_uname_to_file(disk)
|
||||
mounted = devtype == 'tap' and not os.stat(fn).st_rdev
|
||||
if mounted:
|
||||
# This is a file, not a device. pygrub can cope with a
|
||||
# file if it's raw, but if it's QCOW or other such formats
|
||||
# used through blktap, then we need to mount it first.
|
||||
|
||||
log.info("Mounting %s on %s." %
|
||||
(fn, BOOTLOADER_LOOPBACK_DEVICE))
|
||||
|
||||
vbd = {
|
||||
'mode': 'RO',
|
||||
'device': BOOTLOADER_LOOPBACK_DEVICE,
|
||||
}
|
||||
|
||||
from xen.xend import XendDomain
|
||||
dom0 = XendDomain.instance().privilegedDomain()
|
||||
dom0._waitForDeviceUUID(dom0.create_vbd(vbd, fn))
|
||||
fn = BOOTLOADER_LOOPBACK_DEVICE
|
||||
|
||||
try:
|
||||
blcfg = bootloader(blexec, fn, self, False,
|
||||
bootloader_args, kernel, ramdisk, args)
|
||||
finally:
|
||||
if mounted:
|
||||
# This is a file, not a device. pygrub can cope with a
|
||||
# file if it's raw, but if it's QCOW or other such formats
|
||||
# used through blktap, then we need to mount it first.
|
||||
|
||||
log.info("Mounting %s on %s." %
|
||||
log.info("Unmounting %s from %s." %
|
||||
(fn, BOOTLOADER_LOOPBACK_DEVICE))
|
||||
|
||||
vbd = {
|
||||
'mode': 'RO',
|
||||
'device': BOOTLOADER_LOOPBACK_DEVICE,
|
||||
}
|
||||
|
||||
from xen.xend import XendDomain
|
||||
dom0 = XendDomain.instance().privilegedDomain()
|
||||
dom0._waitForDeviceUUID(dom0.create_vbd_with_vdi(vbd, fn))
|
||||
fn = BOOTLOADER_LOOPBACK_DEVICE
|
||||
|
||||
try:
|
||||
blcfg = bootloader(blexec, fn, True,
|
||||
bootloader_args, kernel, ramdisk, args)
|
||||
finally:
|
||||
if mounted:
|
||||
log.info("Unmounting %s from %s." %
|
||||
(fn, BOOTLOADER_LOOPBACK_DEVICE))
|
||||
|
||||
dom0.destroyDevice('tap', '/dev/xvdp')
|
||||
|
||||
break
|
||||
dom0.destroyDevice('tap', '/dev/xvdp')
|
||||
|
||||
if blcfg is None:
|
||||
msg = "Had a bootloader specified, but can't find disk"
|
||||
@@ -1695,6 +1793,25 @@ class XendDomainInfo:
|
||||
log.info("Dev still active but hit max loop timeout")
|
||||
break
|
||||
|
||||
def testvifsComplete(self):
|
||||
""" In case vifs are released and then created for the same
|
||||
domain, we need to wait the device shut down.
|
||||
"""
|
||||
start = time.time()
|
||||
while True:
|
||||
test = 0
|
||||
diff = time.time() - start
|
||||
for i in self.getDeviceController('vif').deviceIDs():
|
||||
test = 1
|
||||
log.info("Dev %s still active, looping...", i)
|
||||
time.sleep(0.1)
|
||||
|
||||
if test == 0:
|
||||
break
|
||||
if diff >= MIGRATE_TIMEOUT:
|
||||
log.info("Dev still active but hit max loop timeout")
|
||||
break
|
||||
|
||||
def _storeVmDetails(self):
|
||||
to_store = {}
|
||||
|
||||
@@ -1811,7 +1928,8 @@ class XendDomainInfo:
|
||||
# TODO: we should eventually get rid of old_dom_states
|
||||
|
||||
self.info.update_config(info)
|
||||
|
||||
self._update_consoles()
|
||||
|
||||
if refresh:
|
||||
self.refreshShutdown(info)
|
||||
|
||||
@@ -1823,11 +1941,11 @@ class XendDomainInfo:
|
||||
ignore_devices = ignore_store,
|
||||
legacy_only = legacy_only)
|
||||
|
||||
if not ignore_store and self.dompath:
|
||||
vnc_port = self.readDom('console/vnc-port')
|
||||
if vnc_port is not None:
|
||||
result.append(['device',
|
||||
['console', ['vnc-port', str(vnc_port)]]])
|
||||
#if not ignore_store and self.dompath:
|
||||
# vnc_port = self.readDom('console/vnc-port')
|
||||
# if vnc_port is not None:
|
||||
# result.append(['device',
|
||||
# ['console', ['vnc-port', str(vnc_port)]]])
|
||||
|
||||
return result
|
||||
|
||||
@@ -1859,7 +1977,11 @@ class XendDomainInfo:
|
||||
else:
|
||||
return 'unknown'
|
||||
def get_vcpus_params(self):
|
||||
return '' # TODO
|
||||
if self.getDomid() is None:
|
||||
return self.info['vcpus_params']
|
||||
|
||||
retval = xc.sched_credit_domain_get(self.getDomid())
|
||||
return retval
|
||||
def get_power_state(self):
|
||||
return XEN_API_VM_POWER_STATE[self.state]
|
||||
def get_platform_std_vga(self):
|
||||
@@ -1875,11 +1997,9 @@ class XendDomainInfo:
|
||||
def get_platform_keymap(self):
|
||||
return self.info.get('platform_keymap', '')
|
||||
def get_pci_bus(self):
|
||||
return '' # TODO
|
||||
return self.info.get('pci_bus', '')
|
||||
def get_tools_version(self):
|
||||
return {} # TODO
|
||||
def get_other_config(self):
|
||||
return {} # TODO
|
||||
return self.info.get('tools_version', {})
|
||||
|
||||
def get_on_shutdown(self):
|
||||
after_shutdown = self.info.get('action_after_shutdown')
|
||||
@@ -1915,21 +2035,18 @@ class XendDomainInfo:
|
||||
|
||||
@rtype: dictionary
|
||||
"""
|
||||
dev_type_config = self.info['devices'].get(dev_uuid)
|
||||
dev_type, dev_config = self.info['devices'].get(dev_uuid, (None, None))
|
||||
|
||||
# shortcut if the domain isn't started because
|
||||
# the devcontrollers will have no better information
|
||||
# than XendConfig.
|
||||
if self.state in (XEN_API_VM_POWER_STATE_HALTED,):
|
||||
if dev_type_config:
|
||||
return copy.deepcopy(dev_type_config[1])
|
||||
if dev_config:
|
||||
return copy.deepcopy(dev_config)
|
||||
return None
|
||||
|
||||
# instead of using dev_class, we use the dev_type
|
||||
# that is from XendConfig.
|
||||
# This will accomdate 'tap' as well as 'vbd'
|
||||
dev_type = dev_type_config[0]
|
||||
|
||||
controller = self.getDeviceController(dev_type)
|
||||
if not controller:
|
||||
return None
|
||||
@@ -1938,14 +2055,14 @@ class XendDomainInfo:
|
||||
if not all_configs:
|
||||
return None
|
||||
|
||||
dev_config = copy.deepcopy(dev_type_config[1])
|
||||
updated_dev_config = copy.deepcopy(dev_config)
|
||||
for _devid, _devcfg in all_configs.items():
|
||||
if _devcfg.get('uuid') == dev_uuid:
|
||||
dev_config.update(_devcfg)
|
||||
dev_config['id'] = _devid
|
||||
return dev_config
|
||||
updated_dev_config.update(_devcfg)
|
||||
updated_dev_config['id'] = _devid
|
||||
return updated_dev_config
|
||||
|
||||
return dev_config
|
||||
return updated_dev_config
|
||||
|
||||
def get_dev_xenapi_config(self, dev_class, dev_uuid):
|
||||
config = self.get_dev_config_by_uuid(dev_class, dev_uuid)
|
||||
@@ -1981,23 +2098,52 @@ class XendDomainInfo:
|
||||
# handle that properly.
|
||||
|
||||
config['MTU'] = 1500 # TODO
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
|
||||
if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
|
||||
xennode = XendNode.instance()
|
||||
rx_bps, tx_bps = xennode.get_vif_util(self.domid, devid)
|
||||
config['io_read_kbs'] = rx_bps/1024
|
||||
config['io_write_kbs'] = tx_bps/1024
|
||||
else:
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
|
||||
if dev_class == 'vbd':
|
||||
|
||||
if self.state not in (XEN_API_VM_POWER_STATE_HALTED,):
|
||||
controller = self.getDeviceController(dev_class)
|
||||
devid, _1, _2 = controller.getDeviceDetails(config)
|
||||
xennode = XendNode.instance()
|
||||
rd_blkps, wr_blkps = xennode.get_vbd_util(self.domid, devid)
|
||||
config['io_read_kbs'] = rd_blkps
|
||||
config['io_write_kbs'] = wr_blkps
|
||||
else:
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
|
||||
config['VDI'] = config.get('VDI', '')
|
||||
config['device'] = config.get('dev', '')
|
||||
if ':' in config['device']:
|
||||
vbd_name, vbd_type = config['device'].split(':', 1)
|
||||
config['device'] = vbd_name
|
||||
if vbd_type == 'cdrom':
|
||||
config['type'] = XEN_API_VBD_TYPE[0]
|
||||
else:
|
||||
config['type'] = XEN_API_VBD_TYPE[1]
|
||||
|
||||
config['driver'] = 'paravirtualised' # TODO
|
||||
config['image'] = config.get('uname', '')
|
||||
config['io_read_kbs'] = 0.0
|
||||
config['io_write_kbs'] = 0.0
|
||||
if config['mode'] == 'r':
|
||||
|
||||
if config.get('mode', 'r') == 'r':
|
||||
config['mode'] = 'RO'
|
||||
else:
|
||||
config['mode'] = 'RW'
|
||||
|
||||
if dev_class == 'vtpm':
|
||||
config['driver'] = 'paravirtualised' # TODO
|
||||
if not config.has_key('type'):
|
||||
config['type'] = 'paravirtualised' # TODO
|
||||
if not config.has_key('backend'):
|
||||
config['backend'] = "00000000-0000-0000-0000-000000000000"
|
||||
|
||||
return config
|
||||
|
||||
@@ -2008,15 +2154,16 @@ class XendDomainInfo:
|
||||
except KeyError:
|
||||
raise XendError('Invalid property for device: %s' % field)
|
||||
|
||||
def set_dev_property(self, dev_class, dev_uuid, field, value):
|
||||
self.info['devices'][dev_uuid][1][field] = value
|
||||
|
||||
def get_vcpus_util(self):
|
||||
# TODO: this returns the total accum cpu time, rather than util
|
||||
# TODO: spec says that key is int, however, python does not allow
|
||||
# non-string keys to dictionaries.
|
||||
vcpu_util = {}
|
||||
if 'max_vcpu_id' in self.info and self.domid != None:
|
||||
for i in range(0, self.info['max_vcpu_id']+1):
|
||||
info = xc.vcpu_getinfo(self.domid, i)
|
||||
vcpu_util[str(i)] = info['cpu_time']/1000000000.0
|
||||
xennode = XendNode.instance()
|
||||
if 'vcpus_number' in self.info and self.domid != None:
|
||||
for i in range(0, self.info['vcpus_number']):
|
||||
util = xennode.get_vcpu_util(self.domid, i)
|
||||
vcpu_util[str(i)] = util
|
||||
|
||||
return vcpu_util
|
||||
|
||||
@@ -2032,24 +2179,7 @@ class XendDomainInfo:
|
||||
def get_vtpms(self):
|
||||
return self.info.get('vtpm_refs', [])
|
||||
|
||||
def create_vbd(self, xenapi_vbd):
|
||||
"""Create a VBD device from the passed struct in Xen API format.
|
||||
|
||||
@return: uuid of the device
|
||||
@rtype: string
|
||||
"""
|
||||
|
||||
dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
|
||||
if not dev_uuid:
|
||||
raise XendError('Failed to create device')
|
||||
|
||||
if self.state == XEN_API_VM_POWER_STATE_RUNNING:
|
||||
_, config = self.info['devices'][dev_uuid]
|
||||
config['devid'] = self.getDeviceController('vbd').createDevice(config)
|
||||
|
||||
return dev_uuid
|
||||
|
||||
def create_vbd_with_vdi(self, xenapi_vbd, vdi_image_path):
|
||||
def create_vbd(self, xenapi_vbd, vdi_image_path):
|
||||
"""Create a VBD using a VDI from XendStorageRepository.
|
||||
|
||||
@param xenapi_vbd: vbd struct from the Xen API
|
||||
@@ -2058,14 +2188,26 @@ class XendDomainInfo:
|
||||
@return: uuid of the device
|
||||
"""
|
||||
xenapi_vbd['image'] = vdi_image_path
|
||||
log.debug('create_vbd_with_vdi: %s' % xenapi_vbd)
|
||||
dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
|
||||
log.debug('create_vbd: %s' % xenapi_vbd)
|
||||
dev_uuid = ''
|
||||
if vdi_image_path.startswith('tap'):
|
||||
dev_uuid = self.info.device_add('tap', cfg_xenapi = xenapi_vbd)
|
||||
else:
|
||||
dev_uuid = self.info.device_add('vbd', cfg_xenapi = xenapi_vbd)
|
||||
|
||||
if not dev_uuid:
|
||||
raise XendError('Failed to create device')
|
||||
|
||||
if self.state == XEN_API_VM_POWER_STATE_RUNNING:
|
||||
_, config = self.info['devices'][dev_uuid]
|
||||
config['devid'] = self.getDeviceController('tap').createDevice(config)
|
||||
dev_control = None
|
||||
|
||||
if vdi_image_path.startswith('tap'):
|
||||
dev_control = self.getDeviceController('tap')
|
||||
else:
|
||||
dev_control = self.getDeviceController('vbd')
|
||||
|
||||
config['devid'] = dev_control.createDevice(config)
|
||||
|
||||
return dev_uuid
|
||||
|
||||
@@ -2103,6 +2245,47 @@ class XendDomainInfo:
|
||||
|
||||
return dev_uuid
|
||||
|
||||
def create_console(self, xenapi_console):
|
||||
""" Create a console device from a Xen API struct.
|
||||
|
||||
@return: uuid of device
|
||||
@rtype: string
|
||||
"""
|
||||
if self.state not in (DOM_STATE_HALTED,):
|
||||
raise VmError("Can only add console to a halted domain.")
|
||||
|
||||
dev_uuid = self.info.device_add('console', cfg_xenapi = xenapi_console)
|
||||
if not dev_uuid:
|
||||
raise XendError('Failed to create device')
|
||||
|
||||
return dev_uuid
|
||||
|
||||
def destroy_device_by_uuid(self, dev_type, dev_uuid):
|
||||
if dev_uuid not in self.info['devices']:
|
||||
raise XendError('Device does not exist')
|
||||
|
||||
try:
|
||||
if self.state == XEN_API_VM_POWER_STATE_RUNNING:
|
||||
_, config = self.info['devices'][dev_uuid]
|
||||
devid = config.get('devid')
|
||||
if devid != None:
|
||||
self.getDeviceController(dev_type).destroyDevice(devid, force = False)
|
||||
else:
|
||||
raise XendError('Unable to get devid for device: %s:%s' %
|
||||
(dev_type, dev_uuid))
|
||||
finally:
|
||||
del self.info['devices'][dev_uuid]
|
||||
self.info['%s_refs' % dev_type].remove(dev_uuid)
|
||||
|
||||
def destroy_vbd(self, dev_uuid):
|
||||
self.destroy_device_by_uuid('vbd', dev_uuid)
|
||||
|
||||
def destroy_vif(self, dev_uuid):
|
||||
self.destroy_device_by_uuid('vif', dev_uuid)
|
||||
|
||||
def destroy_vtpm(self, dev_uuid):
|
||||
self.destroy_device_by_uuid('vtpm', dev_uuid)
|
||||
|
||||
def has_device(self, dev_class, dev_uuid):
|
||||
return (dev_uuid in self.info['%s_refs' % dev_class.lower()])
|
||||
|
||||
|
||||
@@ -43,6 +43,14 @@ class NetworkAlreadyConnected(XendError):
|
||||
XendError.__init__(self, 'Network already connected')
|
||||
self.pif_uuid = pif_uuid
|
||||
|
||||
class PIFIsPhysical(XendError):
|
||||
def __init__(self):
|
||||
XendError.__init__(self, 'PIF is physical')
|
||||
|
||||
class VLANTagInvalid(XendError):
|
||||
def __init__(self):
|
||||
XendError.__init__(self, 'VLAN tag invalid')
|
||||
|
||||
class VmError(XendError):
|
||||
"""Vm construction error."""
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/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) 2007 XenSource Ltd.
|
||||
#============================================================================
|
||||
#
|
||||
# A pseudo-StorageRepository to provide a representation for the images
|
||||
# that can be specified by xm.
|
||||
#
|
||||
|
||||
import commands
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import threading
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from xen.util import mkdir
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendVDI import *
|
||||
from xen.xend.XendTask import XendTask
|
||||
from xen.xend.XendStorageRepository import XendStorageRepository
|
||||
from xen.xend.XendStateStore import XendStateStore
|
||||
from xen.xend.XendOptions import instance as xendoptions
|
||||
|
||||
MB = 1024 * 1024
|
||||
|
||||
log = logging.getLogger("xend.XendLocalStorageRepo")
|
||||
|
||||
class XendLocalStorageRepo(XendStorageRepository):
|
||||
"""A backwards compatibility storage repository so that
|
||||
traditional file:/dir/file.img and phy:/dev/hdxx images can
|
||||
still be represented in terms of the Xen API.
|
||||
"""
|
||||
|
||||
def __init__(self, sr_uuid, sr_type = 'local',
|
||||
name_label = 'Local',
|
||||
name_description = 'Traditional Local Storage Repo'):
|
||||
"""
|
||||
@ivar images: mapping of all the images.
|
||||
@type images: dictionary by image uuid.
|
||||
@ivar lock: lock to provide thread safety.
|
||||
"""
|
||||
|
||||
XendStorageRepository.__init__(self, sr_uuid, sr_type,
|
||||
name_label, name_description,
|
||||
'/')
|
||||
|
||||
self.state = XendStateStore(xendoptions().get_xend_state_path()
|
||||
+ '/local_sr')
|
||||
|
||||
stored_images = self.state.load_state('vdi')
|
||||
if stored_images:
|
||||
for image_uuid, image in stored_images.items():
|
||||
self.images[image_uuid] = XendLocalVDI(image)
|
||||
|
||||
def create_vdi(self, vdi_struct):
|
||||
""" Creates a fake VDI image for a traditional image string.
|
||||
|
||||
The image uri is stored in the attribute 'uri'
|
||||
"""
|
||||
vdi_struct['uuid'] = uuid.createString()
|
||||
vdi_struct['SR'] = self.uuid
|
||||
new_image = XendLocalVDI(vdi_struct)
|
||||
self.images[new_image.uuid] = new_image
|
||||
self.save_state()
|
||||
return new_image.uuid
|
||||
|
||||
def save_state(self):
|
||||
vdi_records = dict([(k, v.get_record(transient = False))
|
||||
for k, v in self.images.items()])
|
||||
self.state.save_state('vdi', vdi_records)
|
||||
|
||||
def destroy_vdi(self, vdi_uuid):
|
||||
if vdi_uuid in self.images:
|
||||
del self.images[vdi_uuid]
|
||||
|
||||
self.save_state()
|
||||
|
||||
@@ -52,8 +52,8 @@ if 'TRACE' not in logging.__dict__:
|
||||
for frame in frames:
|
||||
filename = os.path.normcase(frame[1])
|
||||
if filename != thisfile and filename != logging._srcfile:
|
||||
major, minor, _, _, _ = sys.version_info
|
||||
if major == 2 and minor >= 4:
|
||||
major, minor, micro, _, _ = sys.version_info
|
||||
if (major, minor, micro) >= (2, 4, 2):
|
||||
return filename, frame[2], frame[3]
|
||||
else:
|
||||
return filename, frame[2]
|
||||
|
||||
@@ -0,0 +1,332 @@
|
||||
#============================================================================
|
||||
# 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) 2007 XenSource Ltd.
|
||||
#============================================================================
|
||||
|
||||
from xen.lowlevel.xc import xc
|
||||
import time
|
||||
import threading
|
||||
import os
|
||||
import re
|
||||
|
||||
"""Monitoring thread to keep track of Xend statistics. """
|
||||
|
||||
VBD_SYSFS_PATH = '/sys/devices/xen-backend/'
|
||||
VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_req'
|
||||
VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_req'
|
||||
VBD_DOMAIN_RE = r'vbd-(?P<domid>\d+)-(?P<devid>\d+)$'
|
||||
|
||||
NET_PROCFS_PATH = '/proc/net/dev'
|
||||
PROC_NET_DEV_RE = r'(?P<rx_bytes>\d+)\s+' \
|
||||
r'(?P<rx_packets>\d+)\s+' \
|
||||
r'(?P<rx_errs>\d+)\s+' \
|
||||
r'(?P<rx_drop>\d+)\s+' \
|
||||
r'(?P<rx_fifo>\d+)\s+' \
|
||||
r'(?P<rx_frame>\d+)\s+' \
|
||||
r'(?P<rx_compressed>\d+)\s+' \
|
||||
r'(?P<rx_multicast>\d+)\s+' \
|
||||
r'(?P<tx_bytes>\d+)\s+' \
|
||||
r'(?P<tx_packets>\d+)\s+' \
|
||||
r'(?P<tx_errs>\d+)\s+' \
|
||||
r'(?P<tx_drop>\d+)\s+' \
|
||||
r'(?P<tx_fifo>\d+)\s+' \
|
||||
r'(?P<tx_collisions>\d+)\s+' \
|
||||
r'(?P<tx_carrier>\d+)\s+' \
|
||||
r'(?P<tx_compressed>\d+)\s*$'
|
||||
|
||||
|
||||
VIF_DOMAIN_RE = re.compile(r'vif(?P<domid>\d+)\.(?P<iface>\d+):\s*' +
|
||||
PROC_NET_DEV_RE)
|
||||
PIF_RE = re.compile(r'^\s*(?P<iface>peth\d+):\s*' + PROC_NET_DEV_RE)
|
||||
|
||||
# The VBD transfer figures are in "requests" where we don't
|
||||
# really know how many bytes per requests. For now we make
|
||||
# up a number roughly could be.
|
||||
VBD_ROUGH_BYTES_PER_REQUEST = 1024 * 8 * 4
|
||||
|
||||
# Interval to poll xc, sysfs and proc
|
||||
POLL_INTERVAL = 2.0
|
||||
|
||||
class XendMonitor(threading.Thread):
|
||||
"""Monitors VCPU, VBD, VIF and PIF statistics for Xen API.
|
||||
|
||||
Polls sysfs and procfs for statistics on VBDs and VIFs respectively.
|
||||
|
||||
@ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain
|
||||
@type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}}
|
||||
@ivar domain_vifs_util: Bytes per second for VIFs indexed by domain
|
||||
@type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}}
|
||||
@ivar domain_vbds_util: Blocks per second for VBDs index by domain.
|
||||
@type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}}
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
threading.Thread.__init__(self)
|
||||
self.setDaemon(True)
|
||||
self.xc = xc()
|
||||
|
||||
self.lock = threading.Lock()
|
||||
|
||||
# tracks the last polled statistics
|
||||
self._domain_vcpus = {}
|
||||
self._domain_vifs = {}
|
||||
self._domain_vbds = {}
|
||||
self.pifs = {}
|
||||
|
||||
# instantaneous statistics
|
||||
self._domain_vcpus_util = {}
|
||||
self._domain_vifs_util = {}
|
||||
self._domain_vbds_util = {}
|
||||
self.pifs_util = {}
|
||||
|
||||
def get_domain_vcpus_util(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self._domain_vcpus_util
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def get_domain_vbds_util(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self._domain_vbds_util
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def get_domain_vifs_util(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self._domain_vifs_util
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def get_pifs_util(self):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self.pifs_util
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def _get_vif_stats(self):
|
||||
stats = {}
|
||||
|
||||
if not os.path.exists(NET_PROCFS_PATH):
|
||||
return stats
|
||||
|
||||
usage_at = time.time()
|
||||
for line in open(NET_PROCFS_PATH):
|
||||
is_vif = re.search(VIF_DOMAIN_RE, line.strip())
|
||||
if not is_vif:
|
||||
continue
|
||||
|
||||
domid = int(is_vif.group('domid'))
|
||||
vifid = int(is_vif.group('iface'))
|
||||
rx_bytes = int(is_vif.group('rx_bytes'))
|
||||
tx_bytes = int(is_vif.group('tx_bytes'))
|
||||
if not domid in stats:
|
||||
stats[domid] = {}
|
||||
|
||||
stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes)
|
||||
|
||||
return stats
|
||||
|
||||
def _get_pif_stats(self):
|
||||
stats = {}
|
||||
|
||||
if not os.path.exists(NET_PROCFS_PATH):
|
||||
return stats
|
||||
|
||||
usage_at = time.time()
|
||||
for line in open(NET_PROCFS_PATH):
|
||||
is_pif = re.search(PIF_RE, line.strip())
|
||||
if not is_pif:
|
||||
continue
|
||||
|
||||
pifname = is_pif.group('iface')
|
||||
rx_bytes = int(is_pif.group('rx_bytes'))
|
||||
tx_bytes = int(is_pif.group('tx_bytes'))
|
||||
stats[pifname] = (usage_at, rx_bytes, tx_bytes)
|
||||
|
||||
return stats
|
||||
|
||||
def _get_vbd_stats(self):
|
||||
stats = {}
|
||||
|
||||
if not os.path.exists(VBD_SYSFS_PATH):
|
||||
return stats
|
||||
|
||||
for vbd_path in os.listdir(VBD_SYSFS_PATH):
|
||||
is_vbd = re.search(VBD_DOMAIN_RE, vbd_path)
|
||||
if not is_vbd:
|
||||
continue
|
||||
|
||||
domid = int(is_vbd.group('domid'))
|
||||
vbdid = int(is_vbd.group('devid'))
|
||||
rd_stat_path = VBD_RD_PATH % vbd_path
|
||||
wr_stat_path = VBD_WR_PATH % vbd_path
|
||||
|
||||
if not os.path.exists(rd_stat_path) or \
|
||||
not os.path.exists(wr_stat_path):
|
||||
continue
|
||||
|
||||
|
||||
try:
|
||||
usage_at = time.time()
|
||||
rd_stat = int(open(rd_stat_path).readline().strip())
|
||||
wr_stat = int(open(wr_stat_path).readline().strip())
|
||||
rd_stat *= VBD_ROUGH_BYTES_PER_REQUEST
|
||||
wr_stat *= VBD_ROUGH_BYTES_PER_REQUEST
|
||||
|
||||
if domid not in stats:
|
||||
stats[domid] = {}
|
||||
|
||||
stats[domid][vbdid] = (usage_at, rd_stat, wr_stat)
|
||||
|
||||
except (IOError, ValueError):
|
||||
continue
|
||||
|
||||
return stats
|
||||
|
||||
def _get_cpu_stats(self):
|
||||
stats = {}
|
||||
for domain in self.xc.domain_getinfo():
|
||||
domid = domain['domid']
|
||||
vcpu_count = domain['online_vcpus']
|
||||
stats[domid] = {}
|
||||
for i in range(vcpu_count):
|
||||
vcpu_info = self.xc.vcpu_getinfo(domid, i)
|
||||
usage = vcpu_info['cpu_time']
|
||||
usage_at = time.time()
|
||||
stats[domid][i] = (usage_at, usage)
|
||||
|
||||
return stats
|
||||
|
||||
|
||||
def run(self):
|
||||
|
||||
# loop every second for stats
|
||||
while True:
|
||||
self.lock.acquire()
|
||||
try:
|
||||
active_domids = []
|
||||
# Calculate utilisation for VCPUs
|
||||
|
||||
for domid, cputimes in self._get_cpu_stats().items():
|
||||
active_domids.append(domid)
|
||||
if domid not in self._domain_vcpus:
|
||||
# if not initialised, save current stats
|
||||
# and skip utilisation calculation
|
||||
self._domain_vcpus[domid] = cputimes
|
||||
self._domain_vcpus_util[domid] = {}
|
||||
continue
|
||||
|
||||
for vcpu, (usage_at, usage) in cputimes.items():
|
||||
if vcpu not in self._domain_vcpus[domid]:
|
||||
continue
|
||||
|
||||
prv_usage_at, prv_usage = \
|
||||
self._domain_vcpus[domid][vcpu]
|
||||
interval_s = (usage_at - prv_usage_at) * 1000000000
|
||||
if interval_s > 0:
|
||||
util = (usage - prv_usage) / interval_s
|
||||
self._domain_vcpus_util[domid][vcpu] = util
|
||||
|
||||
self._domain_vcpus[domid] = cputimes
|
||||
|
||||
# Calculate utilisation for VBDs
|
||||
|
||||
for domid, vbds in self._get_vbd_stats().items():
|
||||
if domid not in self._domain_vbds:
|
||||
self._domain_vbds[domid] = vbds
|
||||
self._domain_vbds_util[domid] = {}
|
||||
continue
|
||||
|
||||
for devid, (usage_at, rd, wr) in vbds.items():
|
||||
if devid not in self._domain_vbds[domid]:
|
||||
continue
|
||||
|
||||
prv_at, prv_rd, prv_wr = \
|
||||
self._domain_vbds[domid][devid]
|
||||
interval = usage_at - prv_at
|
||||
rd_util = (rd - prv_rd)/interval
|
||||
wr_util = (wr - prv_wr)/interval
|
||||
self._domain_vbds_util[domid][devid] = \
|
||||
(rd_util, wr_util)
|
||||
|
||||
self._domain_vbds[domid] = vbds
|
||||
|
||||
|
||||
# Calculate utilisation for VIFs
|
||||
|
||||
for domid, vifs in self._get_vif_stats().items():
|
||||
|
||||
if domid not in self._domain_vifs:
|
||||
self._domain_vifs[domid] = vifs
|
||||
self._domain_vifs_util[domid] = {}
|
||||
continue
|
||||
|
||||
for devid, (usage_at, rx, tx) in vifs.items():
|
||||
if devid not in self._domain_vifs[domid]:
|
||||
continue
|
||||
|
||||
prv_at, prv_rx, prv_tx = \
|
||||
self._domain_vifs[domid][devid]
|
||||
interval = usage_at - prv_at
|
||||
rx_util = (rx - prv_rx)/interval
|
||||
tx_util = (tx - prv_tx)/interval
|
||||
|
||||
# note these are flipped around because
|
||||
# we are measuring the host interface,
|
||||
# not the guest interface
|
||||
self._domain_vifs_util[domid][devid] = \
|
||||
(tx_util, rx_util)
|
||||
|
||||
self._domain_vifs[domid] = vifs
|
||||
|
||||
# Calculate utilisation for PIFs
|
||||
|
||||
for pifname, stats in self._get_pif_stats().items():
|
||||
if pifname not in self.pifs:
|
||||
self.pifs[pifname] = stats
|
||||
continue
|
||||
|
||||
usage_at, rx, tx = stats
|
||||
prv_at, prv_rx, prv_tx = self.pifs[pifname]
|
||||
interval = usage_at - prv_at
|
||||
rx_util = (rx - prv_rx)/interval
|
||||
tx_util = (tx - prv_tx)/interval
|
||||
|
||||
self.pifs_util[pifname] = (rx_util, tx_util)
|
||||
self.pifs[pifname] = stats
|
||||
|
||||
for domid in self._domain_vcpus_util.keys():
|
||||
if domid not in active_domids:
|
||||
del self._domain_vcpus_util[domid]
|
||||
del self._domain_vcpus[domid]
|
||||
for domid in self._domain_vifs_util.keys():
|
||||
if domid not in active_domids:
|
||||
del self._domain_vifs_util[domid]
|
||||
del self._domain_vifs[domid]
|
||||
for domid in self._domain_vbds_util.keys():
|
||||
if domid not in active_domids:
|
||||
del self._domain_vbds_util[domid]
|
||||
del self._domain_vbds[domid]
|
||||
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
# Sleep a while before next poll
|
||||
time.sleep(POLL_INTERVAL)
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
# 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.
|
||||
# Copyright (c) 2006, 2007 Xensource Inc.
|
||||
#============================================================================
|
||||
|
||||
import os
|
||||
@@ -23,13 +23,16 @@ import xen.lowlevel.xc
|
||||
from xen.util import Brctl
|
||||
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError, NetworkAlreadyConnected
|
||||
from xen.xend.XendRoot import instance as xendroot
|
||||
from xen.xend.XendStorageRepository import XendStorageRepository
|
||||
from xen.xend.XendError import *
|
||||
from xen.xend.XendOptions import instance as xendoptions
|
||||
from xen.xend.XendQCoWStorageRepo import XendQCoWStorageRepo
|
||||
from xen.xend.XendLocalStorageRepo import XendLocalStorageRepo
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendPIF import *
|
||||
from xen.xend.XendPIFMetrics import XendPIFMetrics
|
||||
from xen.xend.XendNetwork import *
|
||||
from xen.xend.XendStateStore import XendStateStore
|
||||
from xen.xend.XendMonitor import XendMonitor
|
||||
|
||||
class XendNode:
|
||||
"""XendNode - Represents a Domain 0 Host."""
|
||||
@@ -37,15 +40,19 @@ class XendNode:
|
||||
def __init__(self):
|
||||
"""Initalises the state of all host specific objects such as
|
||||
|
||||
* Host
|
||||
* Host_CPU
|
||||
* host
|
||||
* host_CPU
|
||||
* host_metrics
|
||||
* PIF
|
||||
* Network
|
||||
* PIF_metrics
|
||||
* network
|
||||
* Storage Repository
|
||||
"""
|
||||
|
||||
self.xc = xen.lowlevel.xc.xc()
|
||||
self.state_store = XendStateStore(xendroot().get_xend_state_path())
|
||||
self.state_store = XendStateStore(xendoptions().get_xend_state_path())
|
||||
self.monitor = XendMonitor()
|
||||
self.monitor.start()
|
||||
|
||||
# load host state from XML file
|
||||
saved_host = self.state_store.load_state('host')
|
||||
@@ -54,12 +61,20 @@ class XendNode:
|
||||
host = saved_host[self.uuid]
|
||||
self.name = host.get('name_label', socket.gethostname())
|
||||
self.desc = host.get('name_description', '')
|
||||
self.host_metrics_uuid = host.get('metrics_uuid',
|
||||
uuid.createString())
|
||||
try:
|
||||
self.other_config = eval(host['other_config'])
|
||||
except:
|
||||
self.other_config = {}
|
||||
self.cpus = {}
|
||||
else:
|
||||
self.uuid = uuid.createString()
|
||||
self.name = socket.gethostname()
|
||||
self.desc = ''
|
||||
self.other_config = {}
|
||||
self.cpus = {}
|
||||
self.host_metrics_uuid = uuid.createString()
|
||||
|
||||
# load CPU UUIDs
|
||||
saved_cpus = self.state_store.load_state('cpu')
|
||||
@@ -84,8 +99,10 @@ class XendNode:
|
||||
self.cpus[cpu_uuid] = cpu_info
|
||||
|
||||
self.pifs = {}
|
||||
self.pif_metrics = {}
|
||||
self.networks = {}
|
||||
|
||||
self.srs = {}
|
||||
|
||||
# initialise networks
|
||||
saved_networks = self.state_store.load_state('network')
|
||||
if saved_networks:
|
||||
@@ -103,11 +120,25 @@ class XendNode:
|
||||
saved_pifs = self.state_store.load_state('pif')
|
||||
if saved_pifs:
|
||||
for pif_uuid, pif in saved_pifs.items():
|
||||
if pif['network'] in self.networks:
|
||||
if pif.get('network') in self.networks:
|
||||
network = self.networks[pif['network']]
|
||||
try:
|
||||
self.PIF_create(pif['name'], pif['MTU'], pif['VLAN'],
|
||||
pif['MAC'], network, False, pif_uuid)
|
||||
if 'device' not in pif and 'name' in pif:
|
||||
# Compatibility hack, can go pretty soon.
|
||||
pif['device'] = pif['name']
|
||||
if 'metrics' not in pif:
|
||||
# Compatibility hack, can go pretty soon.
|
||||
pif['metrics'] = uuid.createString()
|
||||
|
||||
try:
|
||||
pif['VLAN'] = int(pif.get('VLAN', -1))
|
||||
except (ValueError, TypeError):
|
||||
pif['VLAN'] = -1
|
||||
|
||||
self._PIF_create(pif['device'], pif['MTU'],
|
||||
pif['VLAN'],
|
||||
pif['MAC'], network, False, pif_uuid,
|
||||
pif['metrics'])
|
||||
except NetworkAlreadyConnected, exn:
|
||||
log.error('Cannot load saved PIF %s, as network %s ' +
|
||||
'is already connected to PIF %s',
|
||||
@@ -115,16 +146,26 @@ class XendNode:
|
||||
else:
|
||||
for name, mtu, mac in linux_get_phy_ifaces():
|
||||
network = self.networks.values()[0]
|
||||
self.PIF_create(name, mtu, '', mac, network, False)
|
||||
self._PIF_create(name, mtu, -1, mac, network, False)
|
||||
|
||||
# initialise storage
|
||||
saved_sr = self.state_store.load_state('sr')
|
||||
if saved_sr and len(saved_sr) == 1:
|
||||
sr_uuid = saved_sr.keys()[0]
|
||||
self.sr = XendStorageRepository(sr_uuid)
|
||||
else:
|
||||
sr_uuid = uuid.createString()
|
||||
self.sr = XendStorageRepository(sr_uuid)
|
||||
saved_srs = self.state_store.load_state('sr')
|
||||
if saved_srs:
|
||||
for sr_uuid, sr_cfg in saved_srs.items():
|
||||
if sr_cfg['type'] == 'qcow_file':
|
||||
self.srs[sr_uuid] = XendQCoWStorageRepo(sr_uuid)
|
||||
elif sr_cfg['type'] == 'local_image':
|
||||
self.srs[sr_uuid] = XendLocalStorageRepo(sr_uuid)
|
||||
|
||||
# Create missing SRs if they don't exist
|
||||
if not self.get_sr_by_type('local_image'):
|
||||
image_sr_uuid = uuid.createString()
|
||||
self.srs[image_sr_uuid] = XendLocalStorageRepo(image_sr_uuid)
|
||||
|
||||
if not self.get_sr_by_type('qcow_file'):
|
||||
qcow_sr_uuid = uuid.createString()
|
||||
self.srs[qcow_sr_uuid] = XendQCoWStorageRepo(qcow_sr_uuid)
|
||||
|
||||
|
||||
|
||||
def network_create(self, name_label, name_description,
|
||||
@@ -146,16 +187,24 @@ class XendNode:
|
||||
self.save_networks()
|
||||
|
||||
|
||||
def PIF_create(self, name, mtu, vlan, mac, network, persist = True,
|
||||
pif_uuid = None):
|
||||
def _PIF_create(self, name, mtu, vlan, mac, network, persist = True,
|
||||
pif_uuid = None, metrics_uuid = None):
|
||||
for pif in self.pifs.values():
|
||||
if pif.network == network:
|
||||
raise NetworkAlreadyConnected(pif.uuid)
|
||||
|
||||
if pif_uuid is None:
|
||||
pif_uuid = uuid.createString()
|
||||
self.pifs[pif_uuid] = XendPIF(pif_uuid, name, mtu, vlan, mac, network,
|
||||
self)
|
||||
if metrics_uuid is None:
|
||||
metrics_uuid = uuid.createString()
|
||||
|
||||
metrics = XendPIFMetrics(metrics_uuid)
|
||||
pif = XendPIF(pif_uuid, metrics, name, mtu, vlan, mac, network, self)
|
||||
metrics.set_PIF(pif)
|
||||
|
||||
self.pif_metrics[metrics_uuid] = metrics
|
||||
self.pifs[pif_uuid] = pif
|
||||
|
||||
if persist:
|
||||
self.save_PIFs()
|
||||
self.refreshBridges()
|
||||
@@ -163,12 +212,20 @@ class XendNode:
|
||||
|
||||
|
||||
def PIF_create_VLAN(self, pif_uuid, network_uuid, vlan):
|
||||
if vlan < 0 or vlan >= 4096:
|
||||
raise VLANTagInvalid()
|
||||
|
||||
pif = self.pifs[pif_uuid]
|
||||
network = self.networks[network_uuid]
|
||||
return self.PIF_create(pif.name, pif.mtu, vlan, pif.mac, network)
|
||||
return self._PIF_create(pif.device, pif.mtu, vlan, pif.mac, network)
|
||||
|
||||
|
||||
def PIF_destroy(self, pif_uuid):
|
||||
pif = self.pifs[pif_uuid]
|
||||
|
||||
if pif.vlan == -1:
|
||||
raise PIFIsPhysical()
|
||||
|
||||
del self.pifs[pif_uuid]
|
||||
self.save_PIFs()
|
||||
|
||||
@@ -176,17 +233,17 @@ class XendNode:
|
||||
def save(self):
|
||||
# save state
|
||||
host_record = {self.uuid: {'name_label':self.name,
|
||||
'name_description':self.desc}}
|
||||
'name_description':self.desc,
|
||||
'metrics_uuid': self.host_metrics_uuid,
|
||||
'other_config': repr(self.other_config)}}
|
||||
self.state_store.save_state('host',host_record)
|
||||
self.state_store.save_state('cpu', self.cpus)
|
||||
self.save_PIFs()
|
||||
self.save_networks()
|
||||
|
||||
sr_record = {self.sr.uuid: self.sr.get_record()}
|
||||
self.state_store.save_state('sr', sr_record)
|
||||
self.save_SRs()
|
||||
|
||||
def save_PIFs(self):
|
||||
pif_records = dict([(k, v.get_record(transient = False))
|
||||
pif_records = dict([(k, v.get_record())
|
||||
for k, v in self.pifs.items()])
|
||||
self.state_store.save_state('pif', pif_records)
|
||||
|
||||
@@ -195,6 +252,11 @@ class XendNode:
|
||||
for k, v in self.networks.items()])
|
||||
self.state_store.save_state('network', net_records)
|
||||
|
||||
def save_SRs(self):
|
||||
sr_records = dict([(k, v.get_record(transient = False))
|
||||
for k, v in self.srs.items()])
|
||||
self.state_store.save_state('sr', sr_records)
|
||||
|
||||
def shutdown(self):
|
||||
return 0
|
||||
|
||||
@@ -203,7 +265,6 @@ class XendNode:
|
||||
|
||||
def notify(self, _):
|
||||
return 0
|
||||
|
||||
|
||||
#
|
||||
# Ref validation
|
||||
@@ -218,12 +279,50 @@ class XendNode:
|
||||
def is_valid_network(self, network_ref):
|
||||
return (network_ref in self.networks)
|
||||
|
||||
def is_valid_sr(self, sr_ref):
|
||||
return (sr_ref in self.srs)
|
||||
|
||||
def is_valid_vdi(self, vdi_ref):
|
||||
for sr in self.srs.values():
|
||||
if sr.is_valid_vdi(vdi_ref):
|
||||
return True
|
||||
return False
|
||||
|
||||
#
|
||||
# Storage Repo
|
||||
# Storage Repositories
|
||||
#
|
||||
|
||||
def get_sr(self):
|
||||
return self.sr
|
||||
def get_sr(self, sr_uuid):
|
||||
return self.srs.get(sr_uuid)
|
||||
|
||||
def get_sr_by_type(self, sr_type):
|
||||
return [sr.uuid for sr in self.srs.values() if sr.type == sr_type]
|
||||
|
||||
def get_sr_by_name(self, name):
|
||||
return [sr.uuid for sr in self.srs.values() if sr.name_label == name]
|
||||
|
||||
def get_all_sr_uuid(self):
|
||||
return self.srs.keys()
|
||||
|
||||
def get_vdi_by_uuid(self, vdi_uuid):
|
||||
for sr in self.srs.values():
|
||||
if sr.is_valid_vdi(vdi_uuid):
|
||||
return sr.get_vdi_by_uuid(vdi_uuid)
|
||||
return None
|
||||
|
||||
def get_vdi_by_name_label(self, name):
|
||||
for sr in self.srs.values():
|
||||
vdi = sr.get_vdi_by_name_label(name)
|
||||
if vdi:
|
||||
return vdi
|
||||
return None
|
||||
|
||||
def get_sr_containing_vdi(self, vdi_uuid):
|
||||
for sr in self.srs.values():
|
||||
if sr.is_valid_vdi(vdi_uuid):
|
||||
return sr
|
||||
return None
|
||||
|
||||
|
||||
#
|
||||
# Host Functions
|
||||
@@ -285,8 +384,16 @@ class XendNode:
|
||||
raise XendError('Invalid CPU Reference')
|
||||
|
||||
def get_host_cpu_load(self, host_cpu_ref):
|
||||
return 0.0
|
||||
host_cpu = self.cpus.get(host_cpu_ref)
|
||||
if not host_cpu:
|
||||
return 0.0
|
||||
|
||||
vcpu = int(host_cpu['number'])
|
||||
cpu_loads = self.monitor.get_domain_vcpus_util()
|
||||
if 0 in cpu_loads and vcpu in cpu_loads[0]:
|
||||
return cpu_loads[0][vcpu]
|
||||
|
||||
return 0.0
|
||||
|
||||
#
|
||||
# Network Functions
|
||||
@@ -365,14 +472,24 @@ class XendNode:
|
||||
|
||||
return [[k, info[k]] for k in ITEM_ORDER]
|
||||
|
||||
def xenschedinfo(self):
|
||||
sched_id = self.xc.sched_id_get()
|
||||
if sched_id == xen.lowlevel.xc.XEN_SCHEDULER_SEDF:
|
||||
return 'sedf'
|
||||
elif sched_id == xen.lowlevel.xc.XEN_SCHEDULER_CREDIT:
|
||||
return 'credit'
|
||||
else:
|
||||
return 'unknown'
|
||||
|
||||
def xeninfo(self):
|
||||
info = self.xc.xeninfo()
|
||||
info['xen_scheduler'] = self.xenschedinfo()
|
||||
|
||||
ITEM_ORDER = ['xen_major',
|
||||
'xen_minor',
|
||||
'xen_extra',
|
||||
'xen_caps',
|
||||
'xen_scheduler',
|
||||
'xen_pagesize',
|
||||
'platform_params',
|
||||
'xen_changeset',
|
||||
@@ -387,6 +504,28 @@ class XendNode:
|
||||
def xendinfo(self):
|
||||
return [['xend_config_format', 3]]
|
||||
|
||||
#
|
||||
# utilisation tracking
|
||||
#
|
||||
|
||||
def get_vcpu_util(self, domid, vcpuid):
|
||||
cpu_loads = self.monitor.get_domain_vcpus_util()
|
||||
if domid in cpu_loads:
|
||||
return cpu_loads[domid].get(vcpuid, 0.0)
|
||||
return 0.0
|
||||
|
||||
def get_vif_util(self, domid, vifid):
|
||||
vif_loads = self.monitor.get_domain_vifs_util()
|
||||
if domid in vif_loads:
|
||||
return vif_loads[domid].get(vifid, (0.0, 0.0))
|
||||
return (0.0, 0.0)
|
||||
|
||||
def get_vbd_util(self, domid, vbdid):
|
||||
vbd_loads = self.monitor.get_domain_vbds_util()
|
||||
if domid in vbd_loads:
|
||||
return vbd_loads[domid].get(vbdid, (0.0, 0.0))
|
||||
return (0.0, 0.0)
|
||||
|
||||
# dictionary version of *info() functions to get rid of
|
||||
# SXPisms.
|
||||
def nodeinfo_dict(self):
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
Creates the servers and handles configuration.
|
||||
|
||||
Other classes get config variables by importing this module,
|
||||
using instance() to get a XendRoot instance, and then
|
||||
using instance() to get a XendOptions instance, and then
|
||||
the config functions (e.g. get_xend_port()) to get
|
||||
configured values.
|
||||
"""
|
||||
@@ -33,14 +33,11 @@ import sys
|
||||
from xen.xend import sxp, osdep, XendLogging
|
||||
from xen.xend.XendError import XendError
|
||||
|
||||
class XendRoot:
|
||||
"""Root of the management classes."""
|
||||
if os.uname()[0] == 'SunOS':
|
||||
from xen.lowlevel import scf
|
||||
|
||||
"""Default path to the config file."""
|
||||
config_default = "/etc/xen/xend-config.sxp"
|
||||
|
||||
"""Environment variable used to override config_default."""
|
||||
config_var = "XEND_CONFIG"
|
||||
class XendOptions:
|
||||
"""Configuration options."""
|
||||
|
||||
"""Where network control scripts live."""
|
||||
network_script_dir = osdep.scripts_dir
|
||||
@@ -63,6 +60,10 @@ class XendRoot:
|
||||
|
||||
xend_tcp_xmlrpc_server_default = 'no'
|
||||
|
||||
xend_tcp_xmlrpc_server_address_default = 'localhost'
|
||||
|
||||
xend_tcp_xmlrpc_server_port_default = 8006
|
||||
|
||||
xend_unix_xmlrpc_server_default = 'yes'
|
||||
|
||||
"""Default interface address xend listens at. """
|
||||
@@ -75,10 +76,10 @@ class XendRoot:
|
||||
xend_relocation_address_default = ''
|
||||
|
||||
"""Default port xend serves HTTP at. """
|
||||
xend_port_default = '8000'
|
||||
xend_port_default = 8000
|
||||
|
||||
"""Default port xend serves relocation at. """
|
||||
xend_relocation_port_default = '8002'
|
||||
xend_relocation_port_default = 8002
|
||||
|
||||
xend_relocation_hosts_allow_default = ''
|
||||
|
||||
@@ -92,9 +93,9 @@ class XendRoot:
|
||||
"""Default path the unix-domain server listens at."""
|
||||
xend_unix_path_default = '/var/lib/xend/xend-socket'
|
||||
|
||||
dom0_min_mem_default = '0'
|
||||
dom0_min_mem_default = 0
|
||||
|
||||
dom0_vcpus_default = '0'
|
||||
dom0_vcpus_default = 0
|
||||
|
||||
vncpasswd_default = None
|
||||
|
||||
@@ -107,16 +108,20 @@ class XendRoot:
|
||||
"""Default xend management state storage."""
|
||||
xend_state_path_default = '/var/lib/xend/state'
|
||||
|
||||
components = {}
|
||||
"""Default xend QCoW storage repository location."""
|
||||
xend_storage_path_default = '/var/lib/xend/storage'
|
||||
|
||||
"""Default type of backend network interfaces"""
|
||||
netback_type = osdep.netback_type
|
||||
|
||||
"""Default script to configure a backend network interface"""
|
||||
vif_script = osdep.vif_script
|
||||
|
||||
def __init__(self):
|
||||
self.config_path = None
|
||||
self.config = None
|
||||
self.configure()
|
||||
|
||||
|
||||
def _logError(self, fmt, *args):
|
||||
"""Logging function to log to stderr. We use this for XendRoot log
|
||||
"""Logging function to log to stderr. We use this for XendOptions log
|
||||
messages because they may be logged before the logger has been
|
||||
configured. Other components can safely use the logger.
|
||||
"""
|
||||
@@ -125,11 +130,155 @@ class XendRoot:
|
||||
|
||||
def configure(self):
|
||||
self.set_config()
|
||||
XendLogging.init(self.get_config_value("logfile",
|
||||
XendLogging.init(self.get_config_string("logfile",
|
||||
self.logfile_default),
|
||||
self.get_config_value("loglevel",
|
||||
self.get_config_string("loglevel",
|
||||
self.loglevel_default))
|
||||
|
||||
def set_config(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_config_bool(self, name, val=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_config_int(self, name, val=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_config_string(self, name, val=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_xen_api_server(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_xend_http_server(self):
|
||||
"""Get the flag indicating whether xend should run an http server.
|
||||
"""
|
||||
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)
|
||||
|
||||
def get_xend_tcp_xmlrpc_server_port(self):
|
||||
return self.get_config_int("xend-tcp-xmlrpc-server-port",
|
||||
self.xend_tcp_xmlrpc_server_port_default)
|
||||
|
||||
def get_xend_tcp_xmlrpc_server_address(self):
|
||||
return self.get_config_string("xend-tcp-xmlrpc-server-address",
|
||||
self.xend_tcp_xmlrpc_server_address_default)
|
||||
|
||||
def get_xend_unix_xmlrpc_server(self):
|
||||
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)
|
||||
|
||||
def get_xend_port(self):
|
||||
"""Get the port xend listens at for its HTTP interface.
|
||||
"""
|
||||
return self.get_config_int('xend-port', self.xend_port_default)
|
||||
|
||||
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)
|
||||
|
||||
def get_xend_relocation_hosts_allow(self):
|
||||
return self.get_config_string("xend-relocation-hosts-allow",
|
||||
self.xend_relocation_hosts_allow_default)
|
||||
|
||||
def get_xend_address(self):
|
||||
"""Get the address xend listens at for its HTTP port.
|
||||
This defaults to the empty string which allows all hosts to connect.
|
||||
If this is set to 'localhost' only the localhost will be able to connect
|
||||
to the HTTP port.
|
||||
"""
|
||||
return self.get_config_string('xend-address', self.xend_address_default)
|
||||
|
||||
def get_xend_relocation_address(self):
|
||||
"""Get the address xend listens at for its relocation server port.
|
||||
This defaults to the empty string which allows all hosts to connect.
|
||||
If this is set to 'localhost' only the localhost will be able to connect
|
||||
to the relocation port.
|
||||
"""
|
||||
return self.get_config_string('xend-relocation-address', self.xend_relocation_address_default)
|
||||
|
||||
def get_xend_unix_server(self):
|
||||
"""Get the flag indicating whether xend should run a unix-domain server.
|
||||
"""
|
||||
return self.get_config_bool("xend-unix-server", self.xend_unix_server_default)
|
||||
|
||||
def get_xend_unix_path(self):
|
||||
"""Get the path the xend unix-domain server listens at.
|
||||
"""
|
||||
return self.get_config_string("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_string("xend-domains-path", self.xend_domains_path_default)
|
||||
|
||||
def get_xend_state_path(self):
|
||||
""" Get the path for persistent domain configuration storage
|
||||
"""
|
||||
return self.get_config_string("xend-state-path", self.xend_state_path_default)
|
||||
|
||||
def get_xend_storage_path(self):
|
||||
""" Get the path for persistent domain configuration storage
|
||||
"""
|
||||
return self.get_config_string("xend-storage-path", self.xend_storage_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."""
|
||||
|
||||
s = self.get_config_string('network-script')
|
||||
|
||||
if s:
|
||||
result = s.split(" ")
|
||||
result[0] = os.path.join(self.network_script_dir, result[0])
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_external_migration_tool(self):
|
||||
"""@return the name of the tool to handle virtual TPM migration."""
|
||||
return self.get_config_string('external-migration-tool', self.external_migration_tool_default)
|
||||
|
||||
def get_enable_dump(self):
|
||||
return self.get_config_bool('enable-dump', 'no')
|
||||
|
||||
def get_vif_script(self):
|
||||
return self.get_config_string('vif-script', self.vif_script)
|
||||
|
||||
def get_dom0_min_mem(self):
|
||||
return self.get_config_int('dom0-min-mem', self.dom0_min_mem_default)
|
||||
|
||||
def get_dom0_vcpus(self):
|
||||
return self.get_config_int('dom0-cpus', self.dom0_vcpus_default)
|
||||
|
||||
def get_console_limit(self):
|
||||
return self.get_config_int('console-limit', 1024)
|
||||
|
||||
def get_vnclisten_address(self):
|
||||
return self.get_config_string('vnc-listen', self.xend_vnc_listen_default)
|
||||
|
||||
def get_vncpasswd_default(self):
|
||||
return self.get_config_string('vncpasswd',
|
||||
self.vncpasswd_default)
|
||||
|
||||
class XendOptionsFile(XendOptions):
|
||||
|
||||
"""Default path to the config file."""
|
||||
config_default = "/etc/xen/xend-config.sxp"
|
||||
|
||||
"""Environment variable used to override config_default."""
|
||||
config_var = "XEND_CONFIG"
|
||||
|
||||
def set_config(self):
|
||||
"""If the config file exists, read it. If not, ignore it.
|
||||
@@ -158,19 +307,6 @@ class XendRoot:
|
||||
self.config_path)
|
||||
self.config = ['xend-config']
|
||||
|
||||
def get_config(self, name=None):
|
||||
"""Get the configuration element with the given name, or
|
||||
the whole configuration if no name is given.
|
||||
|
||||
@param name: element name (optional)
|
||||
@return: config or none
|
||||
"""
|
||||
if name is None:
|
||||
val = self.config
|
||||
else:
|
||||
val = sxp.child(self.config, name)
|
||||
return val
|
||||
|
||||
def get_config_value(self, name, val=None):
|
||||
"""Get the value of an atomic configuration element.
|
||||
|
||||
@@ -195,128 +331,63 @@ class XendRoot:
|
||||
except Exception:
|
||||
raise XendError("invalid xend config %s: expected int: %s" % (name, v))
|
||||
|
||||
def get_config_string(self, name, val=None):
|
||||
return self.get_config_value(name, val)
|
||||
|
||||
def get_xen_api_server(self):
|
||||
"""Get the Xen-API server configuration.
|
||||
"""
|
||||
return self.get_config_value('xen-api-server',
|
||||
self.xen_api_server_default)
|
||||
|
||||
def get_xend_http_server(self):
|
||||
"""Get the flag indicating whether xend should run an http server.
|
||||
"""
|
||||
return self.get_config_bool("xend-http-server", self.xend_http_server_default)
|
||||
if os.uname()[0] == 'SunOS':
|
||||
class XendOptionsSMF(XendOptions):
|
||||
|
||||
def get_xend_tcp_xmlrpc_server(self):
|
||||
return self.get_config_bool("xend-tcp-xmlrpc-server",
|
||||
self.xend_tcp_xmlrpc_server_default)
|
||||
def set_config(self):
|
||||
pass
|
||||
|
||||
def get_xend_unix_xmlrpc_server(self):
|
||||
return self.get_config_bool("xend-unix-xmlrpc-server",
|
||||
self.xend_unix_xmlrpc_server_default)
|
||||
def get_config_bool(self, name, val=None):
|
||||
try:
|
||||
return scf.get_bool(name)
|
||||
except scf.error, e:
|
||||
if e[0] == scf.SCF_ERROR_NOT_FOUND:
|
||||
return val
|
||||
else:
|
||||
raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
|
||||
|
||||
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)
|
||||
def get_config_int(self, name, val=None):
|
||||
try:
|
||||
return scf.get_int(name)
|
||||
except scf.error, e:
|
||||
if e[0] == scf.SCF_ERROR_NOT_FOUND:
|
||||
return val
|
||||
else:
|
||||
raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
|
||||
|
||||
def get_xend_port(self):
|
||||
"""Get the port xend listens at for its HTTP interface.
|
||||
"""
|
||||
return self.get_config_int('xend-port', self.xend_port_default)
|
||||
def get_config_string(self, name, val=None):
|
||||
try:
|
||||
return scf.get_string(name)
|
||||
except scf.error, e:
|
||||
if e[0] == scf.SCF_ERROR_NOT_FOUND:
|
||||
return val
|
||||
else:
|
||||
raise XendError("option %s: %s:%s" % (name, e[1], e[2]))
|
||||
|
||||
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)
|
||||
|
||||
def get_xend_relocation_hosts_allow(self):
|
||||
return self.get_config_value("xend-relocation-hosts-allow",
|
||||
self.xend_relocation_hosts_allow_default)
|
||||
|
||||
def get_xend_address(self):
|
||||
"""Get the address xend listens at for its HTTP port.
|
||||
This defaults to the empty string which allows all hosts to connect.
|
||||
If this is set to 'localhost' only the localhost will be able to connect
|
||||
to the HTTP port.
|
||||
"""
|
||||
return self.get_config_value('xend-address', self.xend_address_default)
|
||||
|
||||
def get_xend_relocation_address(self):
|
||||
"""Get the address xend listens at for its relocation server port.
|
||||
This defaults to the empty string which allows all hosts to connect.
|
||||
If this is set to 'localhost' only the localhost will be able to connect
|
||||
to the relocation port.
|
||||
"""
|
||||
return self.get_config_value('xend-relocation-address', self.xend_relocation_address_default)
|
||||
|
||||
def get_xend_unix_server(self):
|
||||
"""Get the flag indicating whether xend should run a unix-domain server.
|
||||
"""
|
||||
return self.get_config_bool("xend-unix-server", self.xend_unix_server_default)
|
||||
|
||||
def get_xend_unix_path(self):
|
||||
"""Get the path the xend unix-domain server listens at.
|
||||
"""
|
||||
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_xend_state_path(self):
|
||||
""" Get the path for persistent domain configuration storage
|
||||
"""
|
||||
return self.get_config_value("xend-state-path", self.xend_state_path_default)
|
||||
|
||||
def get_network_script(self):
|
||||
"""@return the script used to alter the network configuration when
|
||||
Xend starts and stops, or None if no such script is specified."""
|
||||
|
||||
s = self.get_config_value('network-script')
|
||||
|
||||
if s:
|
||||
result = s.split(" ")
|
||||
result[0] = os.path.join(self.network_script_dir, result[0])
|
||||
return result
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_external_migration_tool(self):
|
||||
"""@return the name of the tool to handle virtual TPM migration."""
|
||||
return self.get_config_value('external-migration-tool', self.external_migration_tool_default)
|
||||
|
||||
def get_enable_dump(self):
|
||||
return self.get_config_bool('enable-dump', 'no')
|
||||
|
||||
def get_vif_script(self):
|
||||
return self.get_config_value('vif-script', 'vif-bridge')
|
||||
|
||||
def get_dom0_min_mem(self):
|
||||
return self.get_config_int('dom0-min-mem', self.dom0_min_mem_default)
|
||||
|
||||
def get_dom0_vcpus(self):
|
||||
return self.get_config_int('dom0-cpus', self.dom0_vcpus_default)
|
||||
|
||||
def get_console_limit(self):
|
||||
return self.get_config_int('console-limit', 1024)
|
||||
|
||||
def get_vnclisten_address(self):
|
||||
return self.get_config_value('vnc-listen', self.xend_vnc_listen_default)
|
||||
|
||||
def get_vncpasswd_default(self):
|
||||
return self.get_config_value('vncpasswd',
|
||||
self.vncpasswd_default)
|
||||
def get_xen_api_server(self):
|
||||
# When the new server is a supported configuration, we should
|
||||
# expand this.
|
||||
return [["unix"]]
|
||||
|
||||
def instance():
|
||||
"""Get an instance of XendRoot.
|
||||
"""Get an instance of XendOptions.
|
||||
Use this instead of the constructor.
|
||||
"""
|
||||
global inst
|
||||
try:
|
||||
inst
|
||||
except:
|
||||
inst = XendRoot()
|
||||
if os.uname()[0] == 'SunOS':
|
||||
inst = XendOptionsSMF()
|
||||
else:
|
||||
inst = XendOptionsFile()
|
||||
return inst
|
||||
@@ -90,18 +90,20 @@ def linux_set_mtu(iface, mtu):
|
||||
class XendPIF:
|
||||
"""Representation of a Physical Network Interface."""
|
||||
|
||||
def __init__(self, uuid, name, mtu, vlan, mac, network, host):
|
||||
def __init__(self, uuid, metrics, device, mtu, vlan, mac, network,
|
||||
host):
|
||||
self.uuid = uuid
|
||||
self.name = name
|
||||
self.metrics = metrics
|
||||
self.device = device
|
||||
self.mac = mac
|
||||
self.mtu = mtu
|
||||
self.vlan = vlan
|
||||
self.network = network
|
||||
self.host = host
|
||||
|
||||
def set_name(self, new_name):
|
||||
self.name = new_name
|
||||
|
||||
def set_device(self, new_device):
|
||||
self.device = new_device
|
||||
|
||||
def set_mac(self, new_mac):
|
||||
success = linux_set_mac(new_mac)
|
||||
if success:
|
||||
@@ -114,24 +116,15 @@ class XendPIF:
|
||||
self.mtu = new_mtu
|
||||
return success
|
||||
|
||||
def get_io_read_kbs(self):
|
||||
return 0.0
|
||||
|
||||
def get_io_write_kbs(self):
|
||||
return 0.0
|
||||
|
||||
def get_record(self, transient = True):
|
||||
result = {'name': self.name,
|
||||
'MAC': self.mac,
|
||||
'MTU': self.mtu,
|
||||
'VLAN': self.vlan,
|
||||
'host': self.host.uuid,
|
||||
'network': self.network.uuid}
|
||||
if transient:
|
||||
result['io_read_kbs'] = self.get_io_read_kbs()
|
||||
result['io_write_kbs'] = self.get_io_write_kbs()
|
||||
return result
|
||||
|
||||
def get_record(self):
|
||||
return {'uuid': self.uuid,
|
||||
'device': self.device,
|
||||
'MAC': self.mac,
|
||||
'MTU': self.mtu,
|
||||
'VLAN': self.vlan,
|
||||
'host': self.host.uuid,
|
||||
'network': self.network.uuid,
|
||||
'metrics': self.metrics.uuid}
|
||||
|
||||
def refresh(self, bridges):
|
||||
ifname = self.interface_name()
|
||||
@@ -141,13 +134,14 @@ class XendPIF:
|
||||
# there's nothing we can do -- this should have been set up with
|
||||
# the network script. Otherwise, we can use vconfig to derive
|
||||
# a subinterface.
|
||||
if not self.vlan:
|
||||
if self.vlan == -1:
|
||||
return
|
||||
|
||||
rc, _ = _cmd('vconfig add %s %s', self.name, self.vlan)
|
||||
rc, _ = _cmd('vconfig add %s %d', self.device, self.vlan)
|
||||
if rc != 0:
|
||||
log.error('Could not refresh %s', ifname)
|
||||
log.error('Could not refresh VLAN for interface %s', ifname)
|
||||
return
|
||||
|
||||
log.info('Created network interface %s', ifname)
|
||||
|
||||
for brname, nics in bridges.items():
|
||||
@@ -173,10 +167,10 @@ class XendPIF:
|
||||
|
||||
|
||||
def interface_name(self):
|
||||
if self.vlan:
|
||||
return '%s.%s' % (self.name, self.vlan)
|
||||
if self.vlan != -1:
|
||||
return '%s.%d' % (self.device, self.vlan)
|
||||
else:
|
||||
return self.name
|
||||
return self.device
|
||||
|
||||
|
||||
def _cmd(cmd, *args):
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
#============================================================================
|
||||
# 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-2007 Xensource Inc.
|
||||
#============================================================================
|
||||
|
||||
|
||||
class XendPIFMetrics:
|
||||
"""PIF Metrics."""
|
||||
|
||||
def __init__(self, uuid):
|
||||
self.uuid = uuid
|
||||
|
||||
def set_PIF(self, pif):
|
||||
self.pif = pif
|
||||
|
||||
def get_io_read_kbs(self):
|
||||
return self._get_stat(0)
|
||||
|
||||
def get_io_write_kbs(self):
|
||||
return self._get_stat(1)
|
||||
|
||||
def _get_stat(self, n):
|
||||
from xen.xend.XendNode import instance as xennode
|
||||
pifname = self.pif.device
|
||||
pifs_util = xennode().monitor.get_pifs_util()
|
||||
if pifname in pifs_util:
|
||||
return pifs_util[pifname][n]
|
||||
return 0.0
|
||||
|
||||
def get_record(self):
|
||||
return {'uuid' : self.uuid,
|
||||
'PIF' : self.pif.uuid,
|
||||
'io_read_kbs' : self.get_io_read_kbs(),
|
||||
'io_write_kbs' : self.get_io_write_kbs()
|
||||
}
|
||||
@@ -24,7 +24,7 @@ import types
|
||||
from encode import *
|
||||
from xen.xend import sxp
|
||||
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend import XendOptions
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
@@ -34,7 +34,7 @@ HTTP_ACCEPTED = 202
|
||||
HTTP_NO_CONTENT = 204
|
||||
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
|
||||
class XendError(RuntimeError):
|
||||
@@ -218,7 +218,7 @@ class UnixXendClientProtocol(HttpXendClientProtocol):
|
||||
|
||||
def __init__(self, path=None):
|
||||
if path is None:
|
||||
path = xroot.get_xend_unix_path()
|
||||
path = xoptions.get_xend_unix_path()
|
||||
self.path = path
|
||||
|
||||
def makeConnection(self, _):
|
||||
|
||||
@@ -0,0 +1,366 @@
|
||||
#!/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,2007 XenSource Ltd.
|
||||
#============================================================================
|
||||
#
|
||||
# The default QCOW Xen API Storage Repository
|
||||
#
|
||||
|
||||
import commands
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import threading
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from xen.util import mkdir
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendVDI import *
|
||||
from xen.xend.XendTask import XendTask
|
||||
from xen.xend.XendStorageRepository import XendStorageRepository
|
||||
from xen.xend.XendOptions import instance as xendoptions
|
||||
|
||||
XEND_STORAGE_NO_MAXIMUM = sys.maxint
|
||||
XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
|
||||
XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
|
||||
QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s"
|
||||
|
||||
MB = 1024 * 1024
|
||||
|
||||
log = logging.getLogger("xend.XendQCowStorageRepo")
|
||||
|
||||
|
||||
def qcow_virtual_size(qcow_file):
|
||||
"""Read the first 32 bytes of the QCoW header to determine its size.
|
||||
|
||||
See: http://www.gnome.org/~markmc/qcow-image-format.html.
|
||||
"""
|
||||
try:
|
||||
qcow_header = open(qcow_file, 'rb').read(32)
|
||||
parts = struct.unpack('>IIQIIQ', qcow_header)
|
||||
return parts[-1]
|
||||
except IOError:
|
||||
return -1
|
||||
|
||||
class XendQCoWStorageRepo(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, sr_uuid,
|
||||
sr_type = "qcow_file",
|
||||
name_label = "QCoW",
|
||||
name_description = "Xend QCoW Storage Repository",
|
||||
location = xendoptions().get_xend_storage_path(),
|
||||
storage_max = XEND_STORAGE_NO_MAXIMUM):
|
||||
"""
|
||||
@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.
|
||||
"""
|
||||
|
||||
XendStorageRepository.__init__(self, sr_uuid, sr_type, name_label,
|
||||
name_description, location,
|
||||
storage_max)
|
||||
self.storage_free = 0
|
||||
self._refresh()
|
||||
|
||||
def get_record(self, transient = True):
|
||||
retval = {'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'virtual_allocation': self.virtual_allocation,
|
||||
'physical_utilisation': self.physical_utilisation,
|
||||
'physical_size': self.physical_size,
|
||||
'type': self.type,
|
||||
'location': self.location,
|
||||
'VDIs': self.images.keys()}
|
||||
|
||||
if self.physical_size == XEND_STORAGE_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
retval['physical_size'] = stfs.f_blocks * stfs.f_frsize
|
||||
|
||||
return retval
|
||||
|
||||
def _refresh(self):
|
||||
"""Internal function that refreshes the state of the disk and
|
||||
updates the list of images available.
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
mkdir.parents(self.location, stat.S_IRWXU)
|
||||
|
||||
# scan the directory and populate self.images
|
||||
virtual_alloc = 0
|
||||
physical_used = 0
|
||||
seen_images = []
|
||||
for filename in os.listdir(self.location):
|
||||
if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
|
||||
image_uuid = filename[:-5]
|
||||
seen_images.append(image_uuid)
|
||||
|
||||
qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
|
||||
cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
|
||||
qcow_path = os.path.join(self.location, qcow_file)
|
||||
cfg_path = os.path.join(self.location, cfg_file)
|
||||
|
||||
phys_size = os.stat(qcow_path).st_size
|
||||
virt_size = qcow_virtual_size(qcow_path)
|
||||
|
||||
# add this image if we haven't seen it before
|
||||
if image_uuid not in self.images:
|
||||
vdi = XendQCoWVDI(image_uuid, self.uuid,
|
||||
qcow_path, cfg_path,
|
||||
virt_size, phys_size)
|
||||
|
||||
if cfg_path and os.path.exists(cfg_path):
|
||||
try:
|
||||
vdi.load_config(cfg_path)
|
||||
except:
|
||||
log.error('Corrupt VDI configuration file %s' %
|
||||
cfg_path)
|
||||
|
||||
self.images[image_uuid] = vdi
|
||||
|
||||
physical_used += phys_size
|
||||
virtual_alloc += virt_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]
|
||||
|
||||
self.virtual_allocation = virtual_alloc
|
||||
self.physical_utilisation = physical_used
|
||||
|
||||
# update free storage if we have to track that
|
||||
if self.physical_size == XEND_STORAGE_NO_MAXIMUM:
|
||||
self.storage_free = self._get_free_space()
|
||||
else:
|
||||
self.storage_free = self.physical_size - self.virtual_allocation
|
||||
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
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
|
||||
"""
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_bavail * stfs.f_frsize
|
||||
|
||||
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.physical_size != XEND_STORAGE_NO_MAXIMUM:
|
||||
return self.storage_free > size_bytes
|
||||
|
||||
bytes_free = self._get_free_space()
|
||||
if size_bytes < bytes_free:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_image_files(self, desired_size_bytes):
|
||||
"""Create an image and return its assigned UUID.
|
||||
|
||||
@param desired_size_bytes: Desired image size in bytes
|
||||
@type desired_size_bytes: 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(self.location,
|
||||
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_vdi(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:
|
||||
log.exception("Failed to destroy image")
|
||||
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.physical_size == XEND_STORAGE_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_blocks * stfs.f_frsize
|
||||
else:
|
||||
return self.physical_size
|
||||
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:
|
||||
return self.physical_utilisation
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def virtual_allocation(self):
|
||||
"""Returns the total virtual space allocated within the storage repo.
|
||||
@rtype: int
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self.virtual_allocation
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
|
||||
def create_vdi(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(self.location, cfg_filename)
|
||||
image.save_config(cfg_path)
|
||||
|
||||
except Exception, e:
|
||||
# cleanup before raising exception
|
||||
if image_uuid:
|
||||
self.destroy_vdi(image_uuid)
|
||||
|
||||
raise
|
||||
|
||||
return image_uuid
|
||||
|
||||
|
||||
# remove everything below this line!! for testing only
|
||||
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
|
||||
@@ -107,7 +107,7 @@ class XendStateStore:
|
||||
if child.nodeType != Node.ELEMENT_NODE:
|
||||
continue # skip non element nodes
|
||||
|
||||
uuid = child.getAttribute('uuid')
|
||||
uuid = child.getAttribute('uuid').encode('utf8')
|
||||
cls_dict = {}
|
||||
for val_elem in child.childNodes:
|
||||
if val_elem.nodeType != Node.ELEMENT_NODE:
|
||||
|
||||
@@ -13,65 +13,28 @@
|
||||
# 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.
|
||||
# Copyright (C) 2006, 2007 XenSource Ltd.
|
||||
#============================================================================
|
||||
#
|
||||
# The default QCOW Xen API Storage Repository
|
||||
# Abstract class for XendStorageRepositories
|
||||
#
|
||||
|
||||
import commands
|
||||
import logging
|
||||
import os
|
||||
import stat
|
||||
import threading
|
||||
import re
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from xen.util import mkdir
|
||||
from xen.xend import uuid
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendVDI import *
|
||||
|
||||
|
||||
XEND_STORAGE_NO_MAXIMUM = sys.maxint
|
||||
XEND_STORAGE_DIR = "/var/lib/xend/storage/"
|
||||
XEND_STORAGE_QCOW_FILENAME = "%s.qcow"
|
||||
XEND_STORAGE_VDICFG_FILENAME = "%s.vdi.xml"
|
||||
QCOW_CREATE_COMMAND = "/usr/sbin/qcow-create -r %d %s"
|
||||
|
||||
MB = 1024 * 1024
|
||||
|
||||
log = logging.getLogger("xend.XendStorageRepository")
|
||||
|
||||
|
||||
def qcow_virtual_size(qcow_file):
|
||||
"""Read the first 32 bytes of the QCoW header to determine its size.
|
||||
|
||||
See: http://www.gnome.org/~markmc/qcow-image-format.html.
|
||||
"""
|
||||
try:
|
||||
qcow_header = open(qcow_file, 'rb').read(32)
|
||||
parts = struct.unpack('>IIQIIQ', qcow_header)
|
||||
return parts[-1]
|
||||
except IOError:
|
||||
return -1
|
||||
|
||||
class XendStorageRepository:
|
||||
"""A simple file backed QCOW Storage Repository.
|
||||
""" Base class for Storage Repos. """
|
||||
|
||||
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, uuid,
|
||||
sr_type = "qcow_file",
|
||||
name_label = "Local",
|
||||
name_description = "Xend Storage Repository",
|
||||
location = XEND_STORAGE_DIR,
|
||||
sr_type = "unknown",
|
||||
name_label = 'Unknown',
|
||||
name_description = 'Not Implemented',
|
||||
location = '',
|
||||
storage_max = XEND_STORAGE_NO_MAXIMUM):
|
||||
"""
|
||||
@keyword storage_max: Maximum disk space to use in bytes.
|
||||
@@ -91,303 +54,51 @@ class XendStorageRepository:
|
||||
self.name_description = name_description
|
||||
self.images = {}
|
||||
|
||||
self.storage_max = storage_max
|
||||
self.storage_free = 0
|
||||
self.storage_used = 0
|
||||
self.storage_alloc = 0
|
||||
|
||||
self.physical_size = storage_max
|
||||
self.physical_utilisation = 0
|
||||
self.virtual_allocation = 0
|
||||
|
||||
self.lock = threading.RLock()
|
||||
self._refresh()
|
||||
|
||||
def get_record(self):
|
||||
def get_record(self, transient = True):
|
||||
retval = {'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'virtual_allocation': self.storage_alloc,
|
||||
'physical_utilisation': self.storage_used,
|
||||
'physical_size': self.storage_max,
|
||||
'virtual_allocation': self.virtual_allocation,
|
||||
'physical_utilisation': self.physical_utilisation,
|
||||
'physical_size': self.physical_size,
|
||||
'type': self.type,
|
||||
'location': self.location,
|
||||
'VDIs': self.images.keys()}
|
||||
|
||||
if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
retval['physical_size'] = stfs.f_blocks * stfs.f_frsize
|
||||
|
||||
return retval
|
||||
|
||||
def _refresh(self):
|
||||
"""Internal function that refreshes the state of the disk and
|
||||
updates the list of images available.
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
mkdir.parents(self.location, stat.S_IRWXU)
|
||||
|
||||
# scan the directory and populate self.images
|
||||
virtual_alloc = 0
|
||||
physical_used = 0
|
||||
seen_images = []
|
||||
for filename in os.listdir(self.location):
|
||||
if filename[-5:] == XEND_STORAGE_QCOW_FILENAME[-5:]:
|
||||
image_uuid = filename[:-5]
|
||||
seen_images.append(image_uuid)
|
||||
|
||||
qcow_file = XEND_STORAGE_QCOW_FILENAME % image_uuid
|
||||
cfg_file = XEND_STORAGE_VDICFG_FILENAME % image_uuid
|
||||
qcow_path = os.path.join(self.location, qcow_file)
|
||||
cfg_path = os.path.join(self.location, cfg_file)
|
||||
|
||||
phys_size = os.stat(qcow_path).st_size
|
||||
virt_size = qcow_virtual_size(qcow_path)
|
||||
|
||||
# add this image if we haven't seen it before
|
||||
if image_uuid not in self.images:
|
||||
vdi = XendQCOWVDI(image_uuid, self.uuid,
|
||||
qcow_path, cfg_path,
|
||||
virt_size, phys_size)
|
||||
|
||||
if cfg_path and os.path.exists(cfg_path):
|
||||
try:
|
||||
vdi.load_config(cfg_path)
|
||||
except:
|
||||
log.error('Corrupt VDI configuration file %s' %
|
||||
cfg_path)
|
||||
|
||||
self.images[image_uuid] = vdi
|
||||
|
||||
physical_used += phys_size
|
||||
virtual_alloc += virt_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]
|
||||
|
||||
self.storage_alloc = virtual_alloc
|
||||
self.storage_used = physical_used
|
||||
|
||||
# update free storage if we have to track that
|
||||
if self.storage_max == XEND_STORAGE_NO_MAXIMUM:
|
||||
self.storage_free = self._get_free_space()
|
||||
else:
|
||||
self.storage_free = self.storage_max - self.storage_alloc
|
||||
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
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
|
||||
"""
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_bavail * stfs.f_frsize
|
||||
|
||||
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 != XEND_STORAGE_NO_MAXIMUM:
|
||||
return self.storage_free > size_bytes
|
||||
|
||||
bytes_free = self._get_free_space()
|
||||
if size_bytes < bytes_free:
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_image_files(self, desired_size_bytes):
|
||||
"""Create an image and return its assigned UUID.
|
||||
|
||||
@param desired_size_bytes: Desired image size in bytes
|
||||
@type desired_size_bytes: 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(self.location,
|
||||
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:
|
||||
log.exception("Failed to destroy image")
|
||||
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_NO_MAXIMUM:
|
||||
stfs = os.statvfs(self.location)
|
||||
return stfs.f_blocks * stfs.f_frsize
|
||||
else:
|
||||
return self.storage_max
|
||||
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:
|
||||
return self.storage_used
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def virtual_allocation(self):
|
||||
"""Returns the total virtual space allocated within the storage repo.
|
||||
@rtype: int
|
||||
"""
|
||||
self.lock.acquire()
|
||||
try:
|
||||
return self.storage_alloc
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
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(self.location, 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_name_label(self, label):
|
||||
def get_vdi_by_uuid(self, image_uuid):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
for image_uuid, val in self.images.items():
|
||||
if val.name_label == label:
|
||||
return self.images.get(image_uuid)
|
||||
finally:
|
||||
self.lock.release()
|
||||
|
||||
def get_vdi_by_name_label(self, label):
|
||||
self.lock.acquire()
|
||||
try:
|
||||
for image_uuid, image in self.images.items():
|
||||
if image.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()
|
||||
|
||||
def get_vdis(self):
|
||||
return self.images.keys()
|
||||
|
||||
# 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)
|
||||
def create_vdi(self, vdi_struct):
|
||||
raise NotImplementedError()
|
||||
|
||||
print
|
||||
def destroy_vdi(self, vdi_struct):
|
||||
raise NotImplementedError()
|
||||
@@ -0,0 +1,228 @@
|
||||
#===========================================================================
|
||||
# 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) 2007 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
from xen.xend.XendAPIConstants import XEN_API_TASK_STATUS_TYPE
|
||||
from xen.xend.XendLogging import log
|
||||
import thread
|
||||
import threading
|
||||
|
||||
class XendTask(threading.Thread):
|
||||
"""Represents a Asynchronous Task used by Xen API.
|
||||
|
||||
Basically proxies the callable object in a thread and returns the
|
||||
results via self.{type,result,error_code,error_info}.
|
||||
|
||||
@cvar task_progress: Thread local storage for progress tracking.
|
||||
It is a dict indexed by thread_id. Note that the
|
||||
thread_id may be reused when the previous
|
||||
thread with the thread_id ends.
|
||||
|
||||
@cvar task_progress_lock: lock on thread access to task_progress
|
||||
|
||||
"""
|
||||
|
||||
# progress stack:
|
||||
# thread_id : [(start_task, end_task),
|
||||
# (start_sub_task, end_sub_task)..]
|
||||
# example : (0, 100), (50, 100) (50, 100) ...
|
||||
# That would mean that the task is 75% complete.
|
||||
# as it is 50% of the last 50% of the task.
|
||||
|
||||
task_progress = {}
|
||||
task_progress_lock = threading.Lock()
|
||||
|
||||
def __init__(self, uuid, func, args, func_name, return_type, label, desc,
|
||||
session):
|
||||
"""
|
||||
@param uuid: UUID of the task
|
||||
@type uuid: string
|
||||
@param func: Method to call (from XendAPI)
|
||||
@type func: callable object
|
||||
@param args: arguments to pass to function
|
||||
@type args: list or tuple
|
||||
@param label: name label of the task.
|
||||
@type label: string
|
||||
@param desc: name description of the task.
|
||||
@type desc: string
|
||||
@param func_name: function name, eg ('VM.start')
|
||||
@type desc: string
|
||||
"""
|
||||
|
||||
threading.Thread.__init__(self)
|
||||
self.status_lock = threading.Lock()
|
||||
self.status = XEN_API_TASK_STATUS_TYPE[0]
|
||||
|
||||
self.progress = 0
|
||||
self.type = return_type
|
||||
self.uuid = uuid
|
||||
|
||||
self.result = None
|
||||
self.error_code = ''
|
||||
self.error_info = []
|
||||
|
||||
self.name_label = label or func.__name__
|
||||
self.name_description = desc
|
||||
self.thread_id = 0
|
||||
|
||||
self.func_name = func_name
|
||||
self.func = func
|
||||
self.args = args
|
||||
|
||||
self.session = session
|
||||
|
||||
def set_status(self, new_status):
|
||||
self.status_lock.acquire()
|
||||
try:
|
||||
self.status = new_status
|
||||
finally:
|
||||
self.status_lock.release()
|
||||
|
||||
def get_status(self):
|
||||
self.status_lock.acquire()
|
||||
try:
|
||||
return self.status
|
||||
finally:
|
||||
self.status_lock.release()
|
||||
|
||||
def run(self):
|
||||
"""Runs the method and stores the result for later access.
|
||||
|
||||
Is invoked by threading.Thread.start().
|
||||
"""
|
||||
|
||||
self.thread_id = thread.get_ident()
|
||||
self.task_progress_lock.acquire()
|
||||
try:
|
||||
self.task_progress[self.thread_id] = {}
|
||||
self.progress = 0
|
||||
finally:
|
||||
self.task_progress_lock.release()
|
||||
|
||||
try:
|
||||
result = self.func(*self.args)
|
||||
if result['Status'] == 'Success':
|
||||
self.result = result['Value']
|
||||
self.set_status(XEN_API_TASK_STATUS_TYPE[1])
|
||||
else:
|
||||
self.error_code = result['ErrorDescription'][0]
|
||||
self.error_info = result['ErrorDescription'][1:]
|
||||
self.set_status(XEN_API_TASK_STATUS_TYPE[2])
|
||||
except Exception, e:
|
||||
log.exception('Error running Async Task')
|
||||
self.error_code = 'INTERNAL ERROR'
|
||||
self.error_info = [str(e)]
|
||||
self.set_status(XEN_API_TASK_STATUS_TYPE[2])
|
||||
|
||||
self.task_progress_lock.acquire()
|
||||
try:
|
||||
del self.task_progress[self.thread_id]
|
||||
self.progress = 100
|
||||
finally:
|
||||
self.task_progress_lock.release()
|
||||
|
||||
def get_record(self):
|
||||
"""Returns a Xen API compatible record."""
|
||||
return {
|
||||
'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'status': self.status,
|
||||
'progress': self.get_progress(),
|
||||
'type': self.type,
|
||||
'result': self.result,
|
||||
'error_code': self.error_code,
|
||||
'error_info': self.error_info,
|
||||
'allowed_operations': {},
|
||||
'session': self.session,
|
||||
}
|
||||
|
||||
def get_progress(self):
|
||||
""" Checks the thread local progress storage. """
|
||||
if self.status != XEN_API_TASK_STATUS_TYPE[0]:
|
||||
return 100
|
||||
|
||||
self.task_progress_lock.acquire()
|
||||
try:
|
||||
# Pop each progress range in the stack and map it on to
|
||||
# the next progress range until we find out cumulative
|
||||
# progress based on the (start, end) range of each level
|
||||
start = 0
|
||||
prog_stack = self.task_progress.get(self.thread_id, [])[:]
|
||||
if len(prog_stack) > 0:
|
||||
start, stop = prog_stack.pop()
|
||||
while prog_stack:
|
||||
new_start, new_stop = prog_stack.pop()
|
||||
start = new_start + ((new_stop - new_start)/100.0 * start)
|
||||
|
||||
# only update progress if it increases, this will prevent
|
||||
# progress from going backwards when tasks are popped off
|
||||
# the stack
|
||||
if start > self.progress:
|
||||
self.progress = int(start)
|
||||
finally:
|
||||
self.task_progress_lock.release()
|
||||
|
||||
return self.progress
|
||||
|
||||
|
||||
def log_progress(cls, progress_min, progress_max,
|
||||
func, *args, **kwds):
|
||||
""" Callable function wrapper that logs the progress of the
|
||||
function to thread local storage for task progress calculation.
|
||||
|
||||
This is a class method so other parts of Xend will update
|
||||
the task progress by calling:
|
||||
|
||||
XendTask.push_progress(progress_min, progress_max,
|
||||
func, *args, **kwds)
|
||||
|
||||
The results of the progress is stored in thread local storage
|
||||
and the result of the func(*args, **kwds) is returned back
|
||||
to the caller.
|
||||
|
||||
"""
|
||||
thread_id = thread.get_ident()
|
||||
retval = None
|
||||
|
||||
# Log the start of the method
|
||||
cls.task_progress_lock.acquire()
|
||||
try:
|
||||
if type(cls.task_progress.get(thread_id)) != list:
|
||||
cls.task_progress[thread_id] = []
|
||||
|
||||
cls.task_progress[thread_id].append((progress_min,
|
||||
progress_max))
|
||||
finally:
|
||||
cls.task_progress_lock.release()
|
||||
|
||||
# Execute the method
|
||||
retval = func(*args, **kwds)
|
||||
|
||||
# Log the end of the method by popping the progress range
|
||||
# off the stack.
|
||||
cls.task_progress_lock.acquire()
|
||||
try:
|
||||
cls.task_progress[thread_id].pop()
|
||||
finally:
|
||||
cls.task_progress_lock.release()
|
||||
|
||||
return retval
|
||||
|
||||
log_progress = classmethod(log_progress)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
#===========================================================================
|
||||
# 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) 2007 XenSource Ltd
|
||||
#============================================================================
|
||||
|
||||
"""
|
||||
Task Manager for Xen API asynchronous tasks.
|
||||
|
||||
Stores all tasks in a simple dictionary in module's own local storage to
|
||||
avoid the 'instance()' methods.
|
||||
|
||||
Tasks are indexed by UUID.
|
||||
|
||||
"""
|
||||
|
||||
from xen.xend.XendTask import XendTask
|
||||
from xen.xend import uuid
|
||||
import threading
|
||||
|
||||
tasks = {}
|
||||
tasks_lock = threading.Lock()
|
||||
|
||||
def create_task(func, args, func_name, return_type, label, session):
|
||||
"""Creates a new Task and registers it with the XendTaskManager.
|
||||
|
||||
@param func: callable object XMLRPC method
|
||||
@type func: callable object
|
||||
@param args: tuple or list of arguments
|
||||
@type args: tuple or list
|
||||
@param func_name: XMLRPC method name, so we can estimate the progress
|
||||
@type func_name: string
|
||||
|
||||
@return: Task UUID
|
||||
@rtype: string.
|
||||
"""
|
||||
task_uuid = uuid.createString()
|
||||
try:
|
||||
tasks_lock.acquire()
|
||||
task = XendTask(task_uuid, func, args, func_name, return_type, label,
|
||||
'', session)
|
||||
tasks[task_uuid] = task
|
||||
finally:
|
||||
tasks_lock.release()
|
||||
|
||||
task.start()
|
||||
|
||||
return task_uuid
|
||||
|
||||
def destroy_task(task_uuid):
|
||||
"""Destroys a task.
|
||||
|
||||
@param task_uuid: Task UUID
|
||||
@type task_uuid: string.
|
||||
"""
|
||||
try:
|
||||
tasks_lock.acquire()
|
||||
if task_uuid in tasks:
|
||||
del tasks[task_uuid]
|
||||
finally:
|
||||
tasks_lock.release()
|
||||
|
||||
def get_all_tasks():
|
||||
""" Returns all the UUID of tracked tasks, completed or pending.
|
||||
|
||||
@returns: list of UUIDs
|
||||
@rtype: list of strings
|
||||
"""
|
||||
try:
|
||||
tasks_lock.acquire()
|
||||
return tasks.keys()
|
||||
finally:
|
||||
tasks_lock.release()
|
||||
|
||||
def get_task(task_uuid):
|
||||
""" Retrieves a task by UUID.
|
||||
|
||||
@rtype: XendTask or None
|
||||
@return: Task denoted by UUID.
|
||||
"""
|
||||
try:
|
||||
tasks_lock.acquire()
|
||||
return tasks.get(task_uuid)
|
||||
finally:
|
||||
tasks_lock.release()
|
||||
|
||||
def get_tasks_by_name(task_name):
|
||||
""" Retrieves a task by UUID.
|
||||
|
||||
@rtype: XendTask or None
|
||||
@return: Task denoted by UUID.
|
||||
"""
|
||||
try:
|
||||
tasks_lock.acquire()
|
||||
return [t.uuid for t in tasks if t.name_label == name]
|
||||
finally:
|
||||
tasks_lock.release()
|
||||
|
||||
|
||||
@@ -57,8 +57,6 @@ class XendVDI(AutoSaveObject):
|
||||
'sector_size',
|
||||
'virtual_size',
|
||||
'physical_utilisation',
|
||||
'parent',
|
||||
'children',
|
||||
'sharable',
|
||||
'read_only']
|
||||
|
||||
@@ -72,11 +70,10 @@ class XendVDI(AutoSaveObject):
|
||||
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"
|
||||
self.location = ''
|
||||
|
||||
def load_config_dict(self, cfg):
|
||||
"""Loads configuration into the object from a dict.
|
||||
@@ -141,23 +138,24 @@ class XendVDI(AutoSaveObject):
|
||||
|
||||
return True
|
||||
|
||||
def get_record(self):
|
||||
def get_record(self, transient = True):
|
||||
return {'uuid': self.uuid,
|
||||
'name_label': self.name_label,
|
||||
'name_description': self.name_description,
|
||||
'virtual_size': self.virtual_size,
|
||||
'physical_utilisation': self.physical_utilisation,
|
||||
'sector_size': self.sector_size,
|
||||
'parent': None,
|
||||
'children': [],
|
||||
'sharable': False,
|
||||
'readonly': False,
|
||||
'SR': self.sr.get_uuid(),
|
||||
'SR': self.sr_uuid,
|
||||
'location': self.get_location(),
|
||||
'VBDs': []}
|
||||
|
||||
def get_location(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class XendQCOWVDI(XendVDI):
|
||||
|
||||
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
|
||||
@@ -167,4 +165,28 @@ class XendQCOWVDI(XendVDI):
|
||||
self.virtual_size = vsize
|
||||
self.sector_size = 512
|
||||
self.auto_save = True
|
||||
self.location = 'tap:qcow:%s' % self.qcow_path
|
||||
|
||||
def get_location(self):
|
||||
return self.location
|
||||
|
||||
class XendLocalVDI(XendVDI):
|
||||
def __init__(self, vdi_struct):
|
||||
vdi_uuid = vdi_struct['uuid']
|
||||
sr_uuid = vdi_struct['SR']
|
||||
XendVDI.__init__(self, vdi_uuid, sr_uuid)
|
||||
|
||||
self.auto_save = False
|
||||
self.cfg_path = None
|
||||
self.name_label = vdi_struct.get('name_label','')
|
||||
self.name_description = vdi_struct.get('name_description', '')
|
||||
self.physical_utilisation = 0
|
||||
self.virtual_size = 0
|
||||
self.sector_size = 0
|
||||
self.type = vdi_struct.get('type', '')
|
||||
self.sharable = vdi_struct.get('sharable', False)
|
||||
self.read_only = vdi_struct.get('read_only', False)
|
||||
self.location = vdi_struct.get('location', 'file:/dev/null')
|
||||
|
||||
def get_location(self):
|
||||
return self.location
|
||||
@@ -22,7 +22,7 @@ import time
|
||||
import xen.lowlevel.xc
|
||||
|
||||
import XendDomain
|
||||
import XendRoot
|
||||
import XendOptions
|
||||
from XendLogging import log
|
||||
from XendError import VmError
|
||||
|
||||
@@ -107,11 +107,11 @@ def free(need_mem):
|
||||
# usage, so we recheck the required alloc each time around the loop, but
|
||||
# track the last used value so that we don't trigger too many watches.
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
xc = xen.lowlevel.xc.xc()
|
||||
|
||||
try:
|
||||
dom0_min_mem = xroot.get_dom0_min_mem() * 1024
|
||||
dom0_min_mem = xoptions.get_dom0_min_mem() * 1024
|
||||
|
||||
retries = 0
|
||||
sleep_time = SLEEP_TIME_GROWTH
|
||||
|
||||
@@ -26,6 +26,7 @@ import xen.lowlevel.xc
|
||||
from xen.xend.XendConstants import REVERSE_DOMAIN_SHUTDOWN_REASONS
|
||||
from xen.xend.XendError import VmError, XendError
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendOptions import instance as xenopts
|
||||
from xen.xend.server.netif import randomMAC
|
||||
from xen.xend.xenstore.xswatch import xswatch
|
||||
from xen.xend import arch
|
||||
@@ -172,7 +173,7 @@ class ImageHandler:
|
||||
"""Build the domain. Define in subclass."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def createDeviceModel(self):
|
||||
def createDeviceModel(self, restore = False):
|
||||
"""Create device model for the domain (define in subclass if needed)."""
|
||||
pass
|
||||
|
||||
@@ -277,7 +278,7 @@ class HVMImageHandler(ImageHandler):
|
||||
raise VmError("HVM guest support is unavailable: is VT/AMD-V "
|
||||
"supported by your CPU and enabled in your BIOS?")
|
||||
|
||||
self.dmargs = self.parseDeviceModelArgs(imageConfig, deviceConfig)
|
||||
self.dmargs = self.parseDeviceModelArgs(vmConfig)
|
||||
self.device_model = imageConfig['hvm'].get('device_model')
|
||||
if not self.device_model:
|
||||
raise VmError("hvm: missing device model")
|
||||
@@ -292,8 +293,6 @@ class HVMImageHandler(ImageHandler):
|
||||
|
||||
self.pid = None
|
||||
|
||||
self.dmargs += self.configVNC(imageConfig)
|
||||
|
||||
self.pae = imageConfig['hvm'].get('pae', 0)
|
||||
self.apic = imageConfig['hvm'].get('apic', 0)
|
||||
self.acpi = imageConfig['hvm']['devices'].get('acpi', 0)
|
||||
@@ -327,13 +326,14 @@ class HVMImageHandler(ImageHandler):
|
||||
|
||||
# Return a list of cmd line args to the device models based on the
|
||||
# xm config file
|
||||
def parseDeviceModelArgs(self, imageConfig, deviceConfig):
|
||||
def parseDeviceModelArgs(self, vmConfig):
|
||||
dmargs = [ 'boot', 'fda', 'fdb', 'soundhw',
|
||||
'localtime', 'serial', 'stdvga', 'isa', 'vcpus',
|
||||
'localtime', 'serial', 'stdvga', 'isa',
|
||||
'acpi', 'usb', 'usbdevice', 'keymap' ]
|
||||
ret = []
|
||||
hvmDeviceConfig = imageConfig['hvm']['devices']
|
||||
|
||||
hvmDeviceConfig = vmConfig['image']['hvm']['devices']
|
||||
ret = ['-vcpus', str(self.vm.getVCpuCount())]
|
||||
|
||||
for a in dmargs:
|
||||
v = hvmDeviceConfig.get(a)
|
||||
|
||||
@@ -343,8 +343,11 @@ class HVMImageHandler(ImageHandler):
|
||||
|
||||
# Handle booleans gracefully
|
||||
if a in ['localtime', 'std-vga', 'isa', 'usb', 'acpi']:
|
||||
if v != None: v = int(v)
|
||||
if v: ret.append("-%s" % a)
|
||||
try:
|
||||
if v != None: v = int(v)
|
||||
if v: ret.append("-%s" % a)
|
||||
except (ValueError, TypeError):
|
||||
pass # if we can't convert it to a sane type, ignore it
|
||||
else:
|
||||
if v:
|
||||
ret.append("-%s" % a)
|
||||
@@ -361,76 +364,91 @@ class HVMImageHandler(ImageHandler):
|
||||
ret = ret + ["-domain-name", str(self.vm.info['name_label'])]
|
||||
nics = 0
|
||||
|
||||
for devuuid, (devtype, devinfo) in deviceConfig.items():
|
||||
if devtype == 'vbd':
|
||||
uname = devinfo.get('uname')
|
||||
if uname is not None and 'file:' in uname:
|
||||
(_, vbdparam) = string.split(uname, ':', 1)
|
||||
if not os.path.isfile(vbdparam):
|
||||
raise VmError('Disk image does not exist: %s' %
|
||||
vbdparam)
|
||||
if devtype == 'vif':
|
||||
dtype = devinfo.get('type', 'ioemu')
|
||||
if dtype != 'ioemu':
|
||||
continue
|
||||
nics += 1
|
||||
mac = devinfo.get('mac')
|
||||
if mac == None:
|
||||
mac = randomMAC()
|
||||
bridge = devinfo.get('bridge', 'xenbr0')
|
||||
model = devinfo.get('model', 'rtl8139')
|
||||
ret.append("-net")
|
||||
ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
|
||||
(nics, mac, model))
|
||||
ret.append("-net")
|
||||
ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
|
||||
return ret
|
||||
for devuuid in vmConfig['vbd_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
uname = devinfo.get('uname')
|
||||
if uname is not None and 'file:' in uname:
|
||||
(_, vbdparam) = string.split(uname, ':', 1)
|
||||
if not os.path.isfile(vbdparam):
|
||||
raise VmError('Disk image does not exist: %s' %
|
||||
vbdparam)
|
||||
|
||||
def configVNC(self, imageConfig):
|
||||
# Handle graphics library related options
|
||||
vnc = imageConfig.get('vnc')
|
||||
sdl = imageConfig.get('sdl')
|
||||
ret = []
|
||||
nographic = imageConfig.get('nographic')
|
||||
for devuuid in vmConfig['vif_refs']:
|
||||
devinfo = vmConfig['devices'][devuuid][1]
|
||||
dtype = devinfo.get('type', 'ioemu')
|
||||
if dtype != 'ioemu':
|
||||
continue
|
||||
nics += 1
|
||||
mac = devinfo.get('mac')
|
||||
if mac is None:
|
||||
mac = randomMAC()
|
||||
bridge = devinfo.get('bridge', 'xenbr0')
|
||||
model = devinfo.get('model', 'rtl8139')
|
||||
ret.append("-net")
|
||||
ret.append("nic,vlan=%d,macaddr=%s,model=%s" %
|
||||
(nics, mac, model))
|
||||
ret.append("-net")
|
||||
ret.append("tap,vlan=%d,bridge=%s" % (nics, bridge))
|
||||
|
||||
# get password from VM config (if password omitted, None)
|
||||
vncpasswd_vmconfig = imageConfig.get('vncpasswd')
|
||||
|
||||
if nographic:
|
||||
#
|
||||
# Find RFB console device, and if it exists, make QEMU enable
|
||||
# the VNC console.
|
||||
#
|
||||
if vmConfig['image'].get('nographic'):
|
||||
# skip vnc init if nographic is set
|
||||
ret.append('-nographic')
|
||||
return ret
|
||||
|
||||
if vnc:
|
||||
vncdisplay = imageConfig.get('vncdisplay',
|
||||
int(self.vm.getDomid()))
|
||||
vncunused = imageConfig.get('vncunused')
|
||||
vnc_config = {}
|
||||
has_vnc = int(vmConfig['image'].get('vnc', 0)) != 0
|
||||
has_sdl = int(vmConfig['image'].get('sdl', 0)) != 0
|
||||
for dev_uuid in vmConfig['console_refs']:
|
||||
dev_type, dev_info = vmConfig['devices'][dev_uuid]
|
||||
if dev_type == 'vfb':
|
||||
vnc_config = dev_info.get('other_config', {})
|
||||
has_vnc = True
|
||||
break
|
||||
|
||||
if vncunused:
|
||||
ret += ['-vncunused']
|
||||
if has_vnc:
|
||||
if not vnc_config:
|
||||
for key in ('vncunused', 'vnclisten', 'vncdisplay',
|
||||
'vncpasswd'):
|
||||
if key in vmConfig['image']:
|
||||
vnc_config[key] = vmConfig['image'][key]
|
||||
|
||||
if not vnc_config.get('vncunused', 0) and \
|
||||
vnc_config.get('vncdisplay', 0):
|
||||
ret.append('-vnc')
|
||||
ret.append(str(vncdisplay))
|
||||
else:
|
||||
ret += ['-vnc', '%d' % vncdisplay]
|
||||
ret.append('-vncunused')
|
||||
|
||||
vnclisten = imageConfig.get('vnclisten')
|
||||
vnclisten = vnc_config.get('vnclisten',
|
||||
xenopts().get_vnclisten_address())
|
||||
ret.append('-vnclisten')
|
||||
ret.append(str(vnclisten))
|
||||
|
||||
if not(vnclisten):
|
||||
vnclisten = (xen.xend.XendRoot.instance().
|
||||
get_vnclisten_address())
|
||||
if vnclisten:
|
||||
ret += ['-vnclisten', vnclisten]
|
||||
# Store vncpassword in xenstore
|
||||
vncpasswd = vnc_config.get('vncpasswd')
|
||||
if not vncpasswd:
|
||||
vncpasswd = xenopts().get_vncpasswd_default()
|
||||
|
||||
vncpasswd = vncpasswd_vmconfig
|
||||
if vncpasswd is None:
|
||||
vncpasswd = (xen.xend.XendRoot.instance().
|
||||
get_vncpasswd_default())
|
||||
if vncpasswd is None:
|
||||
raise VmError('vncpasswd is not set up in ' +
|
||||
'VMconfig and xend-config.')
|
||||
raise VmError('vncpasswd is not setup in vmconfig or '
|
||||
'xend-config.sxp')
|
||||
|
||||
if vncpasswd != '':
|
||||
self.vm.storeVm("vncpasswd", vncpasswd)
|
||||
self.vm.storeVm('vncpasswd', vncpasswd)
|
||||
elif has_sdl:
|
||||
# SDL is default in QEMU.
|
||||
pass
|
||||
else:
|
||||
ret.append('-nographic')
|
||||
|
||||
return ret
|
||||
|
||||
def createDeviceModel(self):
|
||||
def createDeviceModel(self, restore = False):
|
||||
if self.pid:
|
||||
return
|
||||
# Execute device model.
|
||||
@@ -439,6 +457,8 @@ class HVMImageHandler(ImageHandler):
|
||||
args = args + ([ "-d", "%d" % self.vm.getDomid(),
|
||||
"-m", "%s" % (self.getRequiredInitialReservation() / 1024)])
|
||||
args = args + self.dmargs
|
||||
if restore:
|
||||
args = args + ([ "-loadvm", "/tmp/xen.qemu-dm.%d" % self.vm.getDomid() ])
|
||||
env = dict(os.environ)
|
||||
if self.display:
|
||||
env['DISPLAY'] = self.display
|
||||
@@ -457,12 +477,16 @@ class HVMImageHandler(ImageHandler):
|
||||
self.register_reboot_feature_watch()
|
||||
self.pid = self.vm.gatherDom(('image/device-model-pid', int))
|
||||
|
||||
def destroy(self):
|
||||
def destroy(self, suspend = False):
|
||||
self.unregister_shutdown_watch()
|
||||
self.unregister_reboot_feature_watch();
|
||||
if self.pid:
|
||||
try:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
sig = signal.SIGKILL
|
||||
if suspend:
|
||||
log.info("use sigusr1 to signal qemu %d", self.pid)
|
||||
sig = signal.SIGUSR1
|
||||
os.kill(self.pid, sig)
|
||||
except OSError, exn:
|
||||
log.exception(exn)
|
||||
try:
|
||||
@@ -551,6 +575,9 @@ class IA64_HVM_ImageHandler(HVMImageHandler):
|
||||
extra_pages = 1024 + 3
|
||||
return mem_kb + extra_pages * page_kb
|
||||
|
||||
def getRequiredInitialReservation(self):
|
||||
return self.vm.getMemoryTarget()
|
||||
|
||||
def getRequiredShadowMemory(self, shadow_mem_kb, maxmem_kb):
|
||||
# Explicit shadow memory is not a concept
|
||||
return 0
|
||||
|
||||
@@ -29,8 +29,23 @@ _xend_autorestart = {
|
||||
"SunOS": False,
|
||||
}
|
||||
|
||||
_pygrub_path = {
|
||||
"SunOS": "/usr/lib/xen/bin/pygrub"
|
||||
}
|
||||
|
||||
_netback_type = {
|
||||
"SunOS": "SUNW_mac"
|
||||
}
|
||||
|
||||
_vif_script = {
|
||||
"SunOS": "vif-vnic"
|
||||
}
|
||||
|
||||
def _get(var, default=None):
|
||||
return var.get(os.uname()[0], default)
|
||||
|
||||
scripts_dir = _get(_scripts_dir, "/etc/xen/scripts")
|
||||
xend_autorestart = _get(_xend_autorestart)
|
||||
pygrub_path = _get(_pygrub_path, "/usr/bin/pygrub")
|
||||
netback_type = _get(_netback_type, "netfront")
|
||||
vif_script = _get(_vif_script, "vif-bridge")
|
||||
@@ -0,0 +1,35 @@
|
||||
from xen.xend.server.DevController import DevController
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
from xen.xend.XendError import VmError
|
||||
|
||||
class ConsoleController(DevController):
|
||||
"""A dummy controller for us to represent serial and vnc
|
||||
console devices with persistent UUIDs.
|
||||
"""
|
||||
|
||||
valid_cfg = ['location', 'uuid', 'protocol']
|
||||
|
||||
def __init__(self, vm):
|
||||
DevController.__init__(self, vm)
|
||||
self.hotplug = False
|
||||
|
||||
def getDeviceDetails(self, config):
|
||||
back = dict([(k, config[k]) for k in self.valid_cfg if k in config])
|
||||
return (self.allocateDeviceID(), back, {})
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
result = DevController.getDeviceConfiguration(self, devid)
|
||||
devinfo = self.readBackend(devid, *self.valid_cfg)
|
||||
config = dict(zip(self.valid_cfg, devinfo))
|
||||
config = dict([(key, val) for key, val in config.items()
|
||||
if val != None])
|
||||
return config
|
||||
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
return 0
|
||||
|
||||
def destroyDevice(self, devid, force):
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
from threading import Event
|
||||
import types
|
||||
|
||||
from xen.xend import sxp, XendRoot
|
||||
from xen.xend import sxp, XendOptions
|
||||
from xen.xend.XendError import VmError
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
@@ -50,7 +50,7 @@ xenbusState = {
|
||||
'Closed' : 6,
|
||||
}
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
xenbusState.update(dict(zip(xenbusState.values(), xenbusState.keys())))
|
||||
|
||||
@@ -75,7 +75,7 @@ class DevController:
|
||||
|
||||
def __init__(self, vm):
|
||||
self.vm = vm
|
||||
|
||||
self.hotplug = True
|
||||
|
||||
def createDevice(self, config):
|
||||
"""Trigger the creation of a device with the given configuration.
|
||||
@@ -151,6 +151,9 @@ class DevController:
|
||||
|
||||
def waitForDevice(self, devid):
|
||||
log.debug("Waiting for %s.", devid)
|
||||
|
||||
if not self.hotplug:
|
||||
return
|
||||
|
||||
status = self.waitForBackend(devid)
|
||||
|
||||
@@ -324,7 +327,7 @@ class DevController:
|
||||
Make sure that the migration has finished and only
|
||||
then return from the call.
|
||||
"""
|
||||
tool = xroot.get_external_migration_tool()
|
||||
tool = xoptions.get_external_migration_tool()
|
||||
if tool:
|
||||
log.info("Calling external migration tool for step %d" % step)
|
||||
fd = os.popen("%s -type %s -step %d -host %s -domname %s" %
|
||||
@@ -341,7 +344,7 @@ class DevController:
|
||||
""" Recover from device migration. The given step was the
|
||||
last one that was successfully executed.
|
||||
"""
|
||||
tool = xroot.get_external_migration_tool()
|
||||
tool = xoptions.get_external_migration_tool()
|
||||
if tool:
|
||||
log.info("Calling external migration tool")
|
||||
fd = os.popen("%s -type %s -step %d -host %s -domname %s -recover" %
|
||||
|
||||
@@ -203,7 +203,7 @@ class Daemon:
|
||||
|
||||
if not osdep.xend_autorestart:
|
||||
self.run(os.fdopen(w, 'w'))
|
||||
break
|
||||
os._exit(0)
|
||||
|
||||
pid = self.fork_pid()
|
||||
if pid:
|
||||
|
||||
@@ -25,7 +25,7 @@ class SrvRoot(SrvDir):
|
||||
"""Server sub-components. Each entry is (name, class), where
|
||||
'name' is the entry name and 'class' is the name of its class.
|
||||
"""
|
||||
#todo Get this list from the XendRoot config.
|
||||
#todo Get this list from the XendOptions config.
|
||||
subdirs = [
|
||||
('node', 'SrvNode' ),
|
||||
('domain', 'SrvDomainDir' ),
|
||||
|
||||
@@ -48,7 +48,7 @@ from threading import Thread
|
||||
|
||||
from xen.web.httpserver import HttpServer, UnixHttpServer
|
||||
|
||||
from xen.xend import XendNode, XendRoot, XendAPI
|
||||
from xen.xend import XendNode, XendOptions, XendAPI
|
||||
from xen.xend import Vifctl
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendClient import XEN_API_SOCKET
|
||||
@@ -57,7 +57,7 @@ from xen.web.SrvDir import SrvDir
|
||||
from SrvRoot import SrvRoot
|
||||
from XMLRPCServer import XMLRPCServer
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
|
||||
class XendServers:
|
||||
@@ -65,6 +65,7 @@ class XendServers:
|
||||
def __init__(self, root):
|
||||
self.servers = []
|
||||
self.root = root
|
||||
self.running = False
|
||||
self.cleaningUp = False
|
||||
self.reloadingConfig = False
|
||||
|
||||
@@ -79,6 +80,7 @@ class XendServers:
|
||||
server.shutdown()
|
||||
except:
|
||||
pass
|
||||
self.running = False
|
||||
|
||||
def reloadConfig(self, signum = 0, frame = None):
|
||||
log.debug("SrvServer.reloadConfig()")
|
||||
@@ -107,13 +109,12 @@ class XendServers:
|
||||
if server.ready:
|
||||
continue
|
||||
|
||||
thread = Thread(target=server.run, name=server.__class__.__name__)
|
||||
if isinstance(server, HttpServer):
|
||||
thread.setDaemon(True)
|
||||
thread = Thread(target=server.run,
|
||||
name=server.__class__.__name__)
|
||||
thread.setDaemon(True)
|
||||
thread.start()
|
||||
threads.append(thread)
|
||||
|
||||
|
||||
# check for when all threads have initialized themselves and then
|
||||
# close the status pipe
|
||||
|
||||
@@ -143,47 +144,32 @@ class XendServers:
|
||||
status.close()
|
||||
status = None
|
||||
|
||||
# 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 = threads
|
||||
while len(runningThreads) > 0:
|
||||
try:
|
||||
for t in threads:
|
||||
t.join(1.0)
|
||||
runningThreads = [t for t in threads
|
||||
if t.isAlive() and not t.isDaemon()]
|
||||
if self.cleaningUp and len(runningThreads) > 0:
|
||||
log.debug("Waiting for %s." %
|
||||
[x.getName() for x in runningThreads])
|
||||
except:
|
||||
pass
|
||||
|
||||
# loop to keep main thread alive until it receives a SIGTERM
|
||||
self.running = True
|
||||
while self.running:
|
||||
time.sleep(100000000)
|
||||
|
||||
if self.reloadingConfig:
|
||||
log.info("Restarting all XML-RPC and Xen-API servers...")
|
||||
self.cleaningUp = False
|
||||
self.reloadingConfig = False
|
||||
xroot.set_config()
|
||||
new_servers = [x for x in self.servers
|
||||
if isinstance(x, HttpServer)]
|
||||
self.servers = new_servers
|
||||
xoptions.set_config()
|
||||
self.servers = []
|
||||
_loadConfig(self, self.root, True)
|
||||
else:
|
||||
break
|
||||
|
||||
def _loadConfig(servers, root, reload):
|
||||
if not reload and xroot.get_xend_http_server():
|
||||
if xoptions.get_xend_http_server():
|
||||
servers.add(HttpServer(root,
|
||||
xroot.get_xend_address(),
|
||||
xroot.get_xend_port()))
|
||||
if not reload and xroot.get_xend_unix_server():
|
||||
path = xroot.get_xend_unix_path()
|
||||
xoptions.get_xend_address(),
|
||||
xoptions.get_xend_port()))
|
||||
if xoptions.get_xend_unix_server():
|
||||
path = xoptions.get_xend_unix_path()
|
||||
log.info('unix path=' + path)
|
||||
servers.add(UnixHttpServer(root, path))
|
||||
|
||||
api_cfg = xroot.get_xen_api_server()
|
||||
api_cfg = xoptions.get_xen_api_server()
|
||||
if api_cfg:
|
||||
try:
|
||||
addrs = [(str(x[0]).split(':'),
|
||||
@@ -213,15 +199,17 @@ def _loadConfig(servers, root, reload):
|
||||
servers.add(XMLRPCServer(auth, True, True, addr,
|
||||
int(port),
|
||||
hosts_allowed = allowed))
|
||||
except ValueError, exn:
|
||||
log.error('Xen-API server configuration %s is invalid.', api_cfg)
|
||||
except TypeError, exn:
|
||||
except (ValueError, TypeError), exn:
|
||||
log.exception('Xen API Server init failed')
|
||||
log.error('Xen-API server configuration %s is invalid.', api_cfg)
|
||||
|
||||
if xroot.get_xend_tcp_xmlrpc_server():
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, True))
|
||||
if xoptions.get_xend_tcp_xmlrpc_server():
|
||||
addr = xoptions.get_xend_tcp_xmlrpc_server_address()
|
||||
port = xoptions.get_xend_tcp_xmlrpc_server_port()
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False, use_tcp = True,
|
||||
host = addr, port = port))
|
||||
|
||||
if xroot.get_xend_unix_xmlrpc_server():
|
||||
if xoptions.get_xend_unix_xmlrpc_server():
|
||||
servers.add(XMLRPCServer(XendAPI.AUTH_PAM, False))
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ from xen.xend.XendError import XendInvalidDomain
|
||||
def fixup_sxpr(sexpr):
|
||||
ret = []
|
||||
for k in sexpr:
|
||||
if type(k) in (types.ListType, types.TupleType):
|
||||
if type(k) in (list, tuple):
|
||||
if len(k) != 2 or k[0] != 'vcpu_avail':
|
||||
ret.append(fixup_sxpr(k))
|
||||
else:
|
||||
@@ -139,6 +139,8 @@ class XMLRPCServer:
|
||||
meth = getattr(self.xenapi, meth_name)
|
||||
if callable(meth) and hasattr(meth, 'api'):
|
||||
self.server.register_function(meth, getattr(meth, 'api'))
|
||||
|
||||
self.server.register_instance(XendAPI.XendAPIAsyncProxy(self.xenapi))
|
||||
|
||||
# Legacy deprecated xm xmlrpc api
|
||||
# --------------------------------------------------------------------
|
||||
@@ -179,21 +181,32 @@ class XMLRPCServer:
|
||||
# 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()
|
||||
self.shutdown()
|
||||
|
||||
def cleanup(self):
|
||||
log.debug("XMLRPCServer.cleanup()")
|
||||
try:
|
||||
self.server.socket.close()
|
||||
except Exception, exn:
|
||||
log.exception(exn)
|
||||
pass
|
||||
log.debug('XMLRPCServer.cleanup()')
|
||||
if hasattr(self, 'server'):
|
||||
try:
|
||||
# This is here to make sure the socket is actually
|
||||
# cleaned up when close() is called. Otherwise
|
||||
# SO_REUSEADDR doesn't take effect. To replicate,
|
||||
# try 'xend reload' and look for EADDRINUSE.
|
||||
#
|
||||
# May be caued by us calling close() outside of
|
||||
# the listen()ing thread.
|
||||
self.server.socket.shutdown(2)
|
||||
except socket.error, e:
|
||||
pass # ignore any socket errors
|
||||
try:
|
||||
self.server.socket.close()
|
||||
except socket.error, e:
|
||||
pass
|
||||
|
||||
def shutdown(self):
|
||||
self.running = False
|
||||
self.ready = False
|
||||
|
||||
if self.ready:
|
||||
self.ready = False
|
||||
self.cleanup()
|
||||
@@ -101,6 +101,7 @@ class BlkifController(DevController):
|
||||
self.writeBackend(devid,
|
||||
'type', new_back['type'],
|
||||
'params', new_back['params'])
|
||||
return new_back.get('uuid')
|
||||
else:
|
||||
raise VmError('Refusing to reconfigure device %s:%d to %s' %
|
||||
(self.deviceClass, devid, config))
|
||||
|
||||
@@ -24,10 +24,10 @@ import os
|
||||
import random
|
||||
import re
|
||||
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend import XendOptions
|
||||
from xen.xend.server.DevController import DevController
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
def randomMAC():
|
||||
"""Generate a random MAC address.
|
||||
@@ -138,8 +138,8 @@ class NetifController(DevController):
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
|
||||
script = os.path.join(xroot.network_script_dir,
|
||||
config.get('script', xroot.get_vif_script()))
|
||||
script = os.path.join(xoptions.network_script_dir,
|
||||
config.get('script', xoptions.get_vif_script()))
|
||||
typ = config.get('type')
|
||||
bridge = config.get('bridge')
|
||||
mac = config.get('mac')
|
||||
@@ -147,19 +147,23 @@ class NetifController(DevController):
|
||||
rate = config.get('rate')
|
||||
uuid = config.get('uuid')
|
||||
ipaddr = config.get('ip')
|
||||
model = config.get('model')
|
||||
|
||||
devid = self.allocateDeviceID()
|
||||
|
||||
if not typ:
|
||||
typ = xoptions.netback_type
|
||||
|
||||
if not mac:
|
||||
mac = randomMAC()
|
||||
|
||||
back = { 'script' : script,
|
||||
'mac' : mac,
|
||||
'handle' : "%i" % devid }
|
||||
'handle' : "%i" % devid,
|
||||
'type' : typ }
|
||||
|
||||
if typ == 'ioemu':
|
||||
front = {}
|
||||
back['type'] = 'ioemu'
|
||||
else:
|
||||
front = { 'handle' : "%i" % devid,
|
||||
'mac' : mac }
|
||||
@@ -173,6 +177,8 @@ class NetifController(DevController):
|
||||
back['rate'] = parseRate(rate)
|
||||
if uuid:
|
||||
back['uuid'] = uuid
|
||||
if model:
|
||||
back['model'] = model
|
||||
|
||||
return (devid, back, front)
|
||||
|
||||
@@ -182,11 +188,12 @@ class NetifController(DevController):
|
||||
|
||||
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
|
||||
'mac', 'type', 'vifname', 'rate',
|
||||
'uuid', 'model')
|
||||
(script, ip, bridge, mac, typ, vifname, rate, uuid, model) = devinfo
|
||||
|
||||
if script:
|
||||
network_script_dir = xroot.network_script_dir + os.sep
|
||||
network_script_dir = xoptions.network_script_dir + os.sep
|
||||
result['script'] = script.replace(network_script_dir, "")
|
||||
if ip:
|
||||
result['ip'] = ip
|
||||
@@ -202,6 +209,8 @@ class NetifController(DevController):
|
||||
result['rate'] = formatRate(rate)
|
||||
if uuid:
|
||||
result['uuid'] = uuid
|
||||
|
||||
if model:
|
||||
result['model'] = model
|
||||
|
||||
return result
|
||||
|
||||
@@ -24,7 +24,7 @@ from xen.web import protocol, tcp, unix
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import XendDomain
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend import XendOptions
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendLogging import log
|
||||
|
||||
@@ -114,15 +114,15 @@ class RelocationProtocol(protocol.Protocol):
|
||||
|
||||
|
||||
def listenRelocation():
|
||||
xroot = XendRoot.instance()
|
||||
if xroot.get_xend_unix_server():
|
||||
xoptions = XendOptions.instance()
|
||||
if xoptions.get_xend_unix_server():
|
||||
path = '/var/lib/xend/relocation-socket'
|
||||
unix.UnixListener(path, RelocationProtocol)
|
||||
if xroot.get_xend_relocation_server():
|
||||
port = xroot.get_xend_relocation_port()
|
||||
interface = xroot.get_xend_relocation_address()
|
||||
if xoptions.get_xend_relocation_server():
|
||||
port = xoptions.get_xend_relocation_port()
|
||||
interface = xoptions.get_xend_relocation_address()
|
||||
|
||||
hosts_allow = xroot.get_xend_relocation_hosts_allow()
|
||||
hosts_allow = xoptions.get_xend_relocation_hosts_allow()
|
||||
if hosts_allow == '':
|
||||
hosts_allow = None
|
||||
else:
|
||||
|
||||
@@ -2,9 +2,9 @@ import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
import xen.xend.XendRoot
|
||||
import xen.xend.XendOptions
|
||||
|
||||
xen.xend.XendRoot.XendRoot.config_default = '/dev/null'
|
||||
xen.xend.XendOptions.XendOptions.config_default = '/dev/null'
|
||||
|
||||
from xen.xend.server import netif
|
||||
|
||||
@@ -13,7 +13,7 @@ FAKE_DOMID = 42
|
||||
FAKE_DEVID = 63
|
||||
|
||||
|
||||
xroot = xen.xend.XendRoot.instance()
|
||||
xoptions = xen.xend.XendOptions.instance()
|
||||
|
||||
|
||||
class test_controllers(unittest.TestCase):
|
||||
@@ -36,8 +36,8 @@ class test_controllers(unittest.TestCase):
|
||||
|
||||
self.assertEqual(backdets['handle'], str(FAKE_DEVID))
|
||||
self.assertEqual(backdets['script'],
|
||||
os.path.join(xroot.network_script_dir,
|
||||
xroot.get_vif_script()))
|
||||
os.path.join(xoptions.network_script_dir,
|
||||
xoptions.get_vif_script()))
|
||||
self.assertValidMac(backdets['mac'], expectedMac)
|
||||
|
||||
self.assertEqual(frontdets['handle'], str(FAKE_DEVID))
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
"""Support for virtual TPM interfaces."""
|
||||
|
||||
from xen.xend import XendRoot
|
||||
from xen.xend import XendOptions
|
||||
from xen.xend.XendLogging import log
|
||||
from xen.xend.XendError import XendError
|
||||
from xen.xend.XendConstants import DEV_MIGRATE_TEST, VTPM_DELETE_SCRIPT
|
||||
@@ -29,7 +29,7 @@ from xen.xend.server.DevController import DevController
|
||||
import os
|
||||
import re
|
||||
|
||||
xroot = XendRoot.instance()
|
||||
xoptions = XendOptions.instance()
|
||||
|
||||
def destroy_vtpmstate(name):
|
||||
if os.path.exists(VTPM_DELETE_SCRIPT):
|
||||
@@ -88,7 +88,7 @@ class TPMifController(DevController):
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
"""@see DevContoller.migrate"""
|
||||
if network:
|
||||
tool = xroot.get_external_migration_tool()
|
||||
tool = xoptions.get_external_migration_tool()
|
||||
if tool != '':
|
||||
log.info("Request to network-migrate device to %s. step=%d.",
|
||||
dst, step)
|
||||
@@ -116,7 +116,7 @@ class TPMifController(DevController):
|
||||
def recover_migrate(self, deviceConfig, network, dst, step, domName):
|
||||
"""@see DevContoller.recover_migrate"""
|
||||
if network:
|
||||
tool = xroot.get_external_migration_tool()
|
||||
tool = xoptions.get_external_migration_tool()
|
||||
if tool != '':
|
||||
log.info("Request to recover network-migrated device. last good step=%d.",
|
||||
step)
|
||||
|
||||
@@ -14,7 +14,8 @@ def spawn_detached(path, args, env):
|
||||
os.waitpid(p, 0)
|
||||
|
||||
CONFIG_ENTRIES = ['type', 'vncdisplay', 'vnclisten', 'vncpasswd', 'vncunused',
|
||||
'display', 'xauthority']
|
||||
'display', 'xauthority', 'keymap',
|
||||
'uuid', 'location', 'protocol']
|
||||
|
||||
class VfbifController(DevController):
|
||||
"""Virtual frame buffer controller. Handles all vfb devices for a domain.
|
||||
@@ -27,10 +28,11 @@ class VfbifController(DevController):
|
||||
def getDeviceDetails(self, config):
|
||||
"""@see DevController.getDeviceDetails"""
|
||||
|
||||
back = dict([(k, config[k]) for k in CONFIG_ENTRIES
|
||||
back = dict([(k, str(config[k])) for k in CONFIG_ENTRIES
|
||||
if config.has_key(k)])
|
||||
|
||||
return (0, back, {})
|
||||
devid = 0
|
||||
return (devid, back, {})
|
||||
|
||||
|
||||
def getDeviceConfiguration(self, devid):
|
||||
@@ -44,6 +46,10 @@ class VfbifController(DevController):
|
||||
|
||||
def createDevice(self, config):
|
||||
DevController.createDevice(self, config)
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# is HVM, so qemu-dm will handle the vfb.
|
||||
return
|
||||
|
||||
std_args = [ "--domid", "%d" % self.vm.getDomid(),
|
||||
"--title", self.vm.getName() ]
|
||||
t = config.get("type", None)
|
||||
@@ -52,7 +58,7 @@ class VfbifController(DevController):
|
||||
if config.has_key("vncpasswd"):
|
||||
passwd = config["vncpasswd"]
|
||||
else:
|
||||
passwd = xen.xend.XendRoot.instance().get_vncpasswd_default()
|
||||
passwd = xen.xend.XendOptions.instance().get_vncpasswd_default()
|
||||
if passwd:
|
||||
self.vm.storeVm("vncpasswd", passwd)
|
||||
log.debug("Stored a VNC password for vfb access")
|
||||
@@ -64,10 +70,12 @@ class VfbifController(DevController):
|
||||
if config.has_key("vncunused"):
|
||||
args += ["--unused"]
|
||||
elif config.has_key("vncdisplay"):
|
||||
args += ["--vncport", "%d" % (5900 + config["vncdisplay"])]
|
||||
args += ["--vncport", "%d" % (5900 + int(config["vncdisplay"]))]
|
||||
vnclisten = config.get("vnclisten",
|
||||
xen.xend.XendRoot.instance().get_vnclisten_address())
|
||||
xen.xend.XendOptions.instance().get_vnclisten_address())
|
||||
args += [ "--listen", vnclisten ]
|
||||
if config.has_key("keymap"):
|
||||
args += ["-k", "%s" % config["keymap"]]
|
||||
spawn_detached(args[0], args + std_args, os.environ)
|
||||
elif t == "sdl":
|
||||
args = [xen.util.auxbin.pathTo("xen-sdlfb")]
|
||||
@@ -80,6 +88,42 @@ class VfbifController(DevController):
|
||||
else:
|
||||
raise VmError('Unknown vfb type %s (%s)' % (t, repr(config)))
|
||||
|
||||
|
||||
def waitForDevice(self, devid):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
log.debug('skip waiting for HVM vfb')
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
DevController.waitForDevice(self, devid)
|
||||
|
||||
|
||||
def reconfigureDevice(self, _, config):
|
||||
""" Only allow appending location information of vnc port into
|
||||
xenstore."""
|
||||
|
||||
if 'location' in config:
|
||||
(devid, back, front) = self.getDeviceDetails(config)
|
||||
self.writeBackend(devid, 'location', config['location'])
|
||||
return back.get('uuid')
|
||||
|
||||
raise VmError('Refusing to reconfigure device vfb:%d' % devid)
|
||||
|
||||
def destroyDevice(self, devid, force):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# remove the backend xenstore entries for HVM guests no matter
|
||||
# what
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
else:
|
||||
DevController.destroyDevice(self, devid, force)
|
||||
|
||||
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
return 0
|
||||
return DevController.migrate(self, deviceConfig, network, dst, step,
|
||||
domName)
|
||||
|
||||
class VkbdifController(DevController):
|
||||
"""Virtual keyboard controller. Handles all vkbd devices for a domain.
|
||||
"""
|
||||
@@ -90,3 +134,24 @@ class VkbdifController(DevController):
|
||||
back = {}
|
||||
front = {}
|
||||
return (devid, back, front)
|
||||
|
||||
def waitForDevice(self, config):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# is a qemu-dm managed device, don't wait for hotplug for these.
|
||||
return
|
||||
|
||||
DevController.waitForDevice(self, config)
|
||||
|
||||
def destroyDevice(self, devid, force):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
# remove the backend xenstore entries for HVM guests no matter
|
||||
# what
|
||||
DevController.destroyDevice(self, devid, True)
|
||||
else:
|
||||
DevController.destroyDevice(self, devid, force)
|
||||
|
||||
def migrate(self, deviceConfig, network, dst, step, domName):
|
||||
if self.vm.info.get('HVM_boot_policy'):
|
||||
return 0
|
||||
return DevController.migrate(self, deviceConfig, network, dst, step,
|
||||
domName)
|
||||
@@ -0,0 +1,42 @@
|
||||
import unittest
|
||||
|
||||
import xen.xend.XendConfig as XendConfig
|
||||
|
||||
|
||||
class test_XendConfig(unittest.TestCase):
|
||||
|
||||
def testParseFromSXP(self):
|
||||
cfg = XendConfig.XendConfig(
|
||||
sxp_obj = (
|
||||
['vm',
|
||||
['bootloader_args', '-q --default_args="root=/dev/sda1 ro" --extra_args="quiet" /images/VM1.sda'],
|
||||
['bootloader', '/usr/bin/pygrub'],
|
||||
['device', ['vif', ['mac', '00:16:3E:4C:D1:00'], ['script', 'vif-bridge'], ['bridge', 'xenbr0']]],
|
||||
['device', ['vif', ['mac', '00:16:3E:48:56:26'], ['script', 'vif-bridge'], ['bridge', 'vbridge0']]],
|
||||
['device', ['vbd', ['uname', 'phy:/images/VM1.sda'], ['dev', 'sda'], ['mode', 'w']]],
|
||||
['device', ['vbd', ['uname', 'phy:/images/VM1.sdb'], ['dev', 'sdb'], ['mode', 'w']]],
|
||||
['memory', '256'], ['name', 'VM1'], ['on_crash', 'restart'],
|
||||
['uuid', '10927a76-fe27-49b2-8f57-2970b7bbed6c'], ['vcpus', '1']
|
||||
]))
|
||||
|
||||
self.assertEqual(cfg['uuid'], '10927a76-fe27-49b2-8f57-2970b7bbed6c')
|
||||
self.assertEqual(cfg['name_label'], 'VM1')
|
||||
self.assertEqual(cfg['memory_static_max'], 256)
|
||||
|
||||
ordered_refs = cfg.ordered_device_refs()
|
||||
self.assertEqual(cfg['devices'][ordered_refs[0]][0], 'vbd')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[1]][0], 'vbd')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[2]][0], 'vif')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[3]][0], 'vif')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[0]][1]['uname'],
|
||||
'phy:/images/VM1.sda')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[1]][1]['uname'],
|
||||
'phy:/images/VM1.sdb')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[2]][1]['mac'],
|
||||
'00:16:3E:4C:D1:00')
|
||||
self.assertEqual(cfg['devices'][ordered_refs[3]][1]['mac'],
|
||||
'00:16:3E:48:56:26')
|
||||
|
||||
|
||||
def test_suite():
|
||||
return unittest.makeSuite(test_XendConfig)
|
||||
@@ -24,3 +24,6 @@ def IntroduceDomain(domid, page, port):
|
||||
|
||||
def GetDomainPath(domid):
|
||||
return xshandle().get_domain_path(domid)
|
||||
|
||||
def ResumeDomain(domid):
|
||||
return xshandle().resume_domain(domid)
|
||||
@@ -24,10 +24,12 @@ import os.path
|
||||
import sys
|
||||
import socket
|
||||
import re
|
||||
import time
|
||||
import xmlrpclib
|
||||
|
||||
from xen.xend import sxp
|
||||
from xen.xend import PrettyPrint
|
||||
from xen.xend import osdep
|
||||
import xen.xend.XendClient
|
||||
from xen.xend.XendBootloader import bootloader
|
||||
from xen.util import blkif
|
||||
@@ -189,6 +191,10 @@ gopts.var('vcpus', val='VCPUS',
|
||||
fn=set_int, default=1,
|
||||
use="# of Virtual CPUS in domain.")
|
||||
|
||||
gopts.var('vcpu_avail', val='VCPUS',
|
||||
fn=set_long, default=None,
|
||||
use="Bitmask for virtual CPUs to make available immediately.")
|
||||
|
||||
gopts.var('cpu_cap', val='CAP',
|
||||
fn=set_int, default=None,
|
||||
use="""Set the maximum amount of cpu.
|
||||
@@ -291,7 +297,8 @@ gopts.var('vfb', val="type={vnc,sdl},vncunused=1,vncdisplay=N,vnclisten=ADDR,dis
|
||||
For type=vnc, connect an external vncviewer. The server will listen
|
||||
on ADDR (default 127.0.0.1) on port N+5900. N defaults to the
|
||||
domain id. If vncunused=1, the server will try to find an arbitrary
|
||||
unused port above 5900.
|
||||
unused port above 5900. vncpasswd overrides the XenD configured
|
||||
default password.
|
||||
For type=sdl, a viewer will be started automatically using the
|
||||
given DISPLAY and XAUTHORITY, which default to the current user's
|
||||
ones.""")
|
||||
@@ -300,7 +307,7 @@ gopts.var('vif', val="type=TYPE,mac=MAC,bridge=BRIDGE,ip=IPADDR,script=SCRIPT,ba
|
||||
fn=append_value, default=[],
|
||||
use="""Add a network interface with the given MAC address and bridge.
|
||||
The vif is configured by calling the given configuration script.
|
||||
If type is not specified, default is netfront not ioemu device.
|
||||
If type is not specified, default is netfront.
|
||||
If mac is not specified a random MAC address is used.
|
||||
If not specified then the network backend chooses it's own MAC address.
|
||||
If bridge is not specified the first bridge found is used.
|
||||
@@ -587,6 +594,9 @@ def configure_vfbs(config_devs, vals):
|
||||
'xauthority', 'type', 'vncpasswd' ]:
|
||||
err("configuration option %s unknown to vfbs" % k)
|
||||
config.append([k,v])
|
||||
if not d.has_key("keymap"):
|
||||
if vals.keymap:
|
||||
config.append(['keymap',vals.keymap])
|
||||
if not d.has_key("display") and os.environ.has_key("DISPLAY"):
|
||||
config.append(["display", os.environ['DISPLAY']])
|
||||
if not d.has_key("xauthority"):
|
||||
@@ -703,23 +713,6 @@ def configure_hvm(config_image, vals):
|
||||
config_image.append([a, vals.__dict__[a]])
|
||||
config_image.append(['vncpasswd', vals.vncpasswd])
|
||||
|
||||
def run_bootloader(vals, config_image):
|
||||
if not os.access(vals.bootloader, os.F_OK):
|
||||
err("Bootloader '%s' does not exist" % vals.bootloader)
|
||||
if not os.access(vals.bootloader, os.X_OK):
|
||||
err("Bootloader '%s' isn't executable" % vals.bootloader)
|
||||
if len(vals.disk) < 1:
|
||||
err("No disks configured and boot loader requested")
|
||||
(uname, dev, mode, backend) = vals.disk[0]
|
||||
file = blkif.blkdev_uname_to_file(uname)
|
||||
|
||||
if vals.bootentry:
|
||||
warn("The bootentry option is deprecated. Use bootargs and pass "
|
||||
"--entry= directly.")
|
||||
vals.bootargs = "--entry=%s" %(vals.bootentry,)
|
||||
|
||||
return bootloader(vals.bootloader, file, not vals.console_autoconnect,
|
||||
vals.bootargs, config_image)
|
||||
|
||||
def make_config(vals):
|
||||
"""Create the domain configuration.
|
||||
@@ -735,7 +728,7 @@ def make_config(vals):
|
||||
|
||||
map(add_conf, ['name', 'memory', 'maxmem', 'shadow_memory',
|
||||
'restart', 'on_poweroff',
|
||||
'on_reboot', 'on_crash', 'vcpus', 'features',
|
||||
'on_reboot', 'on_crash', 'vcpus', 'vcpu_avail', 'features',
|
||||
'on_xend_start', 'on_xend_stop'])
|
||||
|
||||
if vals.uuid is not None:
|
||||
@@ -759,10 +752,14 @@ def make_config(vals):
|
||||
|
||||
config_image = configure_image(vals)
|
||||
if vals.bootloader:
|
||||
config_image = run_bootloader(vals, config_image)
|
||||
if vals.bootloader == "pygrub":
|
||||
vals.bootloader = osdep.pygrub_path
|
||||
|
||||
config.append(['bootloader', vals.bootloader])
|
||||
if vals.bootargs:
|
||||
config.append(['bootloader_args', vals.bootargs])
|
||||
else:
|
||||
config.append(['bootloader_args', '-q'])
|
||||
config.append(['image', config_image])
|
||||
|
||||
config_devs = []
|
||||
@@ -823,7 +820,7 @@ def preprocess_ioports(vals):
|
||||
if len(d) == 1:
|
||||
d.append(d[0])
|
||||
# Components are in hex: add hex specifier.
|
||||
hexd = map(lambda v: '0x'+v, d)
|
||||
hexd = ['0x' + x for x in d]
|
||||
ioports.append(hexd)
|
||||
vals.ioports = ioports
|
||||
|
||||
@@ -990,8 +987,6 @@ def preprocess_vnc(vals):
|
||||
vals.extra = vnc + ' ' + vals.extra
|
||||
|
||||
def preprocess(vals):
|
||||
if not vals.kernel and not vals.bootloader:
|
||||
err("No kernel specified")
|
||||
preprocess_disk(vals)
|
||||
preprocess_pci(vals)
|
||||
preprocess_ioports(vals)
|
||||
@@ -1176,6 +1171,7 @@ def config_security_check(config, verbose):
|
||||
try:
|
||||
domain_label = security.ssidref2label(security.NULL_SSIDREF)
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc(limit=1)
|
||||
return 0
|
||||
domain_policy = 'NULL'
|
||||
@@ -1251,9 +1247,28 @@ def main(argv):
|
||||
if not create_security_check(config):
|
||||
raise security.ACMError('Security Configuration prevents domain from starting')
|
||||
else:
|
||||
dom = make_domain(opts, config)
|
||||
if opts.vals.console_autoconnect:
|
||||
console.execConsole(dom)
|
||||
|
||||
cpid = os.fork()
|
||||
if cpid != 0:
|
||||
for i in range(10):
|
||||
# Catch failure of the create process
|
||||
time.sleep(1)
|
||||
(p, rv) = os.waitpid(cpid, os.WNOHANG)
|
||||
if os.WIFEXITED(rv):
|
||||
if os.WEXITSTATUS(rv) != 0:
|
||||
sys.exit(os.WEXITSTATUS(rv))
|
||||
try:
|
||||
# Acquire the console of the created dom
|
||||
name = sxp.child_value(config, 'name', -1)
|
||||
dom = server.xend.domain(name)
|
||||
domid = int(sxp.child_value(dom, 'domid', '-1'))
|
||||
console.execConsole(domid)
|
||||
except:
|
||||
pass
|
||||
print("Could not start console\n");
|
||||
sys.exit(0)
|
||||
dom = make_domain(opts, config)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv)
|
||||
+55
-17
@@ -130,7 +130,7 @@ SUBCOMMAND_HELP = {
|
||||
'log' : ('', 'Print Xend log'),
|
||||
'rename' : ('<Domain> <NewDomainName>', 'Rename a domain.'),
|
||||
'sched-sedf' : ('<Domain> [options]', 'Get/set EDF parameters.'),
|
||||
'sched-credit': ('-d <Domain> [-w[=WEIGHT]|-c[=CAP]]',
|
||||
'sched-credit': ('[-d <Domain> [-w[=WEIGHT]|-c[=CAP]]]',
|
||||
'Get/set credit scheduler parameters.'),
|
||||
'sysrq' : ('<Domain> <letter>', 'Send a sysrq to a domain.'),
|
||||
'vcpu-list' : ('[<Domain>]',
|
||||
@@ -693,16 +693,28 @@ def parse_doms_info(info):
|
||||
up_time = time.time() - start_time
|
||||
|
||||
return {
|
||||
'domid' : get_info('domid', str, ''),
|
||||
'name' : get_info('name', str, '??'),
|
||||
'domid' : get_info('domid', str, ''),
|
||||
'name' : get_info('name', str, '??'),
|
||||
'mem' : get_info('memory_dynamic_min', int, 0),
|
||||
'vcpus' : get_info('online_vcpus', int, 0),
|
||||
'state' : get_info('state', str, ''),
|
||||
'cpu_time' : get_info('cpu_time', float, 0),
|
||||
'state' : get_info('state', str, ''),
|
||||
'cpu_time' : get_info('cpu_time', float, 0.0),
|
||||
# VCPUs is the number online when the VM is up, or the number
|
||||
# configured otherwise.
|
||||
'vcpus' : get_info('online_vcpus', int,
|
||||
get_info('vcpus', int, 0)),
|
||||
'up_time' : up_time,
|
||||
'seclabel' : security.get_security_printlabel(info),
|
||||
}
|
||||
|
||||
def check_sched_type(sched):
|
||||
current = 'unknown'
|
||||
for x in server.xend.node.info()[1:]:
|
||||
if len(x) > 1 and x[0] == 'xen_scheduler':
|
||||
current = x[1]
|
||||
break
|
||||
if sched != current:
|
||||
err("Xen is running with the %s scheduler" % current)
|
||||
sys.exit(1)
|
||||
|
||||
def parse_sedf_info(info):
|
||||
def get_info(n, t, d):
|
||||
@@ -717,6 +729,10 @@ def parse_sedf_info(info):
|
||||
'weight' : get_info('weight', int, -1),
|
||||
}
|
||||
|
||||
def domid_match(domid, info):
|
||||
return domid is None or domid == info['name'] or \
|
||||
domid == str(info['domid'])
|
||||
|
||||
def xm_brief_list(doms):
|
||||
print '%-40s %3s %5s %5s %10s %9s' % \
|
||||
('Name', 'ID', 'Mem', 'VCPUs', 'State', 'Time(s)')
|
||||
@@ -1091,9 +1107,7 @@ def xm_sched_sedf(args):
|
||||
print( ("%(name)-32s %(domid)3d %(period)9.1f %(slice)9.1f" +
|
||||
" %(latency)7.1f %(extratime)6d %(weight)6d") % info)
|
||||
|
||||
def domid_match(domid, info):
|
||||
return domid is None or domid == info['name'] or \
|
||||
domid == str(info['domid'])
|
||||
check_sched_type('sedf')
|
||||
|
||||
# we want to just display current info if no parameters are passed
|
||||
if len(args) == 0:
|
||||
@@ -1130,6 +1144,9 @@ def xm_sched_sedf(args):
|
||||
doms = filter(lambda x : domid_match(domid, x),
|
||||
[parse_doms_info(dom)
|
||||
for dom in getDomains(None, 'running')])
|
||||
if domid is not None and doms == []:
|
||||
err("Domain '%s' does not exist." % domid)
|
||||
usage('sched-sedf')
|
||||
|
||||
# print header if we aren't setting any parameters
|
||||
if len(opts.keys()) == 0:
|
||||
@@ -1167,6 +1184,8 @@ def xm_sched_sedf(args):
|
||||
def xm_sched_credit(args):
|
||||
"""Get/Set options for Credit Scheduler."""
|
||||
|
||||
check_sched_type('credit')
|
||||
|
||||
try:
|
||||
opts, params = getopt.getopt(args, "d:w:c:",
|
||||
["domain=", "weight=", "cap="])
|
||||
@@ -1174,27 +1193,46 @@ def xm_sched_credit(args):
|
||||
err(opterr)
|
||||
usage('sched-credit')
|
||||
|
||||
domain = None
|
||||
domid = None
|
||||
weight = None
|
||||
cap = None
|
||||
|
||||
for o, a in opts:
|
||||
if o == "-d":
|
||||
domain = a
|
||||
domid = a
|
||||
elif o == "-w":
|
||||
weight = int(a)
|
||||
elif o == "-c":
|
||||
cap = int(a);
|
||||
|
||||
if domain is None:
|
||||
# place holder for system-wide scheduler parameters
|
||||
err("No domain given.")
|
||||
usage('sched-credit')
|
||||
doms = filter(lambda x : domid_match(domid, x),
|
||||
[parse_doms_info(dom)
|
||||
for dom in getDomains(None, 'running')])
|
||||
|
||||
if weight is None and cap is None:
|
||||
print server.xend.domain.sched_credit_get(domain)
|
||||
if domid is not None and doms == []:
|
||||
err("Domain '%s' does not exist." % domid)
|
||||
usage('sched-credit')
|
||||
# print header if we aren't setting any parameters
|
||||
print '%-33s %-2s %-6s %-4s' % ('Name','ID','Weight','Cap')
|
||||
|
||||
for d in doms:
|
||||
try:
|
||||
info = server.xend.domain.sched_credit_get(d['domid'])
|
||||
except xmlrpclib.Fault:
|
||||
# domain does not support sched-credit?
|
||||
info = {'weight': -1, 'cap': -1}
|
||||
|
||||
info['name'] = d['name']
|
||||
info['domid'] = int(d['domid'])
|
||||
print( ("%(name)-32s %(domid)3d %(weight)6d %(cap)4d") % info)
|
||||
else:
|
||||
result = server.xend.domain.sched_credit_set(domain, weight, cap)
|
||||
if domid is None:
|
||||
# place holder for system-wide scheduler parameters
|
||||
err("No domain given.")
|
||||
usage('sched-credit')
|
||||
|
||||
result = server.xend.domain.sched_credit_set(domid, weight, cap)
|
||||
if result != 0:
|
||||
err(str(result))
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@
|
||||
# License along with this library; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
# ============================================================================
|
||||
# Copyright (c) 2006 XenSource Inc.
|
||||
# Copyright (c) 2006-2007 XenSource Inc.
|
||||
# ============================================================================
|
||||
#
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Xen-xm 3.0\n"
|
||||
"PO-Revision-Date: 2006-12-28 15:43+0000\n"
|
||||
"PO-Revision-Date: 2007-01-31 12:34+0000\n"
|
||||
"Last-Translator: Ewan Mellor <ewan@xensource.com>\n"
|
||||
"Language-Team: xen-devel <xen-devel@lists.xensource.com>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -41,15 +41,30 @@ msgstr "The method %(1)s takes %(2)s argument(s) (%(3)s given)."
|
||||
msgid "SESSION_AUTHENTICATION_FAILED"
|
||||
msgstr "Permission denied."
|
||||
|
||||
msgid "VALUE_NOT_SUPPORTED"
|
||||
msgstr "Value \"%(2)s\" for %(1)s is not supported by this server. The server said \"%(3)s\"."
|
||||
|
||||
msgid "HOST_CPU_HANDLE_INVALID"
|
||||
msgstr "The host_cpu handle %(1)s is invalid."
|
||||
|
||||
msgid "HOST_HANDLE_INVALID"
|
||||
msgstr "The host handle %(1)s is invalid."
|
||||
|
||||
msgid "HOST_METRICS_HANDLE_INVALID"
|
||||
msgstr "The host_metrics handle %(1)s is invalid."
|
||||
|
||||
msgid "PIF_HANDLE_INVALID"
|
||||
msgstr "The PIF handle %(1)s is invalid."
|
||||
|
||||
msgid "PIF_METRICS_HANDLE_INVALID"
|
||||
msgstr "The PIF_METRICS handle %(1)s is invalid."
|
||||
|
||||
msgid "SR_HANDLE_INVALID"
|
||||
msgstr "The SR handle %(1)s is invalid."
|
||||
|
||||
msgid "TASK_HANDLE_INVALID"
|
||||
msgstr "The task handle %(1)s is invalid."
|
||||
|
||||
msgid "VBD_HANDLE_INVALID"
|
||||
msgstr "The VBD handle %(1)s is invalid."
|
||||
|
||||
@@ -62,11 +77,23 @@ msgstr "The VIF handle %(1)s is invalid."
|
||||
msgid "VM_HANDLE_INVALID"
|
||||
msgstr "The VM handle %(1)s is invalid."
|
||||
|
||||
msgid "VM_METRICS_HANDLE_INVALID"
|
||||
msgstr "The VM_metrics handle %(1)s is invalid."
|
||||
|
||||
msgid "VTPM_HANDLE_INVALID"
|
||||
msgstr "The VTPM handle %(1)s is invalid."
|
||||
|
||||
msgid "OPERATION_NOT_ALLOWED"
|
||||
msgstr "You attempted an operation that was not allowed."
|
||||
|
||||
msgid "NETWORK_ALREADY_CONNECTED"
|
||||
msgstr "The network you specified already has a PIF attached to it, and so another one may not be attached."
|
||||
|
||||
msgid "PIF_IS_PHYSICAL"
|
||||
msgstr "The PIF %(1)s corresponds to a physical interface, and so may not be destroyed."
|
||||
|
||||
msgid "VLAN_TAG_INVALID"
|
||||
msgstr "The VLAN tag you gave (%(1)s) is invalid -- it must be between 0 and 4095."
|
||||
|
||||
msgid "VM_BAD_POWER_STATE"
|
||||
msgstr "The VM must be %(2)s to perform the requested operation (it is currently %(3)s)."
|
||||
@@ -52,6 +52,7 @@ def help():
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
opts.reset()
|
||||
args = opts.parse(argv)
|
||||
|
||||
if len(args) != 2:
|
||||
|
||||
+10
-10
@@ -250,7 +250,8 @@ class OptVar(Opt):
|
||||
class OptVals:
|
||||
"""Class to hold option values.
|
||||
"""
|
||||
pass
|
||||
def __init__(self):
|
||||
self.quiet = False
|
||||
|
||||
class Opts:
|
||||
"""Container for options.
|
||||
@@ -276,7 +277,6 @@ class Opts:
|
||||
self.argv = []
|
||||
# Option values.
|
||||
self.vals = OptVals()
|
||||
self.vals.quiet = 0
|
||||
# Variables for default scripts.
|
||||
self.vars = {}
|
||||
# Option to use for bare words.
|
||||
@@ -559,14 +559,6 @@ def set_bool(opt, k, v):
|
||||
else:
|
||||
opt.opts.err('Invalid value:' +v)
|
||||
|
||||
def set_u32(opt, k, v):
|
||||
"""Set an option to an u32 value."""
|
||||
try:
|
||||
v = u32(v)
|
||||
except:
|
||||
opt.opts.err('Invalid value: ' + str(v))
|
||||
opt.set(v)
|
||||
|
||||
def set_value(opt, k, v):
|
||||
"""Set an option to a value."""
|
||||
opt.set(v)
|
||||
@@ -579,6 +571,14 @@ def set_int(opt, k, v):
|
||||
opt.opts.err('Invalid value: ' + str(v))
|
||||
opt.set(v)
|
||||
|
||||
def set_long(opt, k, v):
|
||||
"""Set an option to a long integer value."""
|
||||
try:
|
||||
v = long(v)
|
||||
except:
|
||||
opt.opts.err('Invalid value: ' + str(v))
|
||||
opt.set(v)
|
||||
|
||||
def set_float(opt, k, v):
|
||||
"""Set an option to a float value."""
|
||||
try:
|
||||
|
||||
@@ -118,6 +118,7 @@ def main_dom(opts, args):
|
||||
|
||||
def main(argv):
|
||||
opts = gopts
|
||||
opts.reset()
|
||||
args = opts.parse(argv)
|
||||
if opts.vals.help:
|
||||
return
|
||||
|
||||
@@ -3,9 +3,9 @@ import os.path
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import xen.xend.XendRoot
|
||||
import xen.xend.XendOptions
|
||||
|
||||
xen.xend.XendRoot.XendRoot.config_default = '/dev/null'
|
||||
xen.xend.XendOptions.XendOptions.config_default = '/dev/null'
|
||||
|
||||
import xen.xm.create
|
||||
|
||||
|
||||