From fe22186eb989b0302e1cb26a5b92cd77ce47bb9b Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Fri, 14 Jan 2011 16:13:50 +0000 Subject: OS-55: Inject network settings in linux images --- nova/virt/conn_common.py | 50 ++++++++++++++++++++++ nova/virt/disk.py | 19 +++++--- nova/virt/libvirt_conn.py | 24 ++--------- nova/virt/xenapi/vm_utils.py | 36 ++++++++++++++++ nova/virt/xenapi/vmops.py | 7 +++ .../xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 1 + 6 files changed, 110 insertions(+), 27 deletions(-) create mode 100644 nova/virt/conn_common.py diff --git a/nova/virt/conn_common.py b/nova/virt/conn_common.py new file mode 100644 index 000000000..bd9ed7794 --- /dev/null +++ b/nova/virt/conn_common.py @@ -0,0 +1,50 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2010 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from nova import context +from nova import db +from nova import exception +from nova import flags +from nova import log as logging +from nova import utils + +LOG = logging.getLogger('nova.virt.conn_common') +FLAGS = flags.FLAGS + +flags.DEFINE_string('injected_network_template', + utils.abspath('virt/interfaces.template'), + 'Template file for injected network') + +def get_injectables(inst): + key = str(inst['key_data']) + net = None + network_ref = db.network_get_by_instance(context.get_admin_context(), + inst['id']) + if network_ref['injected']: + admin_context = context.get_admin_context() + address = db.instance_get_fixed_address(admin_context, inst['id']) + ra_server = network_ref['ra_server'] + if not ra_server: + ra_server = "fd00::" + with open(FLAGS.injected_network_template) as f: + net = f.read() % {'address': address, + 'netmask': network_ref['netmask'], + 'gateway': network_ref['gateway'], + 'broadcast': network_ref['broadcast'], + 'dns': network_ref['dns'], + 'ra_server': ra_server} + + return key, net diff --git a/nova/virt/disk.py b/nova/virt/disk.py index c5565abfa..88b05f0c0 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -92,11 +92,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): % err) try: - if key: - # inject key file - _inject_key_into_fs(key, tmpdir) - if net: - _inject_net_into_fs(net, tmpdir) + inject_data_into_fs(tmpdir, key, net, execute) finally: # unmount device utils.execute('sudo umount %s' % mapped_device) @@ -158,8 +154,17 @@ def _allocate_device(): def _free_device(device): _DEVICES.append(device) +def inject_data_into_fs(fs, key, net, execute): + """Injects data into a filesystem already mounted by the caller. + Virt connections can call this directly if they mount their fs + in a different way to inject_data + """ + if key: + _inject_key_into_fs(key, fs, execute=execute) + if net: + _inject_net_into_fs(net, fs, execute=execute) -def _inject_key_into_fs(key, fs): +def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. key is an ssh key string. @@ -173,7 +178,7 @@ def _inject_key_into_fs(key, fs): utils.execute('sudo tee -a %s' % keyfile, '\n' + key.strip() + '\n') -def _inject_net_into_fs(net, fs): +def _inject_net_into_fs(net, fs, execute=None): """Inject /etc/network/interfaces into the filesystem rooted at fs. net is the contents of /etc/network/interfaces. diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4e0fd106f..45d8754ab 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -62,6 +62,7 @@ from nova.compute import instance_types from nova.compute import power_state from nova.virt import disk from nova.virt import images +from nova.virt import conn_common libvirt = None libxml2 = None @@ -74,9 +75,7 @@ FLAGS = flags.FLAGS flags.DEFINE_string('rescue_image_id', 'ami-rescue', 'Rescue ami image') flags.DEFINE_string('rescue_kernel_id', 'aki-rescue', 'Rescue aki image') flags.DEFINE_string('rescue_ramdisk_id', 'ari-rescue', 'Rescue ari image') -flags.DEFINE_string('injected_network_template', - utils.abspath('virt/interfaces.template'), - 'Template file for injected network') + flags.DEFINE_string('libvirt_xml_template', utils.abspath('virt/libvirt.xml.template'), 'Libvirt XML Template') @@ -622,23 +621,8 @@ class LibvirtConnection(object): if not inst['kernel_id']: target_partition = "1" - key = str(inst['key_data']) - net = None - network_ref = db.network_get_by_instance(context.get_admin_context(), - inst['id']) - if network_ref['injected']: - admin_context = context.get_admin_context() - address = db.instance_get_fixed_address(admin_context, inst['id']) - ra_server = network_ref['ra_server'] - if not ra_server: - ra_server = "fd00::" - with open(FLAGS.injected_network_template) as f: - net = f.read() % {'address': address, - 'netmask': network_ref['netmask'], - 'gateway': network_ref['gateway'], - 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns'], - 'ra_server': ra_server} + key, net = conn_common.get_injectables(inst) + if key or net: inst_name = inst['name'] img_id = inst.image_id diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 4bbd522c1..70f81b3b6 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -22,6 +22,7 @@ their attributes like VDIs, VIFs, as well as their lookup functions. import os import pickle import re +import tempfile import time import urllib from xml.dom import minidom @@ -33,10 +34,12 @@ from nova import flags from nova import log as logging from nova import utils from nova.auth.manager import AuthManager +from nova.compute import disk from nova.compute import instance_types from nova.compute import power_state from nova.virt import images from nova.virt.xenapi import HelperBase +from nova.virt import conn_common from nova.virt.xenapi.volume_utils import StorageError @@ -439,6 +442,39 @@ class VMHelper(HelperBase): else: return None + @classmethod + def preconfigure_instance(cls, session, instance, vdi_ref): + """Makes alterations to the image before launching as part of spawn. + May also set xenstore values to modify the image behaviour after + VM start.""" + + # As mounting the image VDI is expensive, we only want do do it once, + # if at all, so determine whether it's required first, and then do + # everything + mount_required = False + key, net = conn_common.get_injectables(instance) + if key is not None or net is not None: + mount_required = True + + if mount_required: + def _mounted_processing(device): + devPath = '/dev/'+device+'1' # Note: Partition 1 hardcoded + tmpdir = tempfile.mkdtemp() + try: + out, err = utils.execute('sudo mount %s %s' % (devPath, tmpdir)) + if err: + raise exception.Error(_('Failed to mount filesystem: %s') % err) + try: + disk.inject_data_into_fs(tmpdir, key, net, utils.execute) + finally: + utils.execute('sudo umount %s' % devPath) + finally: + # remove temporary directory + os.rmdir(tmpdir) + + # FIXME: Check self._session is the type of session this fn wants + with_vdi_attached_here(session, vdi_ref, False, _mounted_processing) + @classmethod def compile_info(cls, record): """Fill record with VM status information""" diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index e84ce20c4..efab9c9ce 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -79,6 +79,9 @@ class VMOps(object): disk_image_type = ImageType.DISK else: disk_image_type = ImageType.DISK_RAW + # TODO: Coalesce fetch_image, lookup_image and + # manipulate_root_image so requires a single VDI mount/umount + # sequence vdi_uuid = VMHelper.fetch_image(self._session, instance.id, instance.image_id, user, project, disk_image_type) vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', vdi_uuid) @@ -102,6 +105,10 @@ class VMOps(object): if network_ref: VMHelper.create_vif(self._session, vm_ref, network_ref, instance.mac_address) + + # Alter the image before VM start for, e.g. network injection + VMHelper.preconfigure_instance(self._session, instance, vdi_ref) + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py index f51f5fce4..dbbce5c51 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py @@ -28,6 +28,7 @@ import re import time import XenAPI +import XenAPI ##### Logging setup -- cgit From 88be6540d2a796e313f2d8ef4ccc6e66ba1a3ed1 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Thu, 20 Jan 2011 16:49:54 +0000 Subject: OS-55: Only modify Linux image with no or injection-incapable guest agent OS-55: Support network configuration via xenstore for Windows images --- nova/virt/xenapi/vm_utils.py | 105 +++++++++++++++++++++++++++++++++++++++---- nova/virt/xenapi/vmops.py | 3 ++ 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 70f81b3b6..79d529ce2 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -29,6 +29,8 @@ from xml.dom import minidom from eventlet import event import glance.client +from nova import context +from nova import db from nova import exception from nova import flags from nova import log as logging @@ -458,22 +460,107 @@ class VMHelper(HelperBase): if mount_required: def _mounted_processing(device): - devPath = '/dev/'+device+'1' # Note: Partition 1 hardcoded + devPath = '/dev/'+device+'1' # NB: Partition 1 hardcoded tmpdir = tempfile.mkdtemp() try: - out, err = utils.execute('sudo mount %s %s' % (devPath, tmpdir)) - if err: - raise exception.Error(_('Failed to mount filesystem: %s') % err) + # Mount only Linux filesystems, as we mustn't disturb NTFS images try: - disk.inject_data_into_fs(tmpdir, key, net, utils.execute) - finally: - utils.execute('sudo umount %s' % devPath) + out, err = utils.execute('sudo mount -t ext2,ext3 "%s" "%s"' % (devPath, tmpdir)) + except exception.ProcessExecutionError as e: + err = str(e) + if err: + LOG.info('Failed to mount filesystem (expected for non-linux instances): %s' % err) + else: + try: + # This try block ensures that the umount occurs + + xe_update_networking_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-update-networking') + if os.path.isfile(xe_update_networking_filename): + # The presence of the xe-update-networking file indicates that this guest + # agent can reconfigure the netwokr from xenstore data, so manipulation + # of files in /etc is not required + LOG.info('XenServer tools installed in this image are capable of network injection. ' + 'Networking files will not be manipulated') + else: + xe_daemon_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-daemon') + if os.path.isfile(xe_daemon_filename): + LOG.info('XenServer tools are present in this image but ' + 'are not capable of network injection') + else: + LOG.info('XenServer tools are not installed in this image') + LOG.info('Manipulating interface files directly') + disk.inject_data_into_fs(tmpdir, key, net, utils.execute) + finally: + utils.execute('sudo umount "%s"' % devPath) finally: # remove temporary directory os.rmdir(tmpdir) - - # FIXME: Check self._session is the type of session this fn wants + with_vdi_attached_here(session, vdi_ref, False, _mounted_processing) + + + @classmethod + def preconfigure_xenstore(cls, session, instance, vm_ref): + XENSTORE_TYPES = { + 'BroadcastAddress' : 'multi_sz', + 'DefaultGateway' : 'multi_sz', + 'EnableDhcp' : 'dword', + 'IPAddress' : 'multi_sz', + 'NameServer' : 'string', + 'SubnetMask' : 'multi_sz' + } + + # Network setup + network_ref = db.network_get_by_instance(context.get_admin_context(), + instance['id']) + if network_ref['injected']: + admin_context = context.get_admin_context() + address = db.instance_get_fixed_address(admin_context, instance['id']) + + xenstore_data = { + # NB: Setting broadcast address is not supported by + # Windows or the Windows guest agent, and will be ignored + # on that platform + 'BroadcastAddress': network_ref['broadcast'], + 'EnableDhcp': '0', + 'IPAddress': address, + 'SubnetMask': network_ref['netmask'], + 'DefaultGateway': network_ref['gateway'], + 'NameServer': network_ref['dns'] + } + + device_to_configure = 0 # Configure network device 0 in the VM + + vif_refs = session.call_xenapi('VM.get_VIFs', vm_ref) + mac_addr = None + + for vif_ref in vif_refs: + device = session.call_xenapi('VIF.get_device', vif_ref) + if str(device) == str(device_to_configure): + mac_addr = session.call_xenapi('VIF.get_MAC', vif_ref) + break + + if mac_addr is None: + raise exception.NotFound('Networking device %s not found in VM') + + # MAC address must be upper case in the xenstore key, + # with colons replaced by underscores + underscore_mac_addr = mac_addr.replace(':', '_') + xenstore_prefix='vm-data/vif/'+underscore_mac_addr.upper()+'/tcpip/' + + for xenstore_key, xenstore_value in xenstore_data.iteritems(): + # NB: The xenstore_key part of the instance_key isn't used but must + # be unique. We set it to xenstore_key as a convenient unique name. + # The xenstore_key value takes effect in the /name element. + instance_key = xenstore_prefix + xenstore_key + type = XENSTORE_TYPES[xenstore_key] + + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/name', xenstore_key) + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/type', type) + if type == 'multi_sz': + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data/0', xenstore_value) + else: + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data', xenstore_value) @classmethod def compile_info(cls, record): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index efab9c9ce..ae477f11e 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -109,6 +109,9 @@ class VMOps(object): # Alter the image before VM start for, e.g. network injection VMHelper.preconfigure_instance(self._session, instance, vdi_ref) + # Configure the VM's xenstore data before start for, e.g. network configuration + VMHelper.preconfigure_xenstore(self._session, instance, vm_ref) + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name -- cgit From 8f531ef7c0782feba46f83ec2e45d113753c4052 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Thu, 20 Jan 2011 19:51:23 +0000 Subject: OS-55: pylint fixes --- nova/virt/xenapi/vm_utils.py | 96 ++++++++++++++++++++++++++++---------------- nova/virt/xenapi/vmops.py | 3 +- 2 files changed, 63 insertions(+), 36 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 79d529ce2..eb699d715 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -447,8 +447,7 @@ class VMHelper(HelperBase): @classmethod def preconfigure_instance(cls, session, instance, vdi_ref): """Makes alterations to the image before launching as part of spawn. - May also set xenstore values to modify the image behaviour after - VM start.""" + """ # As mounting the image VDI is expensive, we only want do do it once, # if at all, so determine whether it's required first, and then do @@ -460,38 +459,53 @@ class VMHelper(HelperBase): if mount_required: def _mounted_processing(device): - devPath = '/dev/'+device+'1' # NB: Partition 1 hardcoded + """Callback whioch runds with the image VDI attached""" + + dev_path = '/dev/'+device+'1' # NB: Partition 1 hardcoded tmpdir = tempfile.mkdtemp() try: - # Mount only Linux filesystems, as we mustn't disturb NTFS images + # Mount only Linux filesystems, to avoid disturbing + # NTFS images try: - out, err = utils.execute('sudo mount -t ext2,ext3 "%s" "%s"' % (devPath, tmpdir)) + _, err = utils.execute( + 'sudo mount -t ext2,ext3 "%s" "%s"' % + (dev_path, tmpdir)) except exception.ProcessExecutionError as e: err = str(e) if err: - LOG.info('Failed to mount filesystem (expected for non-linux instances): %s' % err) + LOG.info(_('Failed to mount filesystem (expected for ' + 'non-linux instances): %s') % err) else: try: # This try block ensures that the umount occurs - xe_update_networking_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-update-networking') + xe_update_networking_filename = os.path.join(tmpdir, + 'usr', 'sbin', 'xe-update-networking') if os.path.isfile(xe_update_networking_filename): - # The presence of the xe-update-networking file indicates that this guest - # agent can reconfigure the netwokr from xenstore data, so manipulation - # of files in /etc is not required - LOG.info('XenServer tools installed in this image are capable of network injection. ' - 'Networking files will not be manipulated') + # The presence of the xe-update-networking + # file indicates that this guest agent can + # reconfigure the network from xenstore data, + # so manipulation of files in /etc is not + # required + LOG.info(_('XenServer tools installed in this ' + 'image are capable of network injection. ' + 'Networking files will not be manipulated')) else: - xe_daemon_filename = os.path.join(tmpdir, 'usr', 'sbin', 'xe-daemon') + xe_daemon_filename = os.path.join(tmpdir, 'usr', + 'sbin', 'xe-daemon') if os.path.isfile(xe_daemon_filename): - LOG.info('XenServer tools are present in this image but ' - 'are not capable of network injection') + LOG.info(_('XenServer tools are present in ' + 'this image but are not capable of ' + 'network injection')) else: - LOG.info('XenServer tools are not installed in this image') - LOG.info('Manipulating interface files directly') - disk.inject_data_into_fs(tmpdir, key, net, utils.execute) + LOG.info(_('XenServer tools are not ' + 'installed in this image')) + LOG.info(_('Manipulating interface files ' + 'directly')) + disk.inject_data_into_fs(tmpdir, key, net, + utils.execute) finally: - utils.execute('sudo umount "%s"' % devPath) + utils.execute('sudo umount "%s"' % dev_path) finally: # remove temporary directory os.rmdir(tmpdir) @@ -501,7 +515,11 @@ class VMHelper(HelperBase): @classmethod def preconfigure_xenstore(cls, session, instance, vm_ref): - XENSTORE_TYPES = { + """Sets xenstore values to modify the image behaviour after + VM start. + """ + + xenstore_types = { 'BroadcastAddress' : 'multi_sz', 'DefaultGateway' : 'multi_sz', 'EnableDhcp' : 'dword', @@ -511,11 +529,12 @@ class VMHelper(HelperBase): } # Network setup - network_ref = db.network_get_by_instance(context.get_admin_context(), - instance['id']) + network_ref = db.network_get_by_instance( + context.get_admin_context(), instance['id']) if network_ref['injected']: admin_context = context.get_admin_context() - address = db.instance_get_fixed_address(admin_context, instance['id']) + address = db.instance_get_fixed_address(admin_context, + instance['id']) xenstore_data = { # NB: Setting broadcast address is not supported by @@ -541,26 +560,33 @@ class VMHelper(HelperBase): break if mac_addr is None: - raise exception.NotFound('Networking device %s not found in VM') + raise exception.NotFound(_('Networking device %s not found ' + 'in VM')) # MAC address must be upper case in the xenstore key, # with colons replaced by underscores underscore_mac_addr = mac_addr.replace(':', '_') - xenstore_prefix='vm-data/vif/'+underscore_mac_addr.upper()+'/tcpip/' + xenstore_prefix = ('vm-data/vif/' + + underscore_mac_addr.upper() + '/tcpip/') for xenstore_key, xenstore_value in xenstore_data.iteritems(): - # NB: The xenstore_key part of the instance_key isn't used but must - # be unique. We set it to xenstore_key as a convenient unique name. - # The xenstore_key value takes effect in the /name element. + # NB: The xenstore_key part of the instance_key isn't used but + # must be unique. We set it to xenstore_key as a convenient + # unique name...The xenstore_key value takes effect in the + # /name element. instance_key = xenstore_prefix + xenstore_key - type = XENSTORE_TYPES[xenstore_key] - - session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/name', xenstore_key) - session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/type', type) - if type == 'multi_sz': - session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data/0', xenstore_value) + key_type = xenstore_types[xenstore_key] + + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, + instance_key+'/name', xenstore_key) + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, + instance_key+'/type', key_type) + if key_type == 'multi_sz': + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, + instance_key+'/data/0', xenstore_value) else: - session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key+'/data', xenstore_value) + session.call_xenapi('VM.add_to_xenstore_data', vm_ref, + instance_key+'/data', xenstore_value) @classmethod def compile_info(cls, record): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ae477f11e..df282807a 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -109,7 +109,8 @@ class VMOps(object): # Alter the image before VM start for, e.g. network injection VMHelper.preconfigure_instance(self._session, instance, vdi_ref) - # Configure the VM's xenstore data before start for, e.g. network configuration + # Configure the VM's xenstore data before start for, + # e.g. network configuration VMHelper.preconfigure_xenstore(self._session, instance, vm_ref) LOG.debug(_('Starting VM %s...'), vm_ref) -- cgit From 1dbdb180cb93a812f8336bbfc49bf67a5203d1eb Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Fri, 21 Jan 2011 12:00:45 +0000 Subject: OS-55: Fix current unit tests --- nova/tests/db/fakes.py | 2 ++ nova/virt/xenapi/fake.py | 4 ++++ nova/virt/xenapi/vm_utils.py | 4 ++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 05bdd172e..72e093f99 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -58,6 +58,7 @@ def stub_out_db_instance_api(stubs): 'project_id': values['project_id'], 'launch_time': time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()), 'instance_type': values['instance_type'], + 'key_data': None, 'memory_mb': type_data['memory_mb'], 'mac_address': values['mac_address'], 'vcpus': type_data['vcpus'], @@ -68,6 +69,7 @@ def stub_out_db_instance_api(stubs): def fake_network_get_by_instance(context, instance_id): fields = { 'bridge': 'xenbr0', + 'injected': False } return FakeModel(fields) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index e8352771c..836252730 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -161,6 +161,10 @@ def after_VBD_create(vbd_ref, vbd_rec): vm_name_label = _db_content['VM'][vm_ref]['name_label'] vbd_rec['vm_name_label'] = vm_name_label +def after_VM_create(vm_ref, vm_rec): + """Create read-only fields in the VM record.""" + if 'is_control_domain' not in vm_rec: + vm_rec['is_control_domain'] = False def create_pbd(config, host_ref, sr_ref, attached): return _create_object('PBD', { diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eb699d715..9b78a178a 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -459,7 +459,7 @@ class VMHelper(HelperBase): if mount_required: def _mounted_processing(device): - """Callback whioch runds with the image VDI attached""" + """Callback which runs with the image VDI attached""" dev_path = '/dev/'+device+'1' # NB: Partition 1 hardcoded tmpdir = tempfile.mkdtemp() @@ -467,7 +467,7 @@ class VMHelper(HelperBase): # Mount only Linux filesystems, to avoid disturbing # NTFS images try: - _, err = utils.execute( + out, err = utils.execute( 'sudo mount -t ext2,ext3 "%s" "%s"' % (dev_path, tmpdir)) except exception.ProcessExecutionError as e: -- cgit From 9039c2cc59904a72fc71255a3a31ec2b17018963 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Fri, 21 Jan 2011 16:06:32 +0000 Subject: OS-55: Added unit test for network injection via xenstore --- nova/tests/db/fakes.py | 54 ++++++++++++++++++++++++++++++---------------- nova/tests/test_xenapi.py | 46 +++++++++++++++++++++++++++++++++------ nova/tests/xenapi/stubs.py | 15 +++++++++++++ nova/virt/xenapi/fake.py | 12 +++++++++-- 4 files changed, 100 insertions(+), 27 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 72e093f99..e92efbedd 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -23,23 +23,22 @@ from nova import db from nova import utils from nova.compute import instance_types +class FakeModel(object): + """ Stubs out for model """ + def __init__(self, values): + self.values = values -def stub_out_db_instance_api(stubs): - """ Stubs out the db API for creating Instances """ + def __getattr__(self, name): + return self.values[name] - class FakeModel(object): - """ Stubs out for model """ - def __init__(self, values): - self.values = values + def __getitem__(self, key): + if key in self.values: + return self.values[key] + else: + raise NotImplementedError() - def __getattr__(self, name): - return self.values[name] - - def __getitem__(self, key): - if key in self.values: - return self.values[key] - else: - raise NotImplementedError() +def stub_out_db_instance_api(stubs): + """ Stubs out the db API for creating Instances """ def fake_instance_create(values): """ Stubs out the db.instance_create method """ @@ -66,12 +65,29 @@ def stub_out_db_instance_api(stubs): } return FakeModel(base_options) - def fake_network_get_by_instance(context, instance_id): - fields = { + stubs.Set(db, 'instance_create', fake_instance_create) + +def stub_out_db_network_api(stubs, injected = False): + """Stubs out the db API for retrieving networks""" + network_fields = { 'bridge': 'xenbr0', - 'injected': False + 'injected': injected } - return FakeModel(fields) - stubs.Set(db, 'instance_create', fake_instance_create) + if injected: + network_fields.update({ + 'netmask': '255.255.255.0', + 'gateway': '10.0.0.1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2' + }) + + def fake_network_get_by_instance(context, instance_id): + return FakeModel(network_fields) + + def fake_instance_get_fixed_address(context, instance_id): + return '10.0.0.3' + stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) + stubs.Set(db, 'instance_get_fixed_address', fake_instance_get_fixed_address) + diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 9f5b266f3..2a32845d3 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -50,6 +50,7 @@ class XenAPIVolumeTestCase(test.TestCase): FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' db_fakes.stub_out_db_instance_api(self.stubs) + db_fakes.stub_out_db_network_api(self.stubs) stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() self.values = {'name': 1, 'id': 1, @@ -158,6 +159,7 @@ class XenAPIVMTestCase(test.TestCase): xenapi_fake.reset() xenapi_fake.create_local_srs() db_fakes.stub_out_db_instance_api(self.stubs) + db_fakes.stub_out_db_network_api(self.stubs) xenapi_fake.create_network('fake', FLAGS.flat_network_bridge) stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) stubs.stubout_get_this_vm_uuid(self.stubs) @@ -211,7 +213,7 @@ class XenAPIVMTestCase(test.TestCase): check() - def check_vm_record(self, conn): + def check_vm_record(self, conn, check_injection = False): instances = conn.list_instances() self.assertEquals(instances, [1]) @@ -219,10 +221,10 @@ class XenAPIVMTestCase(test.TestCase): vm_info = conn.get_info(1) # Get XenAPI record for VM - vms = [rec for ref, rec + vms = [(ref, rec) for ref, rec in xenapi_fake.get_all_records('VM').iteritems() if not rec['is_control_domain']] - vm = vms[0] + vm_ref, vm = vms[0] # Check that m1.large above turned into the right thing. instance_type = instance_types.INSTANCE_TYPES['m1.large'] @@ -242,8 +244,35 @@ class XenAPIVMTestCase(test.TestCase): # Check that the VM is running according to XenAPI. self.assertEquals(vm['power_state'], 'Running') - - def _test_spawn(self, image_id, kernel_id, ramdisk_id): + + if check_injection: + xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref) + key_prefix = 'vm-data/vif/22_33_2A_B3_CC_DD/tcpip/' + tcpip_data = dict([(k.replace(key_prefix, ''), v) + for k, v in xenstore_data.iteritems() if k.startswith(key_prefix) ]) + + self.assertEquals(tcpip_data, { + 'BroadcastAddress/data/0': '10.0.0.255', + 'BroadcastAddress/name': 'BroadcastAddress', + 'BroadcastAddress/type': 'multi_sz', + 'DefaultGateway/data/0': '10.0.0.1', + 'DefaultGateway/name': 'DefaultGateway', + 'DefaultGateway/type': 'multi_sz', + 'EnableDhcp/data': '0', + 'EnableDhcp/name': 'EnableDhcp', + 'EnableDhcp/type': 'dword', + 'IPAddress/data/0': '10.0.0.3', + 'IPAddress/name': 'IPAddress', + 'IPAddress/type': 'multi_sz', + 'NameServer/data': '10.0.0.2', + 'NameServer/name': 'NameServer', + 'NameServer/type': 'string', + 'SubnetMask/data/0': '255.255.255.0', + 'SubnetMask/name': 'SubnetMask', + 'SubnetMask/type': 'multi_sz' + }) + + def _test_spawn(self, image_id, kernel_id, ramdisk_id, check_injection = False): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) values = {'name': 1, 'id': 1, @@ -258,7 +287,7 @@ class XenAPIVMTestCase(test.TestCase): conn = xenapi_conn.get_connection(False) instance = db.instance_create(values) conn.spawn(instance) - self.check_vm_record(conn) + self.check_vm_record(conn, check_injection = check_injection) def test_spawn_raw_objectstore(self): FLAGS.xenapi_image_service = 'objectstore' @@ -276,6 +305,11 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) + def test_spawn_netinject(self): + FLAGS.xenapi_image_service = 'glance' + db_fakes.stub_out_db_network_api(self.stubs, injected = True) + self._test_spawn(1, 2, 3, check_injection = True) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 624995ada..3cfc8e5f9 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -171,6 +171,21 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_destroy(self, session_ref, vm_ref): fake.destroy_vm(vm_ref) + def VM_get_VIFs(self, session_ref, vm_ref): + return (['0', '1', '2']) + + def VIF_get_device(self, session_ref, vif_ref): + return ('1', '0', '2')[int(vif_ref)] + + def VIF_get_MAC(self, session_ref, vif_ref): + return ( + '11:22:2a:b3:CC:dd', + '22:33:2a:b3:CC:dd', + '44:44:2a:b3:CC:dd' + )[int(vif_ref)] + + def VM_add_to_xenstore_data(self, session_ref, vm_ref, key, value): + fake.VM_add_to_xenstore_data(vm_ref, key, value) class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 836252730..f9ae8766a 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -148,7 +148,15 @@ def create_vbd(vm_ref, vdi_ref): after_VBD_create(vbd_ref, vbd_rec) return vbd_ref - +def VM_get_xenstore_data(vm_ref): + return _db_content['VM'][vm_ref].get('xenstore_data', '') + +def VM_add_to_xenstore_data(vm_ref, key, value): + db_ref = _db_content['VM'][vm_ref] + if not 'xenstore_data' in db_ref: + db_ref['xenstore_data'] = {} + db_ref['xenstore_data'][key] = value + def after_VBD_create(vbd_ref, vbd_rec): """Create read-only fields and backref from VM to VBD when VBD is created.""" @@ -401,7 +409,7 @@ class SessionBase(object): field in _db_content[cls][ref]): return _db_content[cls][ref][field] - LOG.debuug(_('Raising NotImplemented')) + LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( _('xenapi.fake does not have an implementation for %s or it has ' 'been called with the wrong number of arguments') % name) -- cgit From 48990b109eb39f0dd4ea7bf86be79f6e03c3ad74 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Fri, 21 Jan 2011 16:23:30 +0000 Subject: OS55: pylint fixes --- nova/tests/test_xenapi.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 2a32845d3..c94aeefd1 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -249,7 +249,8 @@ class XenAPIVMTestCase(test.TestCase): xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref) key_prefix = 'vm-data/vif/22_33_2A_B3_CC_DD/tcpip/' tcpip_data = dict([(k.replace(key_prefix, ''), v) - for k, v in xenstore_data.iteritems() if k.startswith(key_prefix) ]) + for k, v in xenstore_data.iteritems() + if k.startswith(key_prefix) ]) self.assertEquals(tcpip_data, { 'BroadcastAddress/data/0': '10.0.0.255', @@ -272,7 +273,8 @@ class XenAPIVMTestCase(test.TestCase): 'SubnetMask/type': 'multi_sz' }) - def _test_spawn(self, image_id, kernel_id, ramdisk_id, check_injection = False): + def _test_spawn(self, image_id, kernel_id, ramdisk_id, + check_injection = False): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) values = {'name': 1, 'id': 1, -- cgit From c97618e1eaff4091f01381073a298d0f67050126 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Mon, 24 Jan 2011 18:28:50 +0000 Subject: OS-55: Post-merge fixes --- nova/tests/db/fakes.py | 3 ++- nova/virt/xenapi/vm_utils.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index e92efbedd..4a0d1cc3c 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -79,7 +79,8 @@ def stub_out_db_network_api(stubs, injected = False): 'netmask': '255.255.255.0', 'gateway': '10.0.0.1', 'broadcast': '10.0.0.255', - 'dns': '10.0.0.2' + 'dns': '10.0.0.2', + 'ra_server': None }) def fake_network_get_by_instance(context, instance_id): diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 9b78a178a..68946a2bb 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -36,9 +36,9 @@ from nova import flags from nova import log as logging from nova import utils from nova.auth.manager import AuthManager -from nova.compute import disk from nova.compute import instance_types from nova.compute import power_state +from nova.virt import disk from nova.virt import images from nova.virt.xenapi import HelperBase from nova.virt import conn_common -- cgit From cd346a2cda13833f976b9e838d67cf17c52f327e Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Mon, 24 Jan 2011 19:04:25 +0000 Subject: OS-55: PEP8 fixes --- nova/tests/db/fakes.py | 17 +++++----- nova/tests/test_xenapi.py | 19 +++++------ nova/tests/xenapi/stubs.py | 10 +++--- nova/virt/conn_common.py | 3 +- nova/virt/disk.py | 2 ++ nova/virt/xenapi/fake.py | 9 +++-- nova/virt/xenapi/vm_utils.py | 80 ++++++++++++++++++++++---------------------- nova/virt/xenapi/vmops.py | 6 ++-- 8 files changed, 77 insertions(+), 69 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 4a0d1cc3c..c47fba5f3 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -23,6 +23,7 @@ from nova import db from nova import utils from nova.compute import instance_types + class FakeModel(object): """ Stubs out for model """ def __init__(self, values): @@ -37,6 +38,7 @@ class FakeModel(object): else: raise NotImplementedError() + def stub_out_db_instance_api(stubs): """ Stubs out the db API for creating Instances """ @@ -67,12 +69,12 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) -def stub_out_db_network_api(stubs, injected = False): + +def stub_out_db_network_api(stubs, injected=False): """Stubs out the db API for retrieving networks""" network_fields = { - 'bridge': 'xenbr0', - 'injected': injected - } + 'bridge': 'xenbr0', + 'injected': injected} if injected: network_fields.update({ @@ -80,8 +82,7 @@ def stub_out_db_network_api(stubs, injected = False): 'gateway': '10.0.0.1', 'broadcast': '10.0.0.255', 'dns': '10.0.0.2', - 'ra_server': None - }) + 'ra_server': None}) def fake_network_get_by_instance(context, instance_id): return FakeModel(network_fields) @@ -90,5 +91,5 @@ def stub_out_db_network_api(stubs, injected = False): return '10.0.0.3' stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) - stubs.Set(db, 'instance_get_fixed_address', fake_instance_get_fixed_address) - + stubs.Set(db, 'instance_get_fixed_address', + fake_instance_get_fixed_address) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index c94aeefd1..f7f7102a8 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -213,7 +213,7 @@ class XenAPIVMTestCase(test.TestCase): check() - def check_vm_record(self, conn, check_injection = False): + def check_vm_record(self, conn, check_injection=False): instances = conn.list_instances() self.assertEquals(instances, [1]) @@ -244,13 +244,13 @@ class XenAPIVMTestCase(test.TestCase): # Check that the VM is running according to XenAPI. self.assertEquals(vm['power_state'], 'Running') - + if check_injection: xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref) key_prefix = 'vm-data/vif/22_33_2A_B3_CC_DD/tcpip/' tcpip_data = dict([(k.replace(key_prefix, ''), v) for k, v in xenstore_data.iteritems() - if k.startswith(key_prefix) ]) + if k.startswith(key_prefix)]) self.assertEquals(tcpip_data, { 'BroadcastAddress/data/0': '10.0.0.255', @@ -270,11 +270,10 @@ class XenAPIVMTestCase(test.TestCase): 'NameServer/type': 'string', 'SubnetMask/data/0': '255.255.255.0', 'SubnetMask/name': 'SubnetMask', - 'SubnetMask/type': 'multi_sz' - }) + 'SubnetMask/type': 'multi_sz'}) def _test_spawn(self, image_id, kernel_id, ramdisk_id, - check_injection = False): + check_injection=False): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) values = {'name': 1, 'id': 1, @@ -289,7 +288,7 @@ class XenAPIVMTestCase(test.TestCase): conn = xenapi_conn.get_connection(False) instance = db.instance_create(values) conn.spawn(instance) - self.check_vm_record(conn, check_injection = check_injection) + self.check_vm_record(conn, check_injection=check_injection) def test_spawn_raw_objectstore(self): FLAGS.xenapi_image_service = 'objectstore' @@ -309,9 +308,9 @@ class XenAPIVMTestCase(test.TestCase): def test_spawn_netinject(self): FLAGS.xenapi_image_service = 'glance' - db_fakes.stub_out_db_network_api(self.stubs, injected = True) - self._test_spawn(1, 2, 3, check_injection = True) - + db_fakes.stub_out_db_network_api(self.stubs, injected=True) + self._test_spawn(1, 2, 3, check_injection=True) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 3cfc8e5f9..027855592 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -173,20 +173,20 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_get_VIFs(self, session_ref, vm_ref): return (['0', '1', '2']) - + def VIF_get_device(self, session_ref, vif_ref): return ('1', '0', '2')[int(vif_ref)] - + def VIF_get_MAC(self, session_ref, vif_ref): return ( '11:22:2a:b3:CC:dd', '22:33:2a:b3:CC:dd', - '44:44:2a:b3:CC:dd' - )[int(vif_ref)] - + '44:44:2a:b3:CC:dd')[int(vif_ref)] + def VM_add_to_xenstore_data(self, session_ref, vm_ref, key, value): fake.VM_add_to_xenstore_data(vm_ref, key, value) + class FakeSessionForVolumeTests(fake.SessionBase): """ Stubs out a XenAPISession for Volume tests """ def __init__(self, uri): diff --git a/nova/virt/conn_common.py b/nova/virt/conn_common.py index bd9ed7794..5550b50c1 100644 --- a/nova/virt/conn_common.py +++ b/nova/virt/conn_common.py @@ -27,7 +27,8 @@ FLAGS = flags.FLAGS flags.DEFINE_string('injected_network_template', utils.abspath('virt/interfaces.template'), 'Template file for injected network') - + + def get_injectables(inst): key = str(inst['key_data']) net = None diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 88b05f0c0..21bb53369 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -154,6 +154,7 @@ def _allocate_device(): def _free_device(device): _DEVICES.append(device) + def inject_data_into_fs(fs, key, net, execute): """Injects data into a filesystem already mounted by the caller. Virt connections can call this directly if they mount their fs @@ -164,6 +165,7 @@ def inject_data_into_fs(fs, key, net, execute): if net: _inject_net_into_fs(net, fs, execute=execute) + def _inject_key_into_fs(key, fs, execute=None): """Add the given public ssh key to root's authorized_keys. diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index f9ae8766a..561e47911 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -148,15 +148,18 @@ def create_vbd(vm_ref, vdi_ref): after_VBD_create(vbd_ref, vbd_rec) return vbd_ref + def VM_get_xenstore_data(vm_ref): return _db_content['VM'][vm_ref].get('xenstore_data', '') - + + def VM_add_to_xenstore_data(vm_ref, key, value): db_ref = _db_content['VM'][vm_ref] if not 'xenstore_data' in db_ref: db_ref['xenstore_data'] = {} db_ref['xenstore_data'][key] = value - + + def after_VBD_create(vbd_ref, vbd_rec): """Create read-only fields and backref from VM to VBD when VBD is created.""" @@ -169,11 +172,13 @@ def after_VBD_create(vbd_ref, vbd_rec): vm_name_label = _db_content['VM'][vm_ref]['name_label'] vbd_rec['vm_name_label'] = vm_name_label + def after_VM_create(vm_ref, vm_rec): """Create read-only fields in the VM record.""" if 'is_control_domain' not in vm_rec: vm_rec['is_control_domain'] = False + def create_pbd(config, host_ref, sr_ref, attached): return _create_object('PBD', { 'device-config': config, diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 68946a2bb..fa60c44c3 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -448,7 +448,7 @@ class VMHelper(HelperBase): def preconfigure_instance(cls, session, instance, vdi_ref): """Makes alterations to the image before launching as part of spawn. """ - + # As mounting the image VDI is expensive, we only want do do it once, # if at all, so determine whether it's required first, and then do # everything @@ -456,12 +456,13 @@ class VMHelper(HelperBase): key, net = conn_common.get_injectables(instance) if key is not None or net is not None: mount_required = True - + if mount_required: + def _mounted_processing(device): """Callback which runs with the image VDI attached""" - - dev_path = '/dev/'+device+'1' # NB: Partition 1 hardcoded + + dev_path = '/dev/' + device + '1' # NB: Partition 1 hardcoded tmpdir = tempfile.mkdtemp() try: # Mount only Linux filesystems, to avoid disturbing @@ -478,9 +479,9 @@ class VMHelper(HelperBase): else: try: # This try block ensures that the umount occurs - - xe_update_networking_filename = os.path.join(tmpdir, - 'usr', 'sbin', 'xe-update-networking') + + xe_update_networking_filename = os.path.join( + tmpdir, 'usr', 'sbin', 'xe-update-networking') if os.path.isfile(xe_update_networking_filename): # The presence of the xe-update-networking # file indicates that this guest agent can @@ -489,14 +490,15 @@ class VMHelper(HelperBase): # required LOG.info(_('XenServer tools installed in this ' 'image are capable of network injection. ' - 'Networking files will not be manipulated')) + 'Networking files will not be' + 'manipulated')) else: - xe_daemon_filename = os.path.join(tmpdir, 'usr', - 'sbin', 'xe-daemon') + xe_daemon_filename = os.path.join(tmpdir, + 'usr', 'sbin', 'xe-daemon') if os.path.isfile(xe_daemon_filename): - LOG.info(_('XenServer tools are present in ' - 'this image but are not capable of ' - 'network injection')) + LOG.info(_('XenServer tools are present ' + 'in this image but are not capable ' + 'of network injection')) else: LOG.info(_('XenServer tools are not ' 'installed in this image')) @@ -510,24 +512,23 @@ class VMHelper(HelperBase): # remove temporary directory os.rmdir(tmpdir) - with_vdi_attached_here(session, vdi_ref, False, _mounted_processing) - - + with_vdi_attached_here(session, vdi_ref, False, + _mounted_processing) + @classmethod def preconfigure_xenstore(cls, session, instance, vm_ref): """Sets xenstore values to modify the image behaviour after VM start. """ - + xenstore_types = { - 'BroadcastAddress' : 'multi_sz', - 'DefaultGateway' : 'multi_sz', - 'EnableDhcp' : 'dword', - 'IPAddress' : 'multi_sz', - 'NameServer' : 'string', - 'SubnetMask' : 'multi_sz' - } - + 'BroadcastAddress': 'multi_sz', + 'DefaultGateway': 'multi_sz', + 'EnableDhcp': 'dword', + 'IPAddress': 'multi_sz', + 'NameServer': 'string', + 'SubnetMask': 'multi_sz'} + # Network setup network_ref = db.network_get_by_instance( context.get_admin_context(), instance['id']) @@ -535,8 +536,8 @@ class VMHelper(HelperBase): admin_context = context.get_admin_context() address = db.instance_get_fixed_address(admin_context, instance['id']) - - xenstore_data = { + + xenstore_data = { # NB: Setting broadcast address is not supported by # Windows or the Windows guest agent, and will be ignored # on that platform @@ -545,30 +546,29 @@ class VMHelper(HelperBase): 'IPAddress': address, 'SubnetMask': network_ref['netmask'], 'DefaultGateway': network_ref['gateway'], - 'NameServer': network_ref['dns'] - } - - device_to_configure = 0 # Configure network device 0 in the VM - + 'NameServer': network_ref['dns']} + + device_to_configure = 0 # Configure network device 0 in the VM + vif_refs = session.call_xenapi('VM.get_VIFs', vm_ref) mac_addr = None - + for vif_ref in vif_refs: device = session.call_xenapi('VIF.get_device', vif_ref) if str(device) == str(device_to_configure): mac_addr = session.call_xenapi('VIF.get_MAC', vif_ref) break - + if mac_addr is None: raise exception.NotFound(_('Networking device %s not found ' 'in VM')) - + # MAC address must be upper case in the xenstore key, # with colons replaced by underscores underscore_mac_addr = mac_addr.replace(':', '_') xenstore_prefix = ('vm-data/vif/' + underscore_mac_addr.upper() + '/tcpip/') - + for xenstore_key, xenstore_value in xenstore_data.iteritems(): # NB: The xenstore_key part of the instance_key isn't used but # must be unique. We set it to xenstore_key as a convenient @@ -578,15 +578,15 @@ class VMHelper(HelperBase): key_type = xenstore_types[xenstore_key] session.call_xenapi('VM.add_to_xenstore_data', vm_ref, - instance_key+'/name', xenstore_key) + instance_key + '/name', xenstore_key) session.call_xenapi('VM.add_to_xenstore_data', vm_ref, - instance_key+'/type', key_type) + instance_key + '/type', key_type) if key_type == 'multi_sz': session.call_xenapi('VM.add_to_xenstore_data', vm_ref, - instance_key+'/data/0', xenstore_value) + instance_key + '/data/0', xenstore_value) else: session.call_xenapi('VM.add_to_xenstore_data', vm_ref, - instance_key+'/data', xenstore_value) + instance_key + '/data', xenstore_value) @classmethod def compile_info(cls, record): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index df282807a..9eea19c42 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -105,14 +105,14 @@ class VMOps(object): if network_ref: VMHelper.create_vif(self._session, vm_ref, network_ref, instance.mac_address) - + # Alter the image before VM start for, e.g. network injection VMHelper.preconfigure_instance(self._session, instance, vdi_ref) - + # Configure the VM's xenstore data before start for, # e.g. network configuration VMHelper.preconfigure_xenstore(self._session, instance, vm_ref) - + LOG.debug(_('Starting VM %s...'), vm_ref) self._session.call_xenapi('VM.start', vm_ref, False, False) instance_name = instance.name -- cgit From 0f868b00dbb2de469dde3519f2370e59937c4fc6 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Wed, 26 Jan 2011 19:58:45 +0000 Subject: OS-55: Added a test case for XenAPI file-based network injection OS-55: Stubbed out utils.execute for all XenAPI VM tests, including command simulation where necessary --- nova/tests/fake_utils.py | 98 +++++++++++++++++++++++++++++++++++++++++++++++ nova/tests/test_xenapi.py | 83 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 nova/tests/fake_utils.py diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py new file mode 100644 index 000000000..f51d31e0c --- /dev/null +++ b/nova/tests/fake_utils.py @@ -0,0 +1,98 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright (c) 2011 Citrix Systems, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +"""This modules stubs out functions in nova.utils +""" + +import re +import types + +from eventlet import greenthread + +from nova import exception +from nova import log as logging +from nova import utils + +LOG = logging.getLogger('nova.tests.fake_utils') + +_fake_execute_repliers = [] +_fake_execute_log = [] + + +def fake_execute_get_log(): + global _fake_execute_log + return _fake_execute_log + + +def fake_execute_clear_log(): + global _fake_execute_log + _fake_execute_log = [] + + +def fake_execute_set_repliers(repliers): + """Allows the client to configure replies to commands""" + global _fake_execute_repliers + _fake_execute_repliers = repliers + + +def fake_execute_default_reply_handler(*ignore_args): + """A reply handler for commands that haven't been added to the reply + list. Returns empty strings for stdout and stderr + """ + return '', '' + + +def fake_execute(cmd, process_input=None, addl_env=None, check_exit_code=True): + """This function stubs out execute, optionally executing + a preconfigued function to return expected data + """ + global _fake_execute_repliers + + LOG.debug(_("Faking execution of cmd (subprocess): %s"), cmd) + _fake_execute_log.append(cmd) + + reply_handler = fake_execute_default_reply_handler + + for fake_replier in _fake_execute_repliers: + if re.match(fake_replier[0], cmd): + reply_handler = fake_replier[1] + LOG.debug(_('Faked command matched %s') % fake_replier[0]) + break + + if isinstance(reply_handler, types.StringTypes): + # If the reply handler is a string, return it as stdout + reply = reply_handler, '' + else: + try: + # Alternative is a function, so call it + reply = reply_handler(cmd, process_input, addl_env, + check_exit_code) + except exception.ProcessExecutionError as e: + LOG.debug(_('Faked command raised an exception %s' % str(e))) + raise + + LOG.debug(_("Reply to faked command is stdout='%(0)s' stderr='%(1)s'") % + {'0': reply[0], '1': reply[1]}) + + # Replicate the sleep call in the real function + greenthread.sleep(0) + return reply + + +def stub_out_utils_execute(stubs): + fake_execute_set_repliers([]) + fake_execute_clear_log() + stubs.Set(utils, 'execute', fake_execute) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index f7f7102a8..8af47da63 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -18,11 +18,14 @@ Test suite for XenAPI """ +import os +import re import stubout from nova import db from nova import context from nova import flags +from nova import log as logging from nova import test from nova import utils from nova.auth import manager @@ -35,6 +38,9 @@ from nova.virt.xenapi.vmops import SimpleDH from nova.tests.db import fakes as db_fakes from nova.tests.xenapi import stubs from nova.tests.glance import stubs as glance_stubs +from nova.tests import fake_utils + +LOG = logging.getLogger('nova.tests.test_xenapi') FLAGS = flags.FLAGS @@ -166,6 +172,7 @@ class XenAPIVMTestCase(test.TestCase): stubs.stubout_stream_disk(self.stubs) glance_stubs.stubout_glance_client(self.stubs, glance_stubs.FakeGlance) + fake_utils.stub_out_utils_execute(self.stubs) self.conn = xenapi_conn.get_connection(False) def test_list_instances_0(self): @@ -306,11 +313,85 @@ class XenAPIVMTestCase(test.TestCase): FLAGS.xenapi_image_service = 'glance' self._test_spawn(1, 2, 3) - def test_spawn_netinject(self): + def test_spawn_netinject_file(self): + FLAGS.xenapi_image_service = 'glance' + db_fakes.stub_out_db_network_api(self.stubs, injected=True) + + self._tee_executed = False + + def _tee_handler(cmd, input, *ignore_args): + self.assertNotEqual(input, None) + + config = [line.strip() for line in input.split("\n")] + + # Find the start of eth0 configuration and check it + index = config.index('auto eth0') + self.assertEquals(config[index + 1:index + 8], [ + 'iface eth0 inet static', + 'address 10.0.0.3', + 'netmask 255.255.255.0', + 'broadcast 10.0.0.255', + 'gateway 10.0.0.1', + 'dns-nameservers 10.0.0.2', + '']) + + self._tee_executed = True + + return '', '' + + fake_utils.fake_execute_set_repliers([ + # Capture the sudo tee .../etc/network/interfaces command + (r'(sudo\s+)?tee.*interfaces', _tee_handler), + ]) + self._test_spawn(1, 2, 3, check_injection=True) + self.assertTrue(self._tee_executed) + + def test_spawn_netinject_xenstore(self): FLAGS.xenapi_image_service = 'glance' db_fakes.stub_out_db_network_api(self.stubs, injected=True) + + self._tee_executed = False + + def _mount_handler(cmd, *ignore_args): + # When mounting, create real files under the mountpoint to simulate + # files in the mounted filesystem + + # RegExp extracts the path of the mountpoint + match = re.match(r'(sudo\s+)?mount[^"]*"[^"]*"\s+"([^"]*)"', cmd) + self._tmpdir = match.group(2) + LOG.debug(_('Creating files in %s to simulate guest agent' % + self._tmpdir)) + os.makedirs(os.path.join(self._tmpdir, 'usr', 'sbin')) + # Touch the file using open + open(os.path.join(self._tmpdir, 'usr', 'sbin', + 'xe-update-networking'), 'w').close() + return '', '' + + def _umount_handler(cmd, *ignore_args): + # Umount would normall make files in the m,ounted filesystem + # disappear, so do that here + LOG.debug(_('Removing simulated guest agent files in %s' % + self._tmpdir)) + os.remove(os.path.join(self._tmpdir, 'usr', 'sbin', + 'xe-update-networking')) + os.rmdir(os.path.join(self._tmpdir, 'usr', 'sbin')) + os.rmdir(os.path.join(self._tmpdir, 'usr')) + return '', '' + + def _tee_handler(cmd, input, *ignore_args): + self._tee_executed = True + return '', '' + + fake_utils.fake_execute_set_repliers([ + (r'(sudo\s+)?mount', _mount_handler), + (r'(sudo\s+)?umount', _umount_handler), + (r'(sudo\s+)?tee.*interfaces', _tee_handler)]) self._test_spawn(1, 2, 3, check_injection=True) + # tee must not run in this case, where an injection-capable + # guest agent is detected + self.assertFalse(self._tee_executed) + def tearDown(self): super(XenAPIVMTestCase, self).tearDown() self.manager.delete_project(self.project) -- cgit From 46456155d42dd8a668b370fa84972c388094e1d8 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Fri, 28 Jan 2011 18:40:19 +0000 Subject: OS-55: Fix typo for libvirt_conn operation --- nova/virt/disk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 21bb53369..98121df2a 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -92,7 +92,7 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False): % err) try: - inject_data_into_fs(tmpdir, key, net, execute) + inject_data_into_fs(tmpdir, key, net, utils.execute) finally: # unmount device utils.execute('sudo umount %s' % mapped_device) -- cgit From 2dfcfccd74821851c965ee2912fd315e25e7f838 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Tue, 15 Feb 2011 12:15:49 +0000 Subject: OS-55: Moved conn_common code into disk.py --- nova/virt/conn_common.py | 51 -------------------------------------------- nova/virt/disk.py | 27 +++++++++++++++++++++++ nova/virt/libvirt_conn.py | 3 +-- nova/virt/xenapi/vm_utils.py | 3 +-- 4 files changed, 29 insertions(+), 55 deletions(-) delete mode 100644 nova/virt/conn_common.py diff --git a/nova/virt/conn_common.py b/nova/virt/conn_common.py deleted file mode 100644 index 5550b50c1..000000000 --- a/nova/virt/conn_common.py +++ /dev/null @@ -1,51 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2010 Citrix Systems, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from nova import context -from nova import db -from nova import exception -from nova import flags -from nova import log as logging -from nova import utils - -LOG = logging.getLogger('nova.virt.conn_common') -FLAGS = flags.FLAGS - -flags.DEFINE_string('injected_network_template', - utils.abspath('virt/interfaces.template'), - 'Template file for injected network') - - -def get_injectables(inst): - key = str(inst['key_data']) - net = None - network_ref = db.network_get_by_instance(context.get_admin_context(), - inst['id']) - if network_ref['injected']: - admin_context = context.get_admin_context() - address = db.instance_get_fixed_address(admin_context, inst['id']) - ra_server = network_ref['ra_server'] - if not ra_server: - ra_server = "fd00::" - with open(FLAGS.injected_network_template) as f: - net = f.read() % {'address': address, - 'netmask': network_ref['netmask'], - 'gateway': network_ref['gateway'], - 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns'], - 'ra_server': ra_server} - - return key, net diff --git a/nova/virt/disk.py b/nova/virt/disk.py index 98121df2a..cee1ffbce 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -26,6 +26,8 @@ import os import tempfile import time +from nova import context +from nova import db from nova import exception from nova import flags from nova import log as logging @@ -38,6 +40,9 @@ flags.DEFINE_integer('minimum_root_size', 1024 * 1024 * 1024 * 10, 'minimum size in bytes of root partition') flags.DEFINE_integer('block_size', 1024 * 1024 * 256, 'block_size to use for dd') +flags.DEFINE_string('injected_network_template', + utils.abspath('virt/interfaces.template'), + 'Template file for injected network') def extend(image, size): @@ -155,6 +160,28 @@ def _free_device(device): _DEVICES.append(device) +def get_injectables(inst): + key = str(inst['key_data']) + net = None + network_ref = db.network_get_by_instance(context.get_admin_context(), + inst['id']) + if network_ref['injected']: + admin_context = context.get_admin_context() + address = db.instance_get_fixed_address(admin_context, inst['id']) + ra_server = network_ref['ra_server'] + if not ra_server: + ra_server = "fd00::" + with open(FLAGS.injected_network_template) as f: + net = f.read() % {'address': address, + 'netmask': network_ref['netmask'], + 'gateway': network_ref['gateway'], + 'broadcast': network_ref['broadcast'], + 'dns': network_ref['dns'], + 'ra_server': ra_server} + + return key, net + + def inject_data_into_fs(fs, key, net, execute): """Injects data into a filesystem already mounted by the caller. Virt connections can call this directly if they mount their fs diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 45d8754ab..806f35a81 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -62,7 +62,6 @@ from nova.compute import instance_types from nova.compute import power_state from nova.virt import disk from nova.virt import images -from nova.virt import conn_common libvirt = None libxml2 = None @@ -621,7 +620,7 @@ class LibvirtConnection(object): if not inst['kernel_id']: target_partition = "1" - key, net = conn_common.get_injectables(inst) + key, net = disk.get_injectables(inst) if key or net: inst_name = inst['name'] diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index fa60c44c3..603cef1f6 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -41,7 +41,6 @@ from nova.compute import power_state from nova.virt import disk from nova.virt import images from nova.virt.xenapi import HelperBase -from nova.virt import conn_common from nova.virt.xenapi.volume_utils import StorageError @@ -453,7 +452,7 @@ class VMHelper(HelperBase): # if at all, so determine whether it's required first, and then do # everything mount_required = False - key, net = conn_common.get_injectables(instance) + key, net = disk.get_injectables(instance) if key is not None or net is not None: mount_required = True -- cgit From 552875913e263d0e44be4613f0a07d3b53067e96 Mon Sep 17 00:00:00 2001 From: Andy Southgate Date: Wed, 16 Feb 2011 19:32:45 +0000 Subject: Fixed merge error --- nova/virt/xenapi/vm_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index c5347498d..4b9883d21 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -597,6 +597,7 @@ class VMHelper(HelperBase): session.call_xenapi('VM.add_to_xenstore_data', vm_ref, instance_key + '/data', xenstore_value) + @classmethod def lookup_kernel_ramdisk(cls, session, vm): vm_rec = session.get_xenapi().VM.get_record(vm) if 'PV_kernel' in vm_rec and 'PV_ramdisk' in vm_rec: -- cgit From 75190533d887a8824f234aa5e6185328f6f9b4f8 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Tue, 22 Feb 2011 19:54:42 +0000 Subject: Remove duplicate import gained across a merge. --- plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py index dbbce5c51..108459a27 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py @@ -28,8 +28,6 @@ import re import time import XenAPI -import XenAPI - ##### Logging setup def configure_logging(name): -- cgit From 9049ad18f0ad40361d936c2066bded3436082275 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Tue, 22 Feb 2011 20:12:30 +0000 Subject: Put the whitespace back *sigh* --- plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py index 108459a27..f51f5fce4 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/pluginlib_nova.py @@ -28,6 +28,7 @@ import re import time import XenAPI + ##### Logging setup def configure_logging(name): -- cgit From 8d1f22255ee968e0693ac0932677a4830a45e57e Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Tue, 22 Feb 2011 20:28:31 +0000 Subject: Added Andy Southgate to the Authors file. --- Authors | 1 + 1 file changed, 1 insertion(+) diff --git a/Authors b/Authors index 494e614a0..f638c3c09 100644 --- a/Authors +++ b/Authors @@ -1,4 +1,5 @@ Andy Smith +Andy Southgate Anne Gentle Anthony Young Antony Messerli -- cgit From d1a6cb96c1c72894cbba24e6806da5c81fb915df Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Tue, 22 Feb 2011 20:39:33 +0000 Subject: Removed block of code that resurrected itself in the last merge. --- nova/virt/xenapi/vmops.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 1b2712d15..c9cc8e698 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -108,10 +108,6 @@ class VMOps(object): instance, kernel, ramdisk, pv_kernel) VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) - if network_ref: - VMHelper.create_vif(self._session, vm_ref, - network_ref, instance.mac_address) - # Alter the image before VM start for, e.g. network injection VMHelper.preconfigure_instance(self._session, instance, vdi_ref) -- cgit From 72940957611bcba7dc41bfe9232743369d9a151f Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 28 Feb 2011 16:04:26 +0000 Subject: Fixed default value for xenapi_agent_path flag --- nova/virt/xenapi_conn.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 546a10d09..4fc69961d 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -106,7 +106,7 @@ flags.DEFINE_integer('xenapi_inject_image', ' data into the disk image should be made.' ' Used only if connection_type=xenapi.') flags.DEFINE_integer('xenapi_agent_path', - True, + '/usr/sbin/xe-update-networking' 'Specifies the path in which the xenapi guest agent' ' should be located. If the agent is present,' ' network configuration if not injected into the image' -- cgit From 026c83551fa2e07f7f20d6b163f7da93e331b084 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Mon, 28 Feb 2011 16:59:00 +0000 Subject: Fixed obvious errors with flags. Note: tests still fail. --- nova/virt/xenapi_conn.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4fc69961d..b9e87c2ce 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -100,13 +100,13 @@ flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', 5, 'Max number of times to poll for VHD to coalesce.' ' Used only if connection_type=xenapi.') -flags.DEFINE_integer('xenapi_inject_image', +flags.DEFINE_bool('xenapi_inject_image', True, 'Specifies whether an attempt to inject network/key' ' data into the disk image should be made.' ' Used only if connection_type=xenapi.') -flags.DEFINE_integer('xenapi_agent_path', - '/usr/sbin/xe-update-networking' +flags.DEFINE_string('xenapi_agent_path', + '/usr/sbin/xe-update-networking', 'Specifies the path in which the xenapi guest agent' ' should be located. If the agent is present,' ' network configuration if not injected into the image' -- cgit From 4e4711ccfc7ce3c3df704a8635dccd506d6e7f01 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 1 Mar 2011 00:28:59 +0000 Subject: Units tests fixed partially. Still need to address checking data injected into xenstore need to convert string into dict or similar. Also todo PEP8 fixes --- nova/tests/db/fakes.py | 41 +++++++++++++++++++++++++++++++---------- nova/tests/test_xenapi.py | 42 ++++++++++++++++++------------------------ nova/tests/xenapi/stubs.py | 3 +++ nova/virt/xenapi/fake.py | 9 +++++++++ nova/virt/xenapi/vm_utils.py | 3 ++- nova/virt/xenapi/vmops.py | 7 +++++++ nova/virt/xenapi_conn.py | 2 +- 7 files changed, 71 insertions(+), 36 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index c47fba5f3..b939f99a0 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -36,6 +36,8 @@ class FakeModel(object): if key in self.values: return self.values[key] else: + print "Key:%s" %key + print "Values:%s" %self.values raise NotImplementedError() @@ -70,26 +72,45 @@ def stub_out_db_instance_api(stubs): stubs.Set(db, 'instance_create', fake_instance_create) -def stub_out_db_network_api(stubs, injected=False): +def stub_out_db_network_api(stubs, injected=True): """Stubs out the db API for retrieving networks""" + network_fields = { + 'id': 'test', 'bridge': 'xenbr0', + 'label': 'test_network', + 'netmask': '255.255.255.0', + 'gateway': '10.0.0.1', + 'broadcast': '10.0.0.255', + 'dns': '10.0.0.2', + 'ra_server': None, 'injected': injected} - - if injected: - network_fields.update({ - 'netmask': '255.255.255.0', - 'gateway': '10.0.0.1', - 'broadcast': '10.0.0.255', - 'dns': '10.0.0.2', - 'ra_server': None}) + + fixed_ip_fields = { + 'address':'10.0.0.3', + 'network_id':'test'} def fake_network_get_by_instance(context, instance_id): return FakeModel(network_fields) + def fake_network_get_all_by_instance(context, instance_id): + l = [] + l.append(FakeModel(network_fields)) + return l + def fake_instance_get_fixed_address(context, instance_id): - return '10.0.0.3' + return FakeModel(fixed_ip_fields).address + + def fake_fixed_ip_get_all_by_instance(context, instance_id): + l = [] + l.append(FakeModel(fixed_ip_fields)) + return l stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) stubs.Set(db, 'instance_get_fixed_address', fake_instance_get_fixed_address) + stubs.Set(db, 'network_get_all_by_instance', + fake_network_get_all_by_instance) + stubs.Set(db, 'fixed_ip_get_all_by_instance', + fake_fixed_ip_get_all_by_instance) + \ No newline at end of file diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index ce89a53c8..10a1b6c11 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -261,30 +261,21 @@ class XenAPIVMTestCase(test.TestCase): if check_injection: xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref) - key_prefix = 'vm-data/vif/22_33_2A_B3_CC_DD/tcpip/' - tcpip_data = dict([(k.replace(key_prefix, ''), v) - for k, v in xenstore_data.iteritems() - if k.startswith(key_prefix)]) - - self.assertEquals(tcpip_data, { - 'BroadcastAddress/data/0': '10.0.0.255', - 'BroadcastAddress/name': 'BroadcastAddress', - 'BroadcastAddress/type': 'multi_sz', - 'DefaultGateway/data/0': '10.0.0.1', - 'DefaultGateway/name': 'DefaultGateway', - 'DefaultGateway/type': 'multi_sz', - 'EnableDhcp/data': '0', - 'EnableDhcp/name': 'EnableDhcp', - 'EnableDhcp/type': 'dword', - 'IPAddress/data/0': '10.0.0.3', - 'IPAddress/name': 'IPAddress', - 'IPAddress/type': 'multi_sz', - 'NameServer/data': '10.0.0.2', - 'NameServer/name': 'NameServer', - 'NameServer/type': 'string', - 'SubnetMask/data/0': '255.255.255.0', - 'SubnetMask/name': 'SubnetMask', - 'SubnetMask/type': 'multi_sz'}) + key = 'vm-data/networking/aabbccddeeff' + LOG.debug("Xenstore data: %s",xenstore_data) + xenstore_value=xenstore_data[key] + #tcpip_data = dict([(k, v) + # for k, v in xenstore_value.iteritems() + # if k.startswith(key_prefix)]) + #LOG.debug("tcpip data: %s",tcpip_data) + #self.assertEquals(tcpip_data['label'],'test_network') + #self.assertEquals(tcpip_data, { + # 'label': 'test_network', + # 'broadcast': '10.0.0.255', + # 'ips': [{'ip': '10.0.0.3', 'netmask':'255.255.255.0', 'enabled':'1'}], + # 'mac': 'aa:bb:cc:dd:ee:ff', + # 'dns': ['10.0.0.2'], + # 'gateway': '10.0.0.1'}) def _test_spawn(self, image_id, kernel_id, ramdisk_id, instance_type="m1.large", check_injection=False): @@ -340,6 +331,9 @@ class XenAPIVMTestCase(test.TestCase): # Find the start of eth0 configuration and check it index = config.index('auto eth0') + LOG.debug("CONFIG") + LOG.debug(config) + self.assertEquals(config[index + 1:index + 8], [ 'iface eth0 inet static', 'address 10.0.0.3', diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 6981b4f7a..551d326a4 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -191,6 +191,9 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_add_to_xenstore_data(self, session_ref, vm_ref, key, value): fake.VM_add_to_xenstore_data(vm_ref, key, value) + + def VM_remove_from_xenstore_data(self, session_ref, vm_ref, key): + fake.VM_remove_from_xenstore_data(vm_ref, key) class FakeSessionForVolumeTests(fake.SessionBase): diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 08e2a03f7..664cfbd79 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -153,7 +153,14 @@ def VM_get_xenstore_data(vm_ref): return _db_content['VM'][vm_ref].get('xenstore_data', '') +def VM_remove_from_xenstore_data(vm_ref, key): + db_ref = _db_content['VM'][vm_ref] + if not 'xenstore_data' in db_ref: + return + db_ref['xenstore_data'][key] = None + def VM_add_to_xenstore_data(vm_ref, key, value): + LOG.debug("ADDING TO XENSTORE DATA %s %s",key,value) db_ref = _db_content['VM'][vm_ref] if not 'xenstore_data' in db_ref: db_ref['xenstore_data'] = {} @@ -503,7 +510,9 @@ class SessionBase(object): def _get_by_field(self, recs, k, v, return_singleton): result = [] + LOG.debug("_get_by_field!!!! - %d", return_singleton) for ref, rec in recs.iteritems(): + LOG.debug("k:%s,rec[k]:%s,v:%s",k,rec.get(k),v) if rec.get(k) == v: result.append(ref) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 0434f745d..a01bab8de 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -409,8 +409,10 @@ class VMHelper(HelperBase): @classmethod def lookup(cls, session, i): """Look the instance i up, and returns it if available""" + LOG.debug("Entering lookup for instance:%s",str(i)) vms = session.get_xenapi().VM.get_by_name_label(i) n = len(vms) + LOG.debug("n:%d",n) if n == 0: return None elif n > 1: @@ -480,7 +482,6 @@ class VMHelper(HelperBase): else: try: # This try block ensures that the umount occurs - xe_guest_agent_filename = os.path.join( tmpdir, FLAGS.xenapi_agent_path) if os.path.isfile(xe_guest_agent_filename): diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index cc84b7032..c137d4931 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -117,6 +117,7 @@ class VMOps(object): VMHelper.preconfigure_instance(self._session, instance, vdi_ref) # inject_network_info and create vifs + LOG.debug("About to run inject_network_info") networks = self.inject_network_info(instance) self.create_vifs(instance, networks) @@ -186,6 +187,7 @@ class VMOps(object): # Must be the instance name instance_name = instance_or_vm except (AttributeError, KeyError): + # # Note the the KeyError will only happen with fakes.py # Not a string; must be an ID or a vm instance if isinstance(instance_or_vm, (int, long)): @@ -201,8 +203,12 @@ class VMOps(object): instance_name = instance_or_vm else: instance_name = instance_or_vm.name + #fake xenapi does not use OpaqueRef as a prefix + #when running tests we will always end up here vm = VMHelper.lookup(self._session, instance_name) if vm is None: + if FLAGS.xenapi_connection_url == 'test_url': + return instance_or_vm raise exception.NotFound( _('Instance not present %s') % instance_name) return vm @@ -496,6 +502,7 @@ class VMOps(object): 'ips': [ip_dict(ip) for ip in network_IPs]} self.write_to_param_xenstore(vm_opaque_ref, {location: mapping}) try: + logging.debug("About to run write_to_xenstore") self.write_to_xenstore(vm_opaque_ref, location, mapping['location']) except KeyError: diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index b9e87c2ce..9f8b6af02 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -106,7 +106,7 @@ flags.DEFINE_bool('xenapi_inject_image', ' data into the disk image should be made.' ' Used only if connection_type=xenapi.') flags.DEFINE_string('xenapi_agent_path', - '/usr/sbin/xe-update-networking', + 'usr/sbin/xe-update-networking', 'Specifies the path in which the xenapi guest agent' ' should be located. If the agent is present,' ' network configuration if not injected into the image' -- cgit From 8806858918f396cfca41a28c191dc9e8d2809a0e Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 1 Mar 2011 01:10:38 +0000 Subject: Fixed xenapi tests Gave up on clever things with map stored as string in xenstore. Used ast.liteeral_eval instead. Changed instance ID and names in tests from in to string (1 => '1') This simplified VMOps._get_vm_opaqueref Changed VMOps._get_vm_opaqueref as references returned by fake xenapi do not start with "OpaqueRef:" prefix. Fixed PEP8 Errors --- nova/tests/db/fakes.py | 13 +++++-------- nova/tests/test_xenapi.py | 46 ++++++++++++++++++++------------------------ nova/tests/xenapi/stubs.py | 2 +- nova/virt/xenapi/fake.py | 6 ++---- nova/virt/xenapi/vm_utils.py | 8 -------- nova/virt/xenapi/vmops.py | 15 +++------------ 6 files changed, 32 insertions(+), 58 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index b939f99a0..7aa72e4a3 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -36,8 +36,6 @@ class FakeModel(object): if key in self.values: return self.values[key] else: - print "Key:%s" %key - print "Values:%s" %self.values raise NotImplementedError() @@ -74,7 +72,7 @@ def stub_out_db_instance_api(stubs): def stub_out_db_network_api(stubs, injected=True): """Stubs out the db API for retrieving networks""" - + network_fields = { 'id': 'test', 'bridge': 'xenbr0', @@ -83,12 +81,12 @@ def stub_out_db_network_api(stubs, injected=True): 'gateway': '10.0.0.1', 'broadcast': '10.0.0.255', 'dns': '10.0.0.2', - 'ra_server': None, + 'ra_server': None, 'injected': injected} - + fixed_ip_fields = { - 'address':'10.0.0.3', - 'network_id':'test'} + 'address': '10.0.0.3', + 'network_id': 'test'} def fake_network_get_by_instance(context, instance_id): return FakeModel(network_fields) @@ -113,4 +111,3 @@ def stub_out_db_network_api(stubs, injected=True): fake_network_get_all_by_instance) stubs.Set(db, 'fixed_ip_get_all_by_instance', fake_fixed_ip_get_all_by_instance) - \ No newline at end of file diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 10a1b6c11..a3d3371a1 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -21,6 +21,7 @@ Test suite for XenAPI import os import re import stubout +import ast from nova import db from nova import context @@ -204,7 +205,7 @@ class XenAPIVMTestCase(test.TestCase): if not vm_rec["is_control_domain"]: vm_labels.append(vm_rec["name_label"]) - self.assertEquals(vm_labels, [1]) + self.assertEquals(vm_labels, ['1']) def ensure_vbd_was_torn_down(): vbd_labels = [] @@ -212,7 +213,7 @@ class XenAPIVMTestCase(test.TestCase): vbd_rec = xenapi_fake.get_record('VBD', vbd_ref) vbd_labels.append(vbd_rec["vm_name_label"]) - self.assertEquals(vbd_labels, [1]) + self.assertEquals(vbd_labels, ['1']) def ensure_vdi_was_torn_down(): for vdi_ref in xenapi_fake.get_all('VDI'): @@ -229,10 +230,10 @@ class XenAPIVMTestCase(test.TestCase): def check_vm_record(self, conn, check_injection=False): instances = conn.list_instances() - self.assertEquals(instances, [1]) + self.assertEquals(instances, ['1']) # Get Nova record for VM - vm_info = conn.get_info(1) + vm_info = conn.get_info('1') # Get XenAPI record for VM vms = [(ref, rec) for ref, rec @@ -262,27 +263,24 @@ class XenAPIVMTestCase(test.TestCase): if check_injection: xenstore_data = xenapi_fake.VM_get_xenstore_data(vm_ref) key = 'vm-data/networking/aabbccddeeff' - LOG.debug("Xenstore data: %s",xenstore_data) - xenstore_value=xenstore_data[key] - #tcpip_data = dict([(k, v) - # for k, v in xenstore_value.iteritems() - # if k.startswith(key_prefix)]) - #LOG.debug("tcpip data: %s",tcpip_data) - #self.assertEquals(tcpip_data['label'],'test_network') - #self.assertEquals(tcpip_data, { - # 'label': 'test_network', - # 'broadcast': '10.0.0.255', - # 'ips': [{'ip': '10.0.0.3', 'netmask':'255.255.255.0', 'enabled':'1'}], - # 'mac': 'aa:bb:cc:dd:ee:ff', - # 'dns': ['10.0.0.2'], - # 'gateway': '10.0.0.1'}) + xenstore_value = xenstore_data[key] + tcpip_data = ast.literal_eval(xenstore_value) + self.assertEquals(tcpip_data, { + 'label': 'test_network', + 'broadcast': '10.0.0.255', + 'ips': [{'ip': '10.0.0.3', + 'netmask':'255.255.255.0', + 'enabled':'1'}], + 'mac': 'aa:bb:cc:dd:ee:ff', + 'dns': ['10.0.0.2'], + 'gateway': '10.0.0.1'}) def _test_spawn(self, image_id, kernel_id, ramdisk_id, instance_type="m1.large", check_injection=False): stubs.stubout_session(self.stubs, stubs.FakeSessionForVMTests) - values = {'name': 1, - 'id': 1, + values = {'name': "1", + 'id': "1", 'project_id': self.project.id, 'user_id': self.user.id, 'image_id': image_id, @@ -331,9 +329,7 @@ class XenAPIVMTestCase(test.TestCase): # Find the start of eth0 configuration and check it index = config.index('auto eth0') - LOG.debug("CONFIG") - LOG.debug(config) - + self.assertEquals(config[index + 1:index + 8], [ 'iface eth0 inet static', 'address 10.0.0.3', @@ -409,8 +405,8 @@ class XenAPIVMTestCase(test.TestCase): def _create_instance(self): """Creates and spawns a test instance""" values = { - 'name': 1, - 'id': 1, + 'name': '1', + 'id': '1', 'project_id': self.project.id, 'user_id': self.user.id, 'image_id': 1, diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 551d326a4..2392a97b3 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -191,7 +191,7 @@ class FakeSessionForVMTests(fake.SessionBase): def VM_add_to_xenstore_data(self, session_ref, vm_ref, key, value): fake.VM_add_to_xenstore_data(vm_ref, key, value) - + def VM_remove_from_xenstore_data(self, session_ref, vm_ref, key): fake.VM_remove_from_xenstore_data(vm_ref, key) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 664cfbd79..7aa82211d 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -156,11 +156,11 @@ def VM_get_xenstore_data(vm_ref): def VM_remove_from_xenstore_data(vm_ref, key): db_ref = _db_content['VM'][vm_ref] if not 'xenstore_data' in db_ref: - return + return db_ref['xenstore_data'][key] = None + def VM_add_to_xenstore_data(vm_ref, key, value): - LOG.debug("ADDING TO XENSTORE DATA %s %s",key,value) db_ref = _db_content['VM'][vm_ref] if not 'xenstore_data' in db_ref: db_ref['xenstore_data'] = {} @@ -510,9 +510,7 @@ class SessionBase(object): def _get_by_field(self, recs, k, v, return_singleton): result = [] - LOG.debug("_get_by_field!!!! - %d", return_singleton) for ref, rec in recs.iteritems(): - LOG.debug("k:%s,rec[k]:%s,v:%s",k,rec.get(k),v) if rec.get(k) == v: result.append(ref) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index a01bab8de..510261b15 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -409,10 +409,8 @@ class VMHelper(HelperBase): @classmethod def lookup(cls, session, i): """Look the instance i up, and returns it if available""" - LOG.debug("Entering lookup for instance:%s",str(i)) vms = session.get_xenapi().VM.get_by_name_label(i) n = len(vms) - LOG.debug("n:%d",n) if n == 0: return None elif n > 1: @@ -451,20 +449,17 @@ class VMHelper(HelperBase): # As mounting the image VDI is expensive, we only want do do it once, # if at all, so determine whether it's required first, and then do # everything - LOG.debug("Running preconfigure_instance") mount_required = False key, net = disk.get_injectables(instance) if key is not None or net is not None: mount_required = True - LOG.debug("Mount_required:%s", str(mount_required)) if mount_required: def _mounted_processing(device): """Callback which runs with the image VDI attached""" dev_path = '/dev/' + device + '1' # NB: Partition 1 hardcoded - LOG.debug("Device path:%s", dev_path) tmpdir = tempfile.mkdtemp() try: # Mount only Linux filesystems, to avoid disturbing @@ -473,7 +468,6 @@ class VMHelper(HelperBase): out, err = utils.execute( 'sudo mount -t ext2,ext3 "%s" "%s"' % (dev_path, tmpdir)) - LOG.debug("filesystem mounted") except exception.ProcessExecutionError as e: err = str(e) if err: @@ -506,7 +500,6 @@ class VMHelper(HelperBase): 'installed in this image')) LOG.info(_('Manipulating interface files ' 'directly')) - LOG.debug("Going to inject data in filesystem") disk.inject_data_into_fs(tmpdir, key, net, utils.execute) finally: @@ -748,7 +741,6 @@ def vbd_unplug_with_retry(session, vbd): # FIXME(sirp): We can use LoopingCall here w/o blocking sleep() while True: try: - LOG.debug("About to unplug VBD") session.get_xenapi().VBD.unplug(vbd) LOG.debug(_('VBD.unplug successful first time.')) return diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index c137d4931..a2f3a8f09 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -112,12 +112,10 @@ class VMOps(object): # Alter the image before VM start for, e.g. network injection #TODO(salvatore-orlando): do this only if flag is true - LOG.debug("About to run preconfigure_instance") if FLAGS.xenapi_inject_image: VMHelper.preconfigure_instance(self._session, instance, vdi_ref) # inject_network_info and create vifs - LOG.debug("About to run inject_network_info") networks = self.inject_network_info(instance) self.create_vifs(instance, networks) @@ -192,19 +190,12 @@ class VMOps(object): # Not a string; must be an ID or a vm instance if isinstance(instance_or_vm, (int, long)): ctx = context.get_admin_context() - try: - instance_obj = db.instance_get(ctx, instance_or_vm) - instance_name = instance_obj.name - except exception.NotFound: - # The unit tests screw this up, as they use an integer for - # the vm name. I'd fix that up, but that's a matter for - # another bug report. So for now, just try with the passed - # value - instance_name = instance_or_vm + instance_obj = db.instance_get(ctx, instance_or_vm) + instance_name = instance_obj.name else: instance_name = instance_or_vm.name #fake xenapi does not use OpaqueRef as a prefix - #when running tests we will always end up here + #when running tests we will always end up here vm = VMHelper.lookup(self._session, instance_name) if vm is None: if FLAGS.xenapi_connection_url == 'test_url': -- cgit From e0f1490e481e5b3e0e28b25049cc69eb905b74d6 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 2 Mar 2011 09:50:44 +0000 Subject: Removed excess TODO comments and debug line --- nova/virt/xenapi/vmops.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index a2f3a8f09..ec620f918 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -111,7 +111,6 @@ class VMOps(object): VMHelper.create_vbd(self._session, vm_ref, vdi_ref, 0, True) # Alter the image before VM start for, e.g. network injection - #TODO(salvatore-orlando): do this only if flag is true if FLAGS.xenapi_inject_image: VMHelper.preconfigure_instance(self._session, instance, vdi_ref) @@ -493,7 +492,6 @@ class VMOps(object): 'ips': [ip_dict(ip) for ip in network_IPs]} self.write_to_param_xenstore(vm_opaque_ref, {location: mapping}) try: - logging.debug("About to run write_to_xenstore") self.write_to_xenstore(vm_opaque_ref, location, mapping['location']) except KeyError: -- cgit From 97563d650a08e7f2d1aa1f08237219291d821e39 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Wed, 2 Mar 2011 10:43:45 +0000 Subject: Changed _get_vm_opaqueref removing test-specific code paths. --- nova/virt/xenapi/vmops.py | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ec620f918..450a06315 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -51,6 +51,7 @@ class VMOps(object): def __init__(self, session): self.XenAPI = session.get_imported_xenapi() self._session = session + self.known_vm_refs = [] VMHelper.XenAPI = self.XenAPI def list_instances(self): @@ -176,29 +177,16 @@ class VMOps(object): a vm name or a vm instance, and want a vm instance in return. """ vm = None - try: - if instance_or_vm.startswith("OpaqueRef:"): - # Got passed an opaque ref; return it + if instance_or_vm in self.known_vm_refs: return instance_or_vm - else: - # Must be the instance name - instance_name = instance_or_vm - except (AttributeError, KeyError): - # - # Note the the KeyError will only happen with fakes.py - # Not a string; must be an ID or a vm instance - if isinstance(instance_or_vm, (int, long)): - ctx = context.get_admin_context() - instance_obj = db.instance_get(ctx, instance_or_vm) - instance_name = instance_obj.name - else: - instance_name = instance_or_vm.name - #fake xenapi does not use OpaqueRef as a prefix - #when running tests we will always end up here + instance_name = instance_or_vm + #if instance_or_vm is not a string; + #must be an ID or a vm instance + if not isinstance(instance_or_vm, str): + instance_name = instance_or_vm.name vm = VMHelper.lookup(self._session, instance_name) + self.known_vm_refs.append(vm) if vm is None: - if FLAGS.xenapi_connection_url == 'test_url': - return instance_or_vm raise exception.NotFound( _('Instance not present %s') % instance_name) return vm @@ -482,7 +470,7 @@ class VMOps(object): mac_id = instance.mac_address.replace(':', '') location = 'vm-data/networking/%s' % mac_id - #TODO(salvatore-orlando): key for broadcast address + #salvatore-orlando: key for broadcast address #provisionally set to 'broadcast' mapping = {'label': network['label'], 'gateway': network['gateway'], -- cgit From e7626da8ade4a3d29d441fed1c21c50cbc9928de Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Tue, 8 Mar 2011 23:33:17 +0000 Subject: using get_uuid in place of get_record in _get_vm_opaqueref changed SessionBase._getter in fake xenapi in order to return HANDLE_INVALID failure when reference is not in DB (was NotImplementedException) --- nova/virt/xenapi/fake.py | 11 ++++++----- nova/virt/xenapi/vmops.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 09e381e2c..eecfa20b1 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -400,7 +400,6 @@ class SessionBase(object): def _getter(self, name, params): self._check_session(params) (cls, func) = name.split('.') - if func == 'get_all': self._check_arg_count(params, 1) return get_all(cls) @@ -423,10 +422,12 @@ class SessionBase(object): if len(params) == 2: field = func[len('get_'):] ref = params[1] - - if (ref in _db_content[cls] and - field in _db_content[cls][ref]): - return _db_content[cls][ref][field] + if (ref in _db_content[cls]): + if (field in _db_content[cls][ref]): + return _db_content[cls][ref][field] + else: + LOG.debug(_('Raising Failure')) + raise Failure(['HANDLE_INVALID', cls, ref]) LOG.debug(_('Raising NotImplemented')) raise NotImplementedError( diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index ddd5b0e16..273a7ba38 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -198,7 +198,7 @@ class VMOps(object): obj = None try: # check for opaque ref - obj = self._session.get_xenapi().VM.get_record(instance_or_vm) + obj = self._session.get_xenapi().VM.get_uuid(instance_or_vm) return instance_or_vm except self.XenAPI.Failure: # wasn't an opaque ref, can be an instance name -- cgit From 4f2d48eeffc79333afae829ea31f7eae0549b40a Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Sun, 13 Mar 2011 23:35:30 +0000 Subject: Removed excess LOG.debug line --- nova/tests/test_xenapi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index 85d804ebd..def37b377 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -268,7 +268,6 @@ class XenAPIVMTestCase(test.TestCase): mem_kib = long(instance_type['memory_mb']) << 10 mem_bytes = str(mem_kib << 10) vcpus = instance_type['vcpus'] - LOG.debug("VM:%s", self.vm) self.assertEquals(self.vm_info['max_mem'], mem_kib) self.assertEquals(self.vm_info['mem'], mem_kib) self.assertEquals(self.vm['memory_static_max'], mem_bytes) -- cgit From 745ade6a7759915eefe39eedc9be7e526df32547 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 18 Mar 2011 10:36:57 +0000 Subject: Fixed issue arisen from recent feature update (utils.execute) --- nova/virt/xenapi/vm_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index eaf5bb391..287a293f0 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -699,8 +699,8 @@ class VMHelper(HelperBase): try: out, err = utils.execute('sudo', 'mount', '-t', 'ext2,ext3', - '"%s"' % dev_path, - '"%s"' % tmpdir) + '%s' % dev_path, + '%s' % tmpdir) except exception.ProcessExecutionError as e: err = str(e) if err: -- cgit From 52bd70d500e7e82acea55c8d23c3fd1d66555cc0 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Thu, 24 Mar 2011 02:01:46 +0000 Subject: Addressing Rick Clark's comments. --- nova/tests/db/fakes.py | 4 +- nova/tests/fake_utils.py | 16 +++--- nova/tests/test_xenapi.py | 4 +- nova/tests/xenapi/stubs.py | 4 +- nova/virt/disk.py | 9 ++-- nova/virt/xenapi/fake.py | 1 - nova/virt/xenapi/vm_utils.py | 125 +++++++++++++++++++++++-------------------- nova/virt/xenapi/vmops.py | 1 - nova/virt/xenapi_conn.py | 20 +++---- 9 files changed, 95 insertions(+), 89 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 62c7cd794..c46b75aa2 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -99,9 +99,7 @@ def stub_out_db_instance_api(stubs, injected=True): return FakeModel(network_fields) def fake_network_get_all_by_instance(context, instance_id): - l = [] - l.append(FakeModel(network_fields)) - return l + return [FakeModel(network_fields)] def fake_instance_get_fixed_address(context, instance_id): return FakeModel(fixed_ip_fields).address diff --git a/nova/tests/fake_utils.py b/nova/tests/fake_utils.py index 8982f50be..823c775cb 100644 --- a/nova/tests/fake_utils.py +++ b/nova/tests/fake_utils.py @@ -33,7 +33,6 @@ _fake_execute_log = [] def fake_execute_get_log(): - global _fake_execute_log return _fake_execute_log @@ -55,7 +54,7 @@ def fake_execute_default_reply_handler(*ignore_args, **ignore_kwargs): return '', '' -def fake_execute(*cmd, **kwargs): +def fake_execute(*cmd_parts, **kwargs): """This function stubs out execute, optionally executing a preconfigued function to return expected data """ @@ -64,8 +63,7 @@ def fake_execute(*cmd, **kwargs): process_input = kwargs.get('process_input', None) addl_env = kwargs.get('addl_env', None) check_exit_code = kwargs.get('check_exit_code', 0) - cmd_map = map(str, cmd) - cmd_str = ' '.join(cmd_map) + cmd_str = ' '.join(str(part) for part in cmd_parts) LOG.debug(_("Faking execution of cmd (subprocess): %s"), cmd_str) _fake_execute_log.append(cmd_str) @@ -78,13 +76,13 @@ def fake_execute(*cmd, **kwargs): LOG.debug(_('Faked command matched %s') % fake_replier[0]) break - if isinstance(reply_handler, types.StringTypes): + if isinstance(reply_handler, basestring): # If the reply handler is a string, return it as stdout reply = reply_handler, '' else: try: # Alternative is a function, so call it - reply = reply_handler(cmd, + reply = reply_handler(cmd_parts, process_input=process_input, addl_env=addl_env, check_exit_code=check_exit_code) @@ -92,8 +90,10 @@ def fake_execute(*cmd, **kwargs): LOG.debug(_('Faked command raised an exception %s' % str(e))) raise - LOG.debug(_("Reply to faked command is stdout='%(0)s' stderr='%(1)s'") % - {'0': reply[0], '1': reply[1]}) + stdout = reply[0] + stderr = reply[1] + LOG.debug(_("Reply to faked command is stdout='%(stdout)s' " + "stderr='%(stderr)s'") % locals()) # Replicate the sleep call in the real function greenthread.sleep(0) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index b22163e9b..d31fa27ac 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -76,7 +76,6 @@ class XenAPIVolumeTestCase(test.TestCase): FLAGS.xenapi_connection_url = 'test_url' FLAGS.xenapi_connection_password = 'test_pass' db_fakes.stub_out_db_instance_api(self.stubs) - #db_fakes.stub_out_db_network_api(self.stubs) stubs.stub_out_get_target(self.stubs) xenapi_fake.reset() self.values = {'id': 1, @@ -333,7 +332,8 @@ class XenAPIVMTestCase(test.TestCase): self.assertEquals(self.vm['HVM_boot_policy'], '') def _test_spawn(self, image_id, kernel_id, ramdisk_id, - instance_type="m1.large", os_type="linux", check_injection=False): + instance_type="m1.large", os_type="linux", + check_injection=False): stubs.stubout_loopingcall_start(self.stubs) values = {'id': 1, 'project_id': self.project.id, diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index d278934c6..0f559b7f9 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -139,9 +139,9 @@ def stubout_is_vdi_pv(stubs): def stubout_loopingcall_start(stubs): - def f_1(self, interval, now=True): + def fake_start(self, interval, now=True): self.f(*self.args, **self.kw) - stubs.Set(utils.LoopingCall, 'start', f_1) + stubs.Set(utils.LoopingCall, 'start', fake_start) class FakeSessionForVMTests(fake.SessionBase): diff --git a/nova/virt/disk.py b/nova/virt/disk.py index eae667c01..ee6d3e36a 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -166,12 +166,13 @@ def _free_device(device): def get_injectables(inst, template=None, template_data=None): - #load cheetah.template if necessary + # Note(salvatore-orlando): + # it the caller does not provide template object and data + # we will import the Cheetah template module and load the + # data from the file specified by injected_network_template flag if not template: - t = __import__('Cheetah.Template', globals(), locals(), ['Template'], - -1) + from Cheetah import Template as t template = t.Template - #load template file if necessary if not template_data: template_data = open(FLAGS.injected_network_template).read() diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index 60db86ecd..18d558058 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -427,7 +427,6 @@ class SessionBase(object): if (field in _db_content[cls][ref]): return _db_content[cls][ref][field] else: - LOG.debug(_('Raising Failure')) raise Failure(['HANDLE_INVALID', cls, ref]) LOG.debug(_('Raising NotImplemented')) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 7b6f2b5da..d429dae3c 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -683,65 +683,12 @@ class VMHelper(HelperBase): # everything mount_required = False key, net = disk.get_injectables(instance) - if key is not None or net is not None: - mount_required = True - - if mount_required: - - def _mounted_processing(device): - """Callback which runs with the image VDI attached""" + mount_required = key or net + if not mount_required: + return - dev_path = '/dev/' + device + '1' # NB: Partition 1 hardcoded - tmpdir = tempfile.mkdtemp() - try: - # Mount only Linux filesystems, to avoid disturbing - # NTFS images - try: - out, err = utils.execute('sudo', 'mount', - '-t', 'ext2,ext3', - dev_path, tmpdir) - except exception.ProcessExecutionError as e: - err = str(e) - if err: - LOG.info(_('Failed to mount filesystem (expected for ' - 'non-linux instances): %s') % err) - else: - try: - # This try block ensures that the umount occurs - xe_guest_agent_filename = os.path.join( - tmpdir, FLAGS.xenapi_agent_path) - if os.path.isfile(xe_guest_agent_filename): - # The presence of the guest agent - # file indicates that this instance can - # reconfigure the network from xenstore data, - # so manipulation of files in /etc is not - # required - LOG.info(_('XenServer tools installed in this ' - 'image are capable of network injection. ' - 'Networking files will not be' - 'manipulated')) - else: - xe_daemon_filename = os.path.join(tmpdir, - 'usr', 'sbin', 'xe-daemon') - if os.path.isfile(xe_daemon_filename): - LOG.info(_('XenServer tools are present ' - 'in this image but are not capable ' - 'of network injection')) - else: - LOG.info(_('XenServer tools are not ' - 'installed in this image')) - LOG.info(_('Manipulating interface files ' - 'directly')) - disk.inject_data_into_fs(tmpdir, key, net, - utils.execute) - finally: - utils.execute('sudo', 'umount', dev_path) - finally: - # remove temporary directory - os.rmdir(tmpdir) - - with_vdi_attached_here(session, vdi_ref, False, - _mounted_processing) + with_vdi_attached_here(session, vdi_ref, False, + lambda dev: _mounted_processing(dev, key, net)) @classmethod def lookup_kernel_ramdisk(cls, session, vm): @@ -1077,3 +1024,65 @@ def _write_partition(virtual_size, dev): def get_name_label_for_image(image): # TODO(sirp): This should eventually be the URI for the Glance image return _('Glance image %s') % image + + +def _mount_filesystem(dev_path, dir): + """mounts the device specified by dev_path in dir""" + try: + out, err = utils.execute('sudo', 'mount', + '-t', 'ext2,ext3', + dev_path, dir) + except exception.ProcessExecutionError as e: + err = str(e) + return err + + +def _find_guest_agent(base_dir, agent_rel_path): + agent_path = os.path.join(base_dir, agent_rel_path) + if os.path.isfile(agent_path): + # The presence of the guest agent + # file indicates that this instance can + # reconfigure the network from xenstore data, + # so manipulation of files in /etc is not + # required + LOG.info(_('XenServer tools installed in this ' + 'image are capable of network injection. ' + 'Networking files will not be' + 'manipulated')) + return True + xe_daemon_filename = os.path.join(base_dir, + 'usr', 'sbin', 'xe-daemon') + if os.path.isfile(xe_daemon_filename): + LOG.info(_('XenServer tools are present ' + 'in this image but are not capable ' + 'of network injection')) + else: + LOG.info(_('XenServer tools are not ' + 'installed in this image')) + return False + + +def _mounted_processing(device, key, net): + """Callback which runs with the image VDI attached""" + + dev_path = '/dev/' + device + '1' # NB: Partition 1 hardcoded + tmpdir = tempfile.mkdtemp() + try: + # Mount only Linux filesystems, to avoid disturbing NTFS images + err = _mount_filesystem(dev_path, tmpdir) + if not err: + try: + # This try block ensures that the umount occurs + if not _find_guest_agent(tmpdir, FLAGS.xenapi_agent_path): + LOG.info(_('Manipulating interface files ' + 'directly')) + disk.inject_data_into_fs(tmpdir, key, net, + utils.execute) + finally: + utils.execute('sudo', 'umount', dev_path) + else: + LOG.info(_('Failed to mount filesystem (expected for ' + 'non-linux instances): %s') % err) + finally: + # remove temporary directory + os.rmdir(tmpdir) diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 59c5f3c13..e3810e218 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -169,7 +169,6 @@ class VMOps(object): def _wait_for_boot(): try: - LOG.debug("ENTERING WAIT FOR BOOT!") state = self.get_info(instance_name)['state'] db.instance_set_state(context.get_admin_context(), instance['id'], state) diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 4f31f1071..7c3d3544f 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -107,17 +107,17 @@ flags.DEFINE_integer('xenapi_vhd_coalesce_max_attempts', 'Max number of times to poll for VHD to coalesce.' ' Used only if connection_type=xenapi.') flags.DEFINE_bool('xenapi_inject_image', - True, - 'Specifies whether an attempt to inject network/key' - ' data into the disk image should be made.' - ' Used only if connection_type=xenapi.') + True, + 'Specifies whether an attempt to inject network/key' + ' data into the disk image should be made.' + ' Used only if connection_type=xenapi.') flags.DEFINE_string('xenapi_agent_path', - 'usr/sbin/xe-update-networking', - 'Specifies the path in which the xenapi guest agent' - ' should be located. If the agent is present,' - ' network configuration if not injected into the image' - ' Used only if connection_type=xenapi.' - ' and xenapi_inject_image=True') + 'usr/sbin/xe-update-networking', + 'Specifies the path in which the xenapi guest agent' + ' should be located. If the agent is present,' + ' network configuration if not injected into the image' + ' Used only if connection_type=xenapi.' + ' and xenapi_inject_image=True') flags.DEFINE_string('xenapi_sr_base_path', '/var/run/sr-mount', 'Base path to the storage repository') -- cgit From d8052812cb5b9d3d3578c0e6651e56b4313d5f85 Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Thu, 24 Mar 2011 10:32:22 +0000 Subject: Sorted out a problem occurred with units tests for VM migration --- nova/tests/test_xenapi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index d31fa27ac..fd8ed39d8 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -553,6 +553,7 @@ class XenAPIMigrateInstance(test.TestCase): 'image_id': 1, 'kernel_id': None, 'ramdisk_id': None, + 'local_gb': 5, 'instance_type': 'm1.large', 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': 'linux'} -- cgit From 7baaace446c441fdd699018912ef7604265000ce Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Thu, 24 Mar 2011 16:36:37 +0000 Subject: Stubbing out utils.execute for migrate tests --- nova/tests/test_xenapi.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index fd8ed39d8..2ee385cff 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -558,6 +558,7 @@ class XenAPIMigrateInstance(test.TestCase): 'mac_address': 'aa:bb:cc:dd:ee:ff', 'os_type': 'linux'} + fake_utils.stub_out_utils_execute(self.stubs) stubs.stub_out_migration_methods(self.stubs) stubs.stubout_get_this_vm_uuid(self.stubs) glance_stubs.stubout_glance_client(self.stubs, -- cgit From 08d40029973d9ca97477393531296502a407debe Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Thu, 24 Mar 2011 22:39:39 +0000 Subject: Addressing Trey's comments. Removed disk_get_injectables, using _get_network_info's return value. --- nova/virt/disk.py | 37 --------------------------------- nova/virt/libvirt_conn.py | 31 +++++++++++++++++++++++++++- nova/virt/xenapi/vm_utils.py | 49 +++++++++++++++++++++++++++++++++++++++++--- nova/virt/xenapi/vmops.py | 10 +++++---- nova/virt/xenapi_conn.py | 2 +- 5 files changed, 83 insertions(+), 46 deletions(-) diff --git a/nova/virt/disk.py b/nova/virt/disk.py index ee6d3e36a..25e4f54a9 100644 --- a/nova/virt/disk.py +++ b/nova/virt/disk.py @@ -165,43 +165,6 @@ def _free_device(device): _DEVICES.append(device) -def get_injectables(inst, template=None, template_data=None): - # Note(salvatore-orlando): - # it the caller does not provide template object and data - # we will import the Cheetah template module and load the - # data from the file specified by injected_network_template flag - if not template: - from Cheetah import Template as t - template = t.Template - if not template_data: - template_data = open(FLAGS.injected_network_template).read() - - key = str(inst['key_data']) - net = None - network_ref = db.network_get_by_instance(context.get_admin_context(), - inst['id']) - if network_ref['injected']: - admin_context = context.get_admin_context() - address = db.instance_get_fixed_address(admin_context, inst['id']) - address_v6 = None - if FLAGS.use_ipv6: - address_v6 = db.instance_get_fixed_address_v6(admin_context, - inst['id']) - interfaces_info = {'address': address, - 'netmask': network_ref['netmask'], - 'gateway': network_ref['gateway'], - 'broadcast': network_ref['broadcast'], - 'dns': network_ref['dns'], - 'address_v6': address_v6, - 'gateway_v6': network_ref['gateway_v6'], - 'netmask_v6': network_ref['netmask_v6'], - 'use_ipv6': FLAGS.use_ipv6} - net = str(template(template_data, - searchList=[interfaces_info])) - - return key, net - - def inject_data_into_fs(fs, key, net, execute): """Injects data into a filesystem already mounted by the caller. Virt connections can call this directly if they mount their fs diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py index 4b855f5de..f6a51fc62 100644 --- a/nova/virt/libvirt_conn.py +++ b/nova/virt/libvirt_conn.py @@ -681,7 +681,36 @@ class LibvirtConnection(driver.ComputeDriver): if not inst['kernel_id']: target_partition = "1" - key, net = disk.get_injectables(inst, Template, self.interfaces_xml) + key = str(inst['key_data']) + net = None + + nets = [] + ifc_template = open(FLAGS.injected_network_template).read() + ifc_num = -1 + admin_context = context.get_admin_context() + for (network_ref, mapping) in network_info: + ifc_num += 1 + + if not 'injected' in network_ref: + continue + + address = mapping['ips'][0]['ip'] + address_v6 = None + if FLAGS.use_ipv6: + address_v6 = mapping['ip6s'][0]['ip'] + net_info = {'name': 'eth%d' % ifc_num, + 'address': address, + 'netmask': network_ref['netmask'], + 'gateway': network_ref['gateway'], + 'broadcast': network_ref['broadcast'], + 'dns': network_ref['dns'], + 'address_v6': address_v6, + 'gateway_v6': network_ref['gateway_v6'], + 'netmask_v6': network_ref['netmask_v6'], + 'use_ipv6': FLAGS.use_ipv6} + nets.append(net_info) + + net = str(Template(ifc_template, searchList=[{'interfaces': nets}])) if key or net: inst_name = inst['name'] diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index d429dae3c..5f66c4237 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -674,7 +674,7 @@ class VMHelper(HelperBase): return None @classmethod - def preconfigure_instance(cls, session, instance, vdi_ref): + def preconfigure_instance(cls, session, instance, vdi_ref, network_info): """Makes alterations to the image before launching as part of spawn. """ @@ -682,11 +682,11 @@ class VMHelper(HelperBase): # if at all, so determine whether it's required first, and then do # everything mount_required = False - key, net = disk.get_injectables(instance) + key, net = _prepare_injectables(instance, network_info) mount_required = key or net if not mount_required: return - + with_vdi_attached_here(session, vdi_ref, False, lambda dev: _mounted_processing(dev, key, net)) @@ -1038,6 +1038,10 @@ def _mount_filesystem(dev_path, dir): def _find_guest_agent(base_dir, agent_rel_path): + """ + tries to locate a guest agent at the path + specificed by agent_rel_path + """ agent_path = os.path.join(base_dir, agent_rel_path) if os.path.isfile(agent_path): # The presence of the guest agent @@ -1086,3 +1090,42 @@ def _mounted_processing(device, key, net): finally: # remove temporary directory os.rmdir(tmpdir) + + +def _prepare_injectables(inst, networks_info): + """ + prepares the ssh key and the network configuration file to be + injected into the disk image + """ + #do the import here - Cheetah.Template will be loaded + #only if injection is performed + from Cheetah import Template as t + template = t.Template + template_data = open(FLAGS.injected_network_template).read() + + key = str(inst['key_data']) + net = None + #fetch info only for the 1st network + if len(networks_info) > 0: + network_info = networks_info[0][1] + if network_info: + #remap data in network_info onto keys for template + ip_v4 = ip_v6 = None + if len(network_info['ips']) > 0: + ip_v4 = network_info['ips'][0] + if len(network_info['ip6s']) > 0: + ip_v6 = network_info['ip6s'][0] + if len(network_info['dns']) > 0: + dns = network_info['dns'][0] + interfaces_info = {'address': ip_v4 and ip_v4['ip'] or '', + 'netmask': ip_v4 and ip_v4['netmask'] or '', + 'gateway': network_info['gateway'], + 'broadcast': network_info['broadcast'], + 'dns': dns, + 'address_v6': ip_v6 and ip_v6['ip'] or '', + 'netmask_v6': ip_v6 and ip_v6['netmask'] or '', + 'gateway_v6': ip_v6 and ip_v6['gateway'] or '', + 'use_ipv6': FLAGS.use_ipv6} + net = str(template(template_data, + searchList=[interfaces_info])) + return key, net \ No newline at end of file diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index 8e5302766..b33b5902c 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -161,14 +161,16 @@ class VMOps(object): VMHelper.create_vbd(session=self._session, vm_ref=vm_ref, vdi_ref=vdi_ref, userdevice=0, bootable=True) - # Alter the image before VM start for, e.g. network injection - if FLAGS.xenapi_inject_image: - VMHelper.preconfigure_instance(self._session, instance, vdi_ref) - # TODO(tr3buchet) - check to make sure we have network info, otherwise # create it now. This goes away once nova-multi-nic hits. if network_info is None: network_info = self._get_network_info(instance) + + # Alter the image before VM start for, e.g. network injection + if FLAGS.xenapi_inject_image: + VMHelper.preconfigure_instance(self._session, instance, + vdi_ref, network_info) + self.create_vifs(vm_ref, network_info) self.inject_network_info(instance, vm_ref, network_info) return vm_ref diff --git a/nova/virt/xenapi_conn.py b/nova/virt/xenapi_conn.py index 0c234cb52..99fd35c61 100644 --- a/nova/virt/xenapi_conn.py +++ b/nova/virt/xenapi_conn.py @@ -116,7 +116,7 @@ flags.DEFINE_string('xenapi_agent_path', 'usr/sbin/xe-update-networking', 'Specifies the path in which the xenapi guest agent' ' should be located. If the agent is present,' - ' network configuration if not injected into the image' + ' network configuration is not injected into the image' ' Used only if connection_type=xenapi.' ' and xenapi_inject_image=True') -- cgit From 73df3e0cd54dc3b5409fba7b38ada6ee07bd911d Mon Sep 17 00:00:00 2001 From: Salvatore Orlando Date: Fri, 25 Mar 2011 01:23:33 +0000 Subject: minor pep8 fix in db/fakes.py --- nova/tests/db/fakes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index c46b75aa2..21a5481bd 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -108,9 +108,7 @@ def stub_out_db_instance_api(stubs, injected=True): return FakeModel(fixed_ip_fields).address def fake_fixed_ip_get_all_by_instance(context, instance_id): - l = [] - l.append(FakeModel(fixed_ip_fields)) - return l + return [FakeModel(fixed_ip_fields)] stubs.Set(db, 'network_get_by_instance', fake_network_get_by_instance) stubs.Set(db, 'instance_type_get_all', fake_instance_type_get_all) -- cgit