summaryrefslogtreecommitdiffstats
path: root/nova/virt
diff options
context:
space:
mode:
authorJustin Santa Barbara <justin@fathomdb.com>2011-03-29 19:09:42 -0700
committerJustin Santa Barbara <justin@fathomdb.com>2011-03-29 19:09:42 -0700
commit93b43cfcaeffa93b2f8ce50f473840c77be532c9 (patch)
treeab3c9f97697c157be300b58ee702e8f4ffab7fb4 /nova/virt
parent2315682856f420ff0b781bead142e1aff82071a4 (diff)
parente5f108058f9b085571330dff3c3e3e3e57d2e5ed (diff)
Merged with trunk
Diffstat (limited to 'nova/virt')
-rw-r--r--nova/virt/disk.py35
-rw-r--r--nova/virt/libvirt.xml.template31
-rw-r--r--nova/virt/libvirt_conn.py32
-rw-r--r--nova/virt/xenapi/fake.py172
-rw-r--r--nova/virt/xenapi/network_utils.py19
-rw-r--r--nova/virt/xenapi/vm_utils.py10
-rw-r--r--nova/virt/xenapi/vmops.py189
7 files changed, 322 insertions, 166 deletions
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index 25e4f54a9..ddea1a1f7 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -116,6 +116,41 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
_unlink_device(device, nbd)
+def setup_container(image, container_dir=None, nbd=False):
+ """Setup the LXC container.
+
+ It will mount the loopback image to the container directory in order
+ to create the root filesystem for the container.
+
+ LXC does not support qcow2 images yet.
+ """
+ try:
+ device = _link_device(image, nbd)
+ utils.execute('sudo', 'mount', device, container_dir)
+ except Exception, exn:
+ LOG.exception(_('Failed to mount filesystem: %s'), exn)
+ _unlink_device(device, nbd)
+
+
+def destroy_container(target, instance, nbd=False):
+ """Destroy the container once it terminates.
+
+ It will umount the container that is mounted, try to find the loopback
+ device associated with the container and delete it.
+
+ LXC does not support qcow2 images yet.
+ """
+ try:
+ container_dir = '%s/rootfs' % target
+ utils.execute('sudo', 'umount', container_dir)
+ finally:
+ out, err = utils.execute('sudo', 'losetup', '-a')
+ for loop in out.splitlines():
+ if instance['name'] in loop:
+ device = loop.split(loop, ':')
+ _unlink_device(device, nbd)
+
+
def _link_device(image, nbd):
"""Link image to device using loopback or nbd"""
if nbd:
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index d74a9e85b..36d18ed95 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -2,7 +2,12 @@
<name>${name}</name>
<memory>${memory_kb}</memory>
<os>
-#if $type == 'uml'
+#if $type == 'lxc'
+ #set $disk_prefix = ''
+ #set $disk_bus = ''
+ <type>exe</type>
+ <init>/sbin/init</init>
+#else if $type == 'uml'
#set $disk_prefix = 'ubd'
#set $disk_bus = 'uml'
<type>uml</type>
@@ -44,7 +49,13 @@
</features>
<vcpu>${vcpus}</vcpu>
<devices>
-#if $getVar('rescue', False)
+#if $type == 'lxc'
+ <filesystem type='mount'>
+ <source dir='${basepath}/rootfs'/>
+ <target dir='/'/>
+ </filesystem>
+#else
+ #if $getVar('rescue', False)
<disk type='file'>
<driver type='${driver_type}'/>
<source file='${basepath}/disk.rescue'/>
@@ -55,18 +66,19 @@
<source file='${basepath}/disk'/>
<target dev='${disk_prefix}b' bus='${disk_bus}'/>
</disk>
-#else
+ #else
<disk type='file'>
<driver type='${driver_type}'/>
<source file='${basepath}/disk'/>
<target dev='${disk_prefix}a' bus='${disk_bus}'/>
</disk>
- #if $getVar('local', False)
- <disk type='file'>
- <driver type='${driver_type}'/>
- <source file='${basepath}/disk.local'/>
- <target dev='${disk_prefix}b' bus='${disk_bus}'/>
- </disk>
+ #if $getVar('local', False)
+ <disk type='file'>
+ <driver type='${driver_type}'/>
+ <source file='${basepath}/disk.local'/>
+ <target dev='${disk_prefix}b' bus='${disk_bus}'/>
+ </disk>
+ #end if
#end if
#end if
@@ -87,7 +99,6 @@
</filterref>
</interface>
#end for
-
<!-- The order is significant here. File must be defined first -->
<serial type="file">
<source path='${basepath}/console.log'/>
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 80eb64f3c..c144e827e 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -20,7 +20,7 @@
"""
A connection to a hypervisor through libvirt.
-Supports KVM, QEMU, UML, and XEN.
+Supports KVM, LXC, QEMU, UML, and XEN.
**Related Flags**
@@ -86,7 +86,7 @@ flags.DEFINE_string('libvirt_xml_template',
flags.DEFINE_string('libvirt_type',
'kvm',
'Libvirt domain type (valid options are: '
- 'kvm, qemu, uml, xen)')
+ 'kvm, lxc, qemu, uml, xen)')
flags.DEFINE_string('libvirt_uri',
'',
'Override the default libvirt URI (which is dependent'
@@ -266,6 +266,8 @@ class LibvirtConnection(driver.ComputeDriver):
uri = FLAGS.libvirt_uri or 'uml:///system'
elif FLAGS.libvirt_type == 'xen':
uri = FLAGS.libvirt_uri or 'xen:///'
+ elif FLAGS.libvirt_type == 'lxc':
+ uri = FLAGS.libvirt_uri or 'lxc:///'
else:
uri = FLAGS.libvirt_uri or 'qemu:///system'
return uri
@@ -344,6 +346,8 @@ class LibvirtConnection(driver.ComputeDriver):
instance_name = instance['name']
LOG.info(_('instance %(instance_name)s: deleting instance files'
' %(target)s') % locals())
+ if FLAGS.libvirt_type == 'lxc':
+ disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images)
if os.path.exists(target):
shutil.rmtree(target)
@@ -625,6 +629,9 @@ class LibvirtConnection(driver.ComputeDriver):
instance['name'])
data = self._flush_xen_console(virsh_output)
fpath = self._append_to_file(data, console_log)
+ elif FLAGS.libvirt_type == 'lxc':
+ # LXC is also special
+ LOG.info(_("Unable to read LXC console"))
else:
fpath = console_log
@@ -738,6 +745,10 @@ class LibvirtConnection(driver.ComputeDriver):
f.write(libvirt_xml)
f.close()
+ if FLAGS.libvirt_type == 'lxc':
+ container_dir = '%s/rootfs' % basepath(suffix='')
+ utils.execute('mkdir', '-p', container_dir)
+
# NOTE(vish): No need add the suffix to console.log
os.close(os.open(basepath('console.log', ''),
os.O_CREAT | os.O_WRONLY, 0660))
@@ -797,12 +808,16 @@ class LibvirtConnection(driver.ComputeDriver):
if not inst['kernel_id']:
target_partition = "1"
+ if FLAGS.libvirt_type == 'lxc':
+ target_partition = None
+
key = str(inst['key_data'])
net = None
nets = []
ifc_template = open(FLAGS.injected_network_template).read()
ifc_num = -1
+ have_injected_networks = False
admin_context = context.get_admin_context()
for (network_ref, mapping) in network_info:
ifc_num += 1
@@ -810,6 +825,7 @@ class LibvirtConnection(driver.ComputeDriver):
if not 'injected' in network_ref:
continue
+ have_injected_networks = True
address = mapping['ips'][0]['ip']
address_v6 = None
if FLAGS.use_ipv6:
@@ -825,9 +841,10 @@ class LibvirtConnection(driver.ComputeDriver):
'netmask_v6': network_ref['netmask_v6']}
nets.append(net_info)
- net = str(Template(ifc_template,
- searchList=[{'interfaces': nets,
- 'use_ipv6': FLAGS.use_ipv6}]))
+ if have_injected_networks:
+ net = str(Template(ifc_template,
+ searchList=[{'interfaces': nets,
+ 'use_ipv6': FLAGS.use_ipv6}]))
if key or net:
inst_name = inst['name']
@@ -842,6 +859,11 @@ class LibvirtConnection(driver.ComputeDriver):
disk.inject_data(basepath('disk'), key, net,
partition=target_partition,
nbd=FLAGS.use_cow_images)
+
+ if FLAGS.libvirt_type == 'lxc':
+ disk.setup_container(basepath('disk'),
+ container_dir=container_dir,
+ nbd=FLAGS.use_cow_images)
except Exception as e:
# This could be a windows image, or a vmdk format disk
LOG.warn(_('instance %(inst_name)s: ignoring error injecting'
diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py
index 18d558058..4434dbf0b 100644
--- a/nova/virt/xenapi/fake.py
+++ b/nova/virt/xenapi/fake.py
@@ -60,8 +60,8 @@ from nova import exception
from nova import log as logging
-_CLASSES = ['host', 'network', 'session', 'SR', 'VBD',\
- 'PBD', 'VDI', 'VIF', 'VM', 'task']
+_CLASSES = ['host', 'network', 'session', 'SR', 'VBD',
+ 'PBD', 'VDI', 'VIF', 'PIF', 'VM', 'VLAN', 'task']
_db_content = {}
@@ -78,30 +78,36 @@ def reset():
for c in _CLASSES:
_db_content[c] = {}
create_host('fake')
- create_vm('fake', 'Running', is_a_template=False, is_control_domain=True)
+ create_vm('fake',
+ 'Running',
+ is_a_template=False,
+ is_control_domain=True)
+
+
+def reset_table(table):
+ if not table in _CLASSES:
+ return
+ _db_content[table] = {}
def create_host(name_label):
- return _create_object('host', {
- 'name_label': name_label,
- })
+ return _create_object('host',
+ {'name_label': name_label})
def create_network(name_label, bridge):
- return _create_object('network', {
- 'name_label': name_label,
- 'bridge': bridge,
- })
+ return _create_object('network',
+ {'name_label': name_label,
+ 'bridge': bridge})
def create_vm(name_label, status,
is_a_template=False, is_control_domain=False):
- return _create_object('VM', {
- 'name_label': name_label,
- 'power-state': status,
- 'is_a_template': is_a_template,
- 'is_control_domain': is_control_domain,
- })
+ return _create_object('VM',
+ {'name_label': name_label,
+ 'power-state': status,
+ 'is_a_template': is_a_template,
+ 'is_control_domain': is_control_domain})
def destroy_vm(vm_ref):
@@ -123,27 +129,24 @@ def destroy_vdi(vdi_ref):
def create_vdi(name_label, read_only, sr_ref, sharable):
- return _create_object('VDI', {
- 'name_label': name_label,
- 'read_only': read_only,
- 'SR': sr_ref,
- 'type': '',
- 'name_description': '',
- 'sharable': sharable,
- 'other_config': {},
- 'location': '',
- 'xenstore_data': '',
- 'sm_config': {},
- 'VBDs': {},
- })
+ return _create_object('VDI',
+ {'name_label': name_label,
+ 'read_only': read_only,
+ 'SR': sr_ref,
+ 'type': '',
+ 'name_description': '',
+ 'sharable': sharable,
+ 'other_config': {},
+ 'location': '',
+ 'xenstore_data': '',
+ 'sm_config': {},
+ 'VBDs': {}})
def create_vbd(vm_ref, vdi_ref):
- vbd_rec = {
- 'VM': vm_ref,
- 'VDI': vdi_ref,
- 'currently_attached': False,
- }
+ vbd_rec = {'VM': vm_ref,
+ 'VDI': vdi_ref,
+ 'currently_attached': False}
vbd_ref = _create_object('VBD', vbd_rec)
after_VBD_create(vbd_ref, vbd_rec)
return vbd_ref
@@ -169,19 +172,24 @@ def after_VM_create(vm_ref, vm_rec):
def create_pbd(config, host_ref, sr_ref, attached):
- return _create_object('PBD', {
- 'device-config': config,
- 'host': host_ref,
- 'SR': sr_ref,
- 'currently-attached': attached,
- })
+ return _create_object('PBD',
+ {'device-config': config,
+ 'host': host_ref,
+ 'SR': sr_ref,
+ 'currently-attached': attached})
def create_task(name_label):
- return _create_object('task', {
- 'name_label': name_label,
- 'status': 'pending',
- })
+ return _create_object('task',
+ {'name_label': name_label,
+ 'status': 'pending'})
+
+
+def create_local_pifs():
+ """Adds a PIF for each to the local database with VLAN=-1.
+ Do this one per host."""
+ for host_ref in _db_content['host'].keys():
+ _create_local_pif(host_ref)
def create_local_srs():
@@ -192,25 +200,34 @@ def create_local_srs():
def _create_local_sr(host_ref):
- sr_ref = _create_object('SR', {
- 'name_label': 'Local storage',
- 'type': 'lvm',
- 'content_type': 'user',
- 'shared': False,
- 'physical_size': str(1 << 30),
- 'physical_utilisation': str(0),
- 'virtual_allocation': str(0),
- 'other_config': {
- 'i18n-original-value-name_label': 'Local storage',
- 'i18n-key': 'local-storage',
- },
- 'VDIs': []
- })
+ sr_ref = _create_object(
+ 'SR',
+ {'name_label': 'Local storage',
+ 'type': 'lvm',
+ 'content_type': 'user',
+ 'shared': False,
+ 'physical_size': str(1 << 30),
+ 'physical_utilisation': str(0),
+ 'virtual_allocation': str(0),
+ 'other_config': {
+ 'i18n-original-value-name_label': 'Local storage',
+ 'i18n-key': 'local-storage'},
+ 'VDIs': []})
pbd_ref = create_pbd('', host_ref, sr_ref, True)
_db_content['SR'][sr_ref]['PBDs'] = [pbd_ref]
return sr_ref
+def _create_local_pif(host_ref):
+ pif_ref = _create_object('PIF',
+ {'name-label': 'Fake PIF',
+ 'MAC': '00:11:22:33:44:55',
+ 'physical': True,
+ 'VLAN': -1,
+ 'device': 'fake0',
+ 'host_uuid': host_ref})
+
+
def _create_object(table, obj):
ref = str(uuid.uuid4())
obj['uuid'] = str(uuid.uuid4())
@@ -234,6 +251,21 @@ def _create_sr(table, obj):
return sr_ref
+def _create_vlan(pif_ref, vlan_num, network_ref):
+ pif_rec = get_record('PIF', pif_ref)
+ vlan_pif_ref = _create_object('PIF',
+ {'name-label': 'Fake VLAN PIF',
+ 'MAC': '00:11:22:33:44:55',
+ 'physical': True,
+ 'VLAN': vlan_num,
+ 'device': pif_rec['device'],
+ 'host_uuid': pif_rec['host_uuid']})
+ return _create_object('VLAN',
+ {'tagged-pif': pif_ref,
+ 'untagged-pif': vlan_pif_ref,
+ 'tag': vlan_num})
+
+
def get_all(table):
return _db_content[table].keys()
@@ -292,6 +324,10 @@ class SessionBase(object):
rec['currently_attached'] = False
rec['device'] = ''
+ def PIF_get_all_records_where(self, _1, _2):
+ # TODO (salvatore-orlando): filter table on _2
+ return _db_content['PIF']
+
def VM_get_xenstore_data(self, _1, vm_ref):
return _db_content['VM'][vm_ref].get('xenstore_data', '')
@@ -302,7 +338,7 @@ class SessionBase(object):
db_ref['xenstore_data'][key] = None
def network_get_all_records_where(self, _1, _2):
- # TODO (salvatore-orlando):filter table on _2
+ # TODO (salvatore-orlando): filter table on _2
return _db_content['network']
def VM_add_to_xenstore_data(self, _1, vm_ref, key, value):
@@ -318,6 +354,9 @@ class SessionBase(object):
def host_call_plugin(*args):
return 'herp'
+ def network_get_all_records_where(self, _1, filter):
+ return self.xenapi.network.get_all_records()
+
def xenapi_request(self, methodname, params):
if methodname.startswith('login'):
self._login(methodname, params)
@@ -337,10 +376,9 @@ class SessionBase(object):
def _login(self, method, params):
self._session = str(uuid.uuid4())
- _db_content['session'][self._session] = {
- 'uuid': str(uuid.uuid4()),
- 'this_host': _db_content['host'].keys()[0],
- }
+ _db_content['session'][self._session] = \
+ {'uuid': str(uuid.uuid4()),
+ 'this_host': _db_content['host'].keys()[0]}
def _logout(self):
s = self._session
@@ -456,12 +494,16 @@ class SessionBase(object):
def _create(self, name, params):
self._check_session(params)
is_sr_create = name == 'SR.create'
+ is_vlan_create = name == 'VLAN.create'
# Storage Repositories have a different API
- expected = is_sr_create and 10 or 2
+ expected = is_sr_create and 10 or is_vlan_create and 4 or 2
self._check_arg_count(params, expected)
(cls, _) = name.split('.')
ref = is_sr_create and \
- _create_sr(cls, params) or _create_object(cls, params[1])
+ _create_sr(cls, params) or \
+ is_vlan_create and \
+ _create_vlan(params[1], params[2], params[3]) or \
+ _create_object(cls, params[1])
# Call hook to provide any fixups needed (ex. creating backrefs)
after_hook = 'after_%s_create' % cls
diff --git a/nova/virt/xenapi/network_utils.py b/nova/virt/xenapi/network_utils.py
index c0406d8f0..94d8e5199 100644
--- a/nova/virt/xenapi/network_utils.py
+++ b/nova/virt/xenapi/network_utils.py
@@ -28,11 +28,26 @@ class NetworkHelper(HelperBase):
"""
The class that wraps the helper methods together.
"""
+ @classmethod
+ def find_network_with_name_label(cls, session, name_label):
+ networks = session.call_xenapi('network.get_by_name_label', name_label)
+ if len(networks) == 1:
+ return networks[0]
+ elif len(networks) > 1:
+ raise Exception(_('Found non-unique network'
+ ' for name_label %s') % name_label)
+ else:
+ return None
@classmethod
def find_network_with_bridge(cls, session, bridge):
- """Return the network on which the bridge is attached, if found."""
- expr = 'field "bridge" = "%s"' % bridge
+ """
+ Return the network on which the bridge is attached, if found.
+ The bridge is defined in the nova db and can be found either in the
+ 'bridge' or 'name_label' fields of the XenAPI network record.
+ """
+ expr = 'field "name__label" = "%s" or ' \
+ 'field "bridge" = "%s"' % (bridge, bridge)
networks = session.call_xenapi('network.get_all_records_where', expr)
if len(networks) == 1:
return networks.keys()[0]
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 2288ea8a5..d07d60800 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -1108,11 +1108,13 @@ def _prepare_injectables(inst, networks_info):
if networks_info:
ifc_num = -1
interfaces_info = []
+ have_injected_networks = False
for (network_ref, info) in networks_info:
ifc_num += 1
if not network_ref['injected']:
continue
+ have_injected_networks = True
ip_v4 = ip_v6 = None
if 'ips' in info and len(info['ips']) > 0:
ip_v4 = info['ips'][0]
@@ -1131,7 +1133,9 @@ def _prepare_injectables(inst, networks_info):
'gateway_v6': ip_v6 and ip_v6['gateway'] or '',
'use_ipv6': FLAGS.use_ipv6}
interfaces_info.append(interface_info)
- net = str(template(template_data,
- searchList=[{'interfaces': interfaces_info,
- 'use_ipv6': FLAGS.use_ipv6}]))
+
+ if have_injected_networks:
+ net = str(template(template_data,
+ searchList=[{'interfaces': interfaces_info,
+ 'use_ipv6': FLAGS.use_ipv6}]))
return key, net
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index 0235e2dc4..c96c35a6e 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -58,7 +58,7 @@ class VMOps(object):
VMHelper.XenAPI = self.XenAPI
def list_instances(self):
- """List VM instances"""
+ """List VM instances."""
# TODO(justinsb): Should we just always use the details method?
# Seems to be the same number of API calls..
vm_refs = []
@@ -69,7 +69,7 @@ class VMOps(object):
return vm_refs
def list_instances_detail(self):
- """List VM instances, returning InstanceInfo objects"""
+ """List VM instances, returning InstanceInfo objects."""
instance_infos = []
for vm_ref in self._session.get_xenapi().VM.get_all():
vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
@@ -119,11 +119,11 @@ class VMOps(object):
self._spawn(instance, vm_ref)
def spawn_rescue(self, instance):
- """Spawn a rescue instance"""
+ """Spawn a rescue instance."""
self.spawn(instance)
def _create_vm(self, instance, vdi_uuid, network_info=None):
- """Create VM instance"""
+ """Create VM instance."""
instance_name = instance.name
vm_ref = VMHelper.lookup(self._session, instance_name)
if vm_ref is not None:
@@ -180,7 +180,7 @@ class VMOps(object):
return vm_ref
def _spawn(self, instance, vm_ref):
- """Spawn a new instance"""
+ """Spawn a new instance."""
LOG.debug(_('Starting VM %s...'), vm_ref)
self._start(instance, vm_ref)
instance_name = instance.name
@@ -236,7 +236,8 @@ class VMOps(object):
return timer.start(interval=0.5, now=True)
def _get_vm_opaque_ref(self, instance_or_vm):
- """Refactored out the common code of many methods that receive either
+ """
+ Refactored out the common code of many methods that receive either
a vm name or a vm instance, and want a vm instance in return.
"""
# if instance_or_vm is a string it must be opaque ref or instance name
@@ -264,21 +265,21 @@ class VMOps(object):
return vm_ref
def _acquire_bootlock(self, vm):
- """Prevent an instance from booting"""
+ """Prevent an instance from booting."""
self._session.call_xenapi(
"VM.set_blocked_operations",
vm,
{"start": ""})
def _release_bootlock(self, vm):
- """Allow an instance to boot"""
+ """Allow an instance to boot."""
self._session.call_xenapi(
"VM.remove_from_blocked_operations",
vm,
"start")
def snapshot(self, instance, image_id):
- """Create snapshot from a running VM instance
+ """Create snapshot from a running VM instance.
:param instance: instance to be snapshotted
:param image_id: id of image to upload to
@@ -298,6 +299,7 @@ class VMOps(object):
3. Push-to-glance: Once coalesced, we call a plugin on the XenServer
that will bundle the VHDs together and then push the bundle into
Glance.
+
"""
template_vm_ref = None
try:
@@ -330,11 +332,12 @@ class VMOps(object):
return
def migrate_disk_and_power_off(self, instance, dest):
- """Copies a VHD from one host machine to another
+ """Copies a VHD from one host machine to another.
+
+ :param instance: the instance that owns the VHD in question.
+ :param dest: the destination host machine.
+ :param disk_type: values are 'primary' or 'cow'.
- :param instance: the instance that owns the VHD in question
- :param dest: the destination host machine
- :param disk_type: values are 'primary' or 'cow'
"""
vm_ref = VMHelper.lookup(self._session, instance.name)
@@ -383,7 +386,7 @@ class VMOps(object):
return {'base_copy': base_copy_uuid, 'cow': cow_uuid}
def link_disks(self, instance, base_copy_uuid, cow_uuid):
- """Links the base copy VHD to the COW via the XAPI plugin"""
+ """Links the base copy VHD to the COW via the XAPI plugin."""
vm_ref = VMHelper.lookup(self._session, instance.name)
new_base_copy_uuid = str(uuid.uuid4())
new_cow_uuid = str(uuid.uuid4())
@@ -404,7 +407,7 @@ class VMOps(object):
return new_cow_uuid
def resize_instance(self, instance, vdi_uuid):
- """Resize a running instance by changing it's RAM and disk size """
+ """Resize a running instance by changing it's RAM and disk size."""
#TODO(mdietz): this will need to be adjusted for swap later
#The new disk size must be in bytes
@@ -418,18 +421,20 @@ class VMOps(object):
LOG.debug(_("Resize instance %s complete") % (instance.name))
def reboot(self, instance):
- """Reboot VM instance"""
+ """Reboot VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.clean_reboot', vm_ref)
self._session.wait_for_task(task, instance.id)
def set_admin_password(self, instance, new_pass):
- """Set the root/admin password on the VM instance. This is done via
- an agent running on the VM. Communication between nova and the agent
- is done via writing xenstore records. Since communication is done over
- the XenAPI RPC calls, we need to encrypt the password. We're using a
- simple Diffie-Hellman class instead of the more advanced one in
- M2Crypto for compatibility with the agent code.
+ """Set the root/admin password on the VM instance.
+
+ This is done via an agent running on the VM. Communication between nova
+ and the agent is done via writing xenstore records. Since communication
+ is done over the XenAPI RPC calls, we need to encrypt the password.
+ We're using a simple Diffie-Hellman class instead of the more advanced
+ one in M2Crypto for compatibility with the agent code.
+
"""
# Need to uniquely identify this request.
transaction_id = str(uuid.uuid4())
@@ -462,11 +467,14 @@ class VMOps(object):
return resp_dict['message']
def inject_file(self, instance, path, contents):
- """Write a file to the VM instance. The path to which it is to be
- written and the contents of the file need to be supplied; both will
- be base64-encoded to prevent errors with non-ASCII characters being
- transmitted. If the agent does not support file injection, or the user
- has disabled it, a NotImplementedError will be raised.
+ """Write a file to the VM instance.
+
+ The path to which it is to be written and the contents of the file
+ need to be supplied; both will be base64-encoded to prevent errors
+ with non-ASCII characters being transmitted. If the agent does not
+ support file injection, or the user has disabled it, a
+ NotImplementedError will be raised.
+
"""
# Files/paths must be base64-encoded for transmission to agent
b64_path = base64.b64encode(path)
@@ -487,7 +495,7 @@ class VMOps(object):
return resp_dict['message']
def _shutdown(self, instance, vm_ref, hard=True):
- """Shutdown an instance"""
+ """Shutdown an instance."""
state = self.get_info(instance['name'])['state']
if state == power_state.SHUTDOWN:
instance_name = instance.name
@@ -511,11 +519,11 @@ class VMOps(object):
LOG.exception(exc)
def _shutdown_rescue(self, rescue_vm_ref):
- """Shutdown a rescue instance"""
+ """Shutdown a rescue instance."""
self._session.call_xenapi("Async.VM.hard_shutdown", rescue_vm_ref)
def _destroy_vdis(self, instance, vm_ref):
- """Destroys all VDIs associated with a VM"""
+ """Destroys all VDIs associated with a VM."""
instance_id = instance.id
LOG.debug(_("Destroying VDIs for Instance %(instance_id)s")
% locals())
@@ -532,7 +540,7 @@ class VMOps(object):
LOG.exception(exc)
def _destroy_rescue_vdis(self, rescue_vm_ref):
- """Destroys all VDIs associated with a rescued VM"""
+ """Destroys all VDIs associated with a rescued VM."""
vdi_refs = VMHelper.lookup_vm_vdis(self._session, rescue_vm_ref)
for vdi_ref in vdi_refs:
try:
@@ -541,7 +549,7 @@ class VMOps(object):
continue
def _destroy_rescue_vbds(self, rescue_vm_ref):
- """Destroys all VBDs tied to a rescue VM"""
+ """Destroys all VBDs tied to a rescue VM."""
vbd_refs = self._session.get_xenapi().VM.get_VBDs(rescue_vm_ref)
for vbd_ref in vbd_refs:
vbd_rec = self._session.get_xenapi().VBD.get_record(vbd_ref)
@@ -550,8 +558,7 @@ class VMOps(object):
VMHelper.destroy_vbd(self._session, vbd_ref)
def _destroy_kernel_ramdisk(self, instance, vm_ref):
- """
- Three situations can occur:
+ """Three situations can occur:
1. We have neither a ramdisk nor a kernel, in which case we are a
RAW image and can omit this step
@@ -561,6 +568,7 @@ class VMOps(object):
3. We have both, in which case we safely remove both the kernel
and the ramdisk.
+
"""
instance_id = instance.id
if not instance.kernel_id and not instance.ramdisk_id:
@@ -589,7 +597,7 @@ class VMOps(object):
LOG.debug(_("kernel/ramdisk files removed"))
def _destroy_vm(self, instance, vm_ref):
- """Destroys a VM record"""
+ """Destroys a VM record."""
instance_id = instance.id
try:
task = self._session.call_xenapi('Async.VM.destroy', vm_ref)
@@ -600,7 +608,7 @@ class VMOps(object):
LOG.debug(_("Instance %(instance_id)s VM destroyed") % locals())
def _destroy_rescue_instance(self, rescue_vm_ref):
- """Destroy a rescue instance"""
+ """Destroy a rescue instance."""
self._destroy_rescue_vbds(rescue_vm_ref)
self._shutdown_rescue(rescue_vm_ref)
self._destroy_rescue_vdis(rescue_vm_ref)
@@ -608,11 +616,11 @@ class VMOps(object):
self._session.call_xenapi("Async.VM.destroy", rescue_vm_ref)
def destroy(self, instance):
- """
- Destroy VM instance
+ """Destroy VM instance.
This is the method exposed by xenapi_conn.destroy(). The rest of the
destroy_* methods are internal.
+
"""
instance_id = instance.id
LOG.info(_("Destroying VM for Instance %(instance_id)s") % locals())
@@ -621,13 +629,13 @@ class VMOps(object):
def _destroy(self, instance, vm_ref, shutdown=True,
destroy_kernel_ramdisk=True):
- """
- Destroys VM instance by performing:
+ """Destroys VM instance by performing:
+
+ 1. A shutdown if requested.
+ 2. Destroying associated VDIs.
+ 3. Destroying kernel and ramdisk files (if necessary).
+ 4. Destroying that actual VM record.
- 1. A shutdown if requested
- 2. Destroying associated VDIs
- 3. Destroying kernel and ramdisk files (if necessary)
- 4. Destroying that actual VM record
"""
if vm_ref is None:
LOG.warning(_("VM is not present, skipping destroy..."))
@@ -650,35 +658,36 @@ class VMOps(object):
callback(ret)
def pause(self, instance, callback):
- """Pause VM instance"""
+ """Pause VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.pause', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def unpause(self, instance, callback):
- """Unpause VM instance"""
+ """Unpause VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.unpause', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def suspend(self, instance, callback):
- """suspend the specified instance"""
+ """Suspend the specified instance."""
vm_ref = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.suspend', vm_ref)
self._wait_with_callback(instance.id, task, callback)
def resume(self, instance, callback):
- """resume the specified instance"""
+ """Resume the specified instance."""
vm_ref = self._get_vm_opaque_ref(instance)
task = self._session.call_xenapi('Async.VM.resume', vm_ref, False,
True)
self._wait_with_callback(instance.id, task, callback)
def rescue(self, instance, callback):
- """Rescue the specified instance
- - shutdown the instance VM
- - set 'bootlock' to prevent the instance from starting in rescue
- - spawn a rescue VM (the vm name-label will be instance-N-rescue)
+ """Rescue the specified instance.
+
+ - shutdown the instance VM.
+ - set 'bootlock' to prevent the instance from starting in rescue.
+ - spawn a rescue VM (the vm name-label will be instance-N-rescue).
"""
rescue_vm_ref = VMHelper.lookup(self._session,
@@ -702,10 +711,11 @@ class VMOps(object):
self._session.call_xenapi("Async.VBD.plug", rescue_vbd_ref)
def unrescue(self, instance, callback):
- """Unrescue the specified instance
- - unplug the instance VM's disk from the rescue VM
- - teardown the rescue VM
- - release the bootlock to allow the instance VM to start
+ """Unrescue the specified instance.
+
+ - unplug the instance VM's disk from the rescue VM.
+ - teardown the rescue VM.
+ - release the bootlock to allow the instance VM to start.
"""
rescue_vm_ref = VMHelper.lookup(self._session,
@@ -723,9 +733,11 @@ class VMOps(object):
self._start(instance, original_vm_ref)
def poll_rescued_instances(self, timeout):
- """Look for expirable rescued instances
+ """Look for expirable rescued instances.
+
- forcibly exit rescue mode for any instances that have been
in rescue mode for >= the provided timeout
+
"""
last_ran = self.poll_rescue_last_ran
if not last_ran:
@@ -761,30 +773,30 @@ class VMOps(object):
False)
def get_info(self, instance):
- """Return data about VM instance"""
+ """Return data about VM instance."""
vm_ref = self._get_vm_opaque_ref(instance)
vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
return VMHelper.compile_info(vm_rec)
def get_diagnostics(self, instance):
- """Return data about VM diagnostics"""
+ """Return data about VM diagnostics."""
vm_ref = self._get_vm_opaque_ref(instance)
vm_rec = self._session.get_xenapi().VM.get_record(vm_ref)
return VMHelper.compile_diagnostics(self._session, vm_rec)
def get_console_output(self, instance):
- """Return snapshot of console"""
+ """Return snapshot of console."""
# TODO: implement this to fix pylint!
return 'FAKE CONSOLE OUTPUT of instance'
def get_ajax_console(self, instance):
- """Return link to instance's ajax console"""
+ """Return link to instance's ajax console."""
# TODO: implement this!
return 'http://fakeajaxconsole/fake_url'
# TODO(tr3buchet) - remove this function after nova multi-nic
def _get_network_info(self, instance):
- """creates network info list for instance"""
+ """Creates network info list for instance."""
admin_context = context.get_admin_context()
IPs = db.fixed_ip_get_all_by_instance(admin_context,
instance['id'])
@@ -826,7 +838,7 @@ class VMOps(object):
def inject_network_info(self, instance, vm_ref, network_info):
"""
Generate the network info and make calls to place it into the
- xenstore and the xenstore param list
+ xenstore and the xenstore param list.
"""
logging.debug(_("injecting network info to xs for vm: |%s|"), vm_ref)
@@ -847,7 +859,7 @@ class VMOps(object):
pass
def create_vifs(self, vm_ref, network_info):
- """Creates vifs for an instance"""
+ """Creates vifs for an instance."""
logging.debug(_("creating vif(s) for vm: |%s|"), vm_ref)
# this function raises if vm_ref is not a vm_opaque_ref
@@ -858,8 +870,8 @@ class VMOps(object):
bridge = network['bridge']
rxtx_cap = info.pop('rxtx_cap')
network_ref = \
- NetworkHelper.find_network_with_bridge(self._session, bridge)
-
+ NetworkHelper.find_network_with_bridge(self._session,
+ bridge)
VMHelper.create_vif(self._session, vm_ref, network_ref,
mac_address, device, rxtx_cap)
@@ -872,7 +884,8 @@ class VMOps(object):
args, vm_ref)
def list_from_xenstore(self, vm, path):
- """Runs the xenstore-ls command to get a listing of all records
+ """
+ Runs the xenstore-ls command to get a listing of all records
from 'path' downward. Returns a dict with the sub-paths as keys,
and the value stored in those paths as values. If nothing is
found at that path, returns None.
@@ -881,7 +894,8 @@ class VMOps(object):
return json.loads(ret)
def read_from_xenstore(self, vm, path):
- """Returns the value stored in the xenstore record for the given VM
+ """
+ Returns the value stored in the xenstore record for the given VM
at the specified location. A XenAPIPlugin.PluginError will be raised
if any error is encountered in the read process.
"""
@@ -897,7 +911,8 @@ class VMOps(object):
return ret
def write_to_xenstore(self, vm, path, value):
- """Writes the passed value to the xenstore record for the given VM
+ """
+ Writes the passed value to the xenstore record for the given VM
at the specified location. A XenAPIPlugin.PluginError will be raised
if any error is encountered in the write process.
"""
@@ -905,7 +920,8 @@ class VMOps(object):
{'value': json.dumps(value)})
def clear_xenstore(self, vm, path):
- """Deletes the VM's xenstore record for the specified path.
+ """
+ Deletes the VM's xenstore record for the specified path.
If there is no such record, the request is ignored.
"""
self._make_xenstore_call('delete_record', vm, path)
@@ -922,7 +938,8 @@ class VMOps(object):
def _make_plugin_call(self, plugin, method, vm, path, addl_args=None,
vm_ref=None):
- """Abstracts out the process of calling a method of a xenapi plugin.
+ """
+ Abstracts out the process of calling a method of a xenapi plugin.
Any errors raised by the plugin will in turn raise a RuntimeError here.
"""
instance_id = vm.id
@@ -952,7 +969,8 @@ class VMOps(object):
return ret
def add_to_xenstore(self, vm, path, key, value):
- """Adds the passed key/value pair to the xenstore record for
+ """
+ Adds the passed key/value pair to the xenstore record for
the given VM at the specified location. A XenAPIPlugin.PluginError
will be raised if any error is encountered in the write process.
"""
@@ -965,7 +983,8 @@ class VMOps(object):
self.write_to_xenstore(vm, path, current)
def remove_from_xenstore(self, vm, path, key_or_keys):
- """Takes either a single key or a list of keys and removes
+ """
+ Takes either a single key or a list of keys and removes
them from the xenstoreirecord data for the given VM.
If the key doesn't exist, the request is ignored.
"""
@@ -992,7 +1011,8 @@ class VMOps(object):
###### names to distinguish them. (dabo)
########################################################################
def read_partial_from_param_xenstore(self, instance_or_vm, key_prefix):
- """Returns a dict of all the keys in the xenstore parameter record
+ """
+ Returns a dict of all the keys in the xenstore parameter record
for the given instance that begin with the key_prefix.
"""
data = self.read_from_param_xenstore(instance_or_vm)
@@ -1003,7 +1023,8 @@ class VMOps(object):
return data
def read_from_param_xenstore(self, instance_or_vm, keys=None):
- """Returns the xenstore parameter record data for the specified VM
+ """
+ Returns the xenstore parameter record data for the specified VM
instance as a dict. Accepts an optional key or list of keys; if a
value for 'keys' is passed, the returned dict is filtered to only
return the values for those keys.
@@ -1025,9 +1046,11 @@ class VMOps(object):
return ret
def add_to_param_xenstore(self, instance_or_vm, key, val):
- """Takes a key/value pair and adds it to the xenstore parameter
+ """
+ Takes a key/value pair and adds it to the xenstore parameter
record for the given vm instance. If the key exists in xenstore,
- it is overwritten"""
+ it is overwritten
+ """
vm_ref = self._get_vm_opaque_ref(instance_or_vm)
self.remove_from_param_xenstore(instance_or_vm, key)
jsonval = json.dumps(val)
@@ -1035,7 +1058,8 @@ class VMOps(object):
(vm_ref, key, jsonval))
def write_to_param_xenstore(self, instance_or_vm, mapping):
- """Takes a dict and writes each key/value pair to the xenstore
+ """
+ Takes a dict and writes each key/value pair to the xenstore
parameter record for the given vm instance. Any existing data for
those keys is overwritten.
"""
@@ -1043,7 +1067,8 @@ class VMOps(object):
self.add_to_param_xenstore(instance_or_vm, k, v)
def remove_from_param_xenstore(self, instance_or_vm, key_or_keys):
- """Takes either a single key or a list of keys and removes
+ """
+ Takes either a single key or a list of keys and removes
them from the xenstore parameter record data for the given VM.
If the key doesn't exist, the request is ignored.
"""
@@ -1069,7 +1094,8 @@ def _runproc(cmd):
class SimpleDH(object):
- """This class wraps all the functionality needed to implement
+ """
+ This class wraps all the functionality needed to implement
basic Diffie-Hellman-Merkle key exchange in Python. It features
intelligent defaults for the prime and base numbers needed for the
calculation, while allowing you to supply your own. It requires that
@@ -1078,7 +1104,8 @@ class SimpleDH(object):
is not available, a RuntimeError will be raised.
"""
def __init__(self, prime=None, base=None, secret=None):
- """You can specify the values for prime and base if you wish;
+ """
+ You can specify the values for prime and base if you wish;
otherwise, reasonable default values will be used.
"""
if prime is None: