From 6930c62a02a39f64506a7b2d2ec5b04dbff5fe3a Mon Sep 17 00:00:00 2001 From: sateesh Date: Mon, 5 Sep 2011 12:51:07 +0530 Subject: Multi-NIC support for vmwareapi virt driver in nova. Does injection of Multi-NIC information to instances with Operating system flavors Ubuntu, Windows and RHEL. vmwareapi virt driver now relies on calls to network manager instead of nova db calls for network configuration information of instance. Ensure if port group is properly associated with vlan_interface specified in case of VLAN networking for instances. Re-oranized VMWareVlanBridgeDriver and added session parmeter to methods to use existing session. Also removed session creation code as session comes as argument. Added check for flat_inject flag before attempting an inject operation. Removed stale code from vmwareapi stubs. Also updated some comments to be more meaningful. Did pep8 and pylint checks. Tried to improve pylint score for newly added lines of code. --- nova/tests/vmwareapi/stubs.py | 2 - nova/virt/vmwareapi/fake.py | 2 +- nova/virt/vmwareapi/vif.py | 27 +++++------ nova/virt/vmwareapi/vm_util.py | 26 +++++----- nova/virt/vmwareapi/vmops.py | 106 ++++++++++++++++++++++++----------------- tools/esx/guest_tool.py | 70 +++++++++++++++------------ 6 files changed, 124 insertions(+), 109 deletions(-) diff --git a/nova/tests/vmwareapi/stubs.py b/nova/tests/vmwareapi/stubs.py index 0ed5e9b68..7de10e612 100644 --- a/nova/tests/vmwareapi/stubs.py +++ b/nova/tests/vmwareapi/stubs.py @@ -45,8 +45,6 @@ def set_stubs(stubs): stubs.Set(vmware_images, 'get_vmdk_size_and_properties', fake.fake_get_vmdk_size_and_properties) stubs.Set(vmware_images, 'upload_image', fake.fake_upload_image) - stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", - fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_get_vim_object", fake_get_vim_object) stubs.Set(vmwareapi_conn.VMWareAPISession, "_is_vim_object", diff --git a/nova/virt/vmwareapi/fake.py b/nova/virt/vmwareapi/fake.py index 4c62d18bb..f47ffdf1c 100644 --- a/nova/virt/vmwareapi/fake.py +++ b/nova/virt/vmwareapi/fake.py @@ -409,7 +409,7 @@ def fake_plug_vifs(*args, **kwargs): def fake_get_network(*args, **kwargs): """Fake get network.""" - return [{'type': 'fake'}] + return {'type': 'fake'} def fake_fetch_image(image, instance, **kwargs): diff --git a/nova/virt/vmwareapi/vif.py b/nova/virt/vmwareapi/vif.py index fb6548b34..9906b89e1 100644 --- a/nova/virt/vmwareapi/vif.py +++ b/nova/virt/vmwareapi/vif.py @@ -17,42 +17,35 @@ """VIF drivers for VMWare.""" -from nova import db from nova import exception from nova import flags from nova import log as logging -from nova import utils from nova.virt.vif import VIFDriver -from nova.virt.vmwareapi_conn import VMWareAPISession from nova.virt.vmwareapi import network_utils LOG = logging.getLogger("nova.virt.vmwareapi.vif") FLAGS = flags.FLAGS +FLAGS['vmwareapi_vlan_interface'].SetDefault('vmnic0') class VMWareVlanBridgeDriver(VIFDriver): """VIF Driver to setup bridge/VLAN networking using VMWare API.""" def plug(self, instance, network, mapping): + """Plug the VIF to specified instance using information passed. + Currently we are plugging the VIF(s) during instance creation itself. + We can use this method when we add support to add additional NIC to + an existing instance.""" + pass + + def ensure_vlan_bridge(self, session, network): """Create a vlan and bridge unless they already exist.""" vlan_num = network['vlan'] bridge = network['bridge'] - bridge_interface = network['bridge_interface'] + vlan_interface = FLAGS.vmwareapi_vlan_interface - # Open vmwareapi session - host_ip = FLAGS.vmwareapi_host_ip - host_username = FLAGS.vmwareapi_host_username - host_password = FLAGS.vmwareapi_host_password - if not host_ip or host_username is None or host_password is None: - raise Exception(_('Must specify vmwareapi_host_ip, ' - 'vmwareapi_host_username ' - 'and vmwareapi_host_password to use ' - 'connection_type=vmwareapi')) - session = VMWareAPISession(host_ip, host_username, host_password, - FLAGS.vmwareapi_api_retry_count) - vlan_interface = bridge_interface # Check if the vlan_interface physical network adapter exists on the # host. if not network_utils.check_if_vlan_interface_exists(session, @@ -92,4 +85,6 @@ class VMWareVlanBridgeDriver(VIFDriver): pgroup=pg_vlanid) def unplug(self, instance, network, mapping): + """Cleanup operations like deleting port group if no instance + is associated with it.""" pass diff --git a/nova/virt/vmwareapi/vm_util.py b/nova/virt/vmwareapi/vm_util.py index 82b5f7214..dd1c81196 100644 --- a/nova/virt/vmwareapi/vm_util.py +++ b/nova/virt/vmwareapi/vm_util.py @@ -39,8 +39,7 @@ def split_datastore_path(datastore_path): def get_vm_create_spec(client_factory, instance, data_store_name, - network_name="vmnet0", - os_type="otherGuest", network_ref=None): + vif_infos, os_type="otherGuest"): """Builds the VM Create spec.""" config_spec = client_factory.create('ns0:VirtualMachineConfigSpec') config_spec.name = instance.name @@ -61,14 +60,12 @@ def get_vm_create_spec(client_factory, instance, data_store_name, config_spec.numCPUs = int(instance.vcpus) config_spec.memoryMB = int(instance.memory_mb) - mac_address = None - if instance['mac_addresses']: - mac_address = instance['mac_addresses'][0]['address'] + vif_spec_list = [] + for vif_info in vif_infos: + vif_spec = create_network_spec(client_factory, vif_info) + vif_spec_list.append(vif_spec) - nic_spec = create_network_spec(client_factory, - network_name, mac_address) - - device_config_spec = [nic_spec] + device_config_spec = vif_spec_list config_spec.deviceChange = device_config_spec return config_spec @@ -93,8 +90,7 @@ def create_controller_spec(client_factory, key): return virtual_device_config -def create_network_spec(client_factory, network_name, mac_address, - network_ref=None): +def create_network_spec(client_factory, vif_info): """ Builds a config spec for the addition of a new network adapter to the VM. @@ -109,6 +105,9 @@ def create_network_spec(client_factory, network_name, mac_address, # NOTE(asomya): Only works on ESXi if the portgroup binding is set to # ephemeral. Invalid configuration if set to static and the NIC does # not come up on boot if set to dynamic. + network_ref = vif_info['network_ref'] + network_name = vif_info['network_name'] + mac_address = vif_info['mac_address'] backing = None if (network_ref and network_ref['type'] == "DistributedVirtualPortgroup"): @@ -295,11 +294,8 @@ def get_dummy_vm_create_spec(client_factory, name, data_store_name): return config_spec -def get_machine_id_change_spec(client_factory, mac, ip_addr, netmask, - gateway, broadcast, dns): +def get_machine_id_change_spec(client_factory, machine_id_str): """Builds the machine id change config spec.""" - machine_id_str = "%s;%s;%s;%s;%s;%s" % (mac, ip_addr, netmask, - gateway, broadcast, dns) virtual_machine_config_spec = \ client_factory.create('ns0:VirtualMachineConfigSpec') diff --git a/nova/virt/vmwareapi/vmops.py b/nova/virt/vmwareapi/vmops.py index 07a6ba6ab..3ac99f923 100644 --- a/nova/virt/vmwareapi/vmops.py +++ b/nova/virt/vmwareapi/vmops.py @@ -27,7 +27,6 @@ import urllib2 import uuid from nova import context as nova_context -from nova import db from nova import exception from nova import flags from nova import log as logging @@ -111,22 +110,6 @@ class VMWareVMOps(object): client_factory = self._session._get_vim().client.factory service_content = self._session._get_vim().get_service_content() - network = db.network_get_by_instance(nova_context.get_admin_context(), - instance['id']) - - net_name = network['bridge'] - - def _check_if_network_bridge_exists(): - network_ref = \ - network_utils.get_network_with_the_name(self._session, - net_name) - if network_ref is None: - raise exception.NetworkNotFoundForBridge(bridge=net_name) - return network_ref - - self.plug_vifs(instance, network_info) - network_obj = _check_if_network_bridge_exists() - def _get_datastore_ref(): """Get the datastore list and choose the first local storage.""" data_stores = self._session._call_method(vim_util, "get_objects", @@ -182,11 +165,36 @@ class VMWareVMOps(object): vm_folder_mor, res_pool_mor = _get_vmfolder_and_res_pool_mors() + def _check_if_network_bridge_exists(network_name): + network_ref = \ + network_utils.get_network_with_the_name(self._session, + network_name) + if network_ref is None: + raise exception.NetworkNotFoundForBridge(bridge=network_name) + return network_ref + + def _get_vif_infos(): + vif_infos = [] + for (network, mapping) in network_info: + mac_address = mapping['mac'] + network_name = network['bridge'] + if mapping.get('should_create_vlan'): + network_ref = self._vif_driver.ensure_vlan_bridge( + self._session, network) + else: + network_ref = _check_if_network_bridge_exists(network_name) + vif_infos.append({'network_name': network_name, + 'mac_address': mac_address, + 'network_ref': network_ref, + }) + return vif_infos + + vif_infos = _get_vif_infos() + # Get the create vm config spec config_spec = vm_util.get_vm_create_spec( client_factory, instance, - data_store_name, net_name, os_type, - network_obj) + data_store_name, vif_infos, os_type) def _execute_create_vm(): """Create VM on ESX host.""" @@ -204,8 +212,10 @@ class VMWareVMOps(object): _execute_create_vm() - # Set the machine id for the VM for setting the IP - self._set_machine_id(client_factory, instance) + # Set the machine.id parameter of the instance to inject + # the NIC configuration inside the VM + if FLAGS.flat_injected: + self._set_machine_id(client_factory, instance, network_info) # Naming the VM files in correspondence with the VM instance name # The flat vmdk file name @@ -716,39 +726,45 @@ class VMWareVMOps(object): """Return link to instance's ajax console.""" return 'http://fakeajaxconsole/fake_url' - def _set_machine_id(self, client_factory, instance): + def _set_machine_id(self, client_factory, instance, network_info): """ - Set the machine id of the VM for guest tools to pick up and change - the IP. + Set the machine id of the VM for guest tools to pick up and reconfigure + the network interfaces. """ - admin_context = nova_context.get_admin_context() vm_ref = self._get_vm_ref_from_the_name(instance.name) if vm_ref is None: raise exception.InstanceNotFound(instance_id=instance.id) - network = db.network_get_by_instance(nova_context.get_admin_context(), - instance['id']) - mac_address = None - if instance['mac_addresses']: - mac_address = instance['mac_addresses'][0]['address'] - - net_mask = network["netmask"] - gateway = network["gateway"] - broadcast = network["broadcast"] - # TODO(vish): add support for dns2 - dns = network["dns1"] - - addresses = db.instance_get_fixed_addresses(admin_context, - instance['id']) - ip_addr = addresses[0] if addresses else None + + machine_id_str = '' + for (network, info) in network_info: + # TODO(vish): add support for dns2 + # TODO(sateesh): add support for injection of ipv6 configuration + ip_v4 = ip_v6 = None + if 'ips' in info and len(info['ips']) > 0: + ip_v4 = info['ips'][0] + if 'ip6s' in info and len(info['ip6s']) > 0: + ip_v6 = info['ip6s'][0] + if len(info['dns']) > 0: + dns = info['dns'][0] + else: + dns = '' + + interface_str = "%s;%s;%s;%s;%s;%s" % \ + (info['mac'], + ip_v4 and ip_v4['ip'] or '', + ip_v4 and ip_v4['netmask'] or '', + info['gateway'], + info['broadcast'], + dns) + machine_id_str = machine_id_str + interface_str + '#' machine_id_change_spec = \ - vm_util.get_machine_id_change_spec(client_factory, mac_address, - ip_addr, net_mask, gateway, - broadcast, dns) + vm_util.get_machine_id_change_spec(client_factory, machine_id_str) + LOG.debug(_("Reconfiguring VM instance %(name)s to set the machine id " "with ip - %(ip_addr)s") % ({'name': instance.name, - 'ip_addr': ip_addr})) + 'ip_addr': ip_v4['ip']})) reconfig_task = self._session._call_method(self._session._get_vim(), "ReconfigVM_Task", vm_ref, spec=machine_id_change_spec) @@ -756,7 +772,7 @@ class VMWareVMOps(object): LOG.debug(_("Reconfigured VM instance %(name)s to set the machine id " "with ip - %(ip_addr)s") % ({'name': instance.name, - 'ip_addr': ip_addr})) + 'ip_addr': ip_v4['ip']})) def _get_datacenter_name_and_ref(self): """Get the datacenter name and the reference.""" diff --git a/tools/esx/guest_tool.py b/tools/esx/guest_tool.py index 97b5302ba..5158d883a 100644 --- a/tools/esx/guest_tool.py +++ b/tools/esx/guest_tool.py @@ -81,28 +81,34 @@ def _bytes2int(bytes): def _parse_network_details(machine_id): """ - Parse the machine.id field to get MAC, IP, Netmask and Gateway fields - machine.id is of the form MAC;IP;Netmask;Gateway;Broadcast;DNS1,DNS2 - where ';' is the separator. + Parse the machine_id to get MAC, IP, Netmask and Gateway fields per NIC. + machine_id is of the form ('NIC_record#NIC_record#', '') + Each of the NIC will have record NIC_record in the form + 'MAC;IP;Netmask;Gateway;Broadcast;DNS' where ';' is field separator. + Each record is separated by '#' from next record. """ + logging.debug(_("Received machine_id from vmtools : %s") % machine_id[0]) network_details = [] if machine_id[1].strip() == "1": pass else: - network_info_list = machine_id[0].split(';') - assert len(network_info_list) % 6 == 0 - no_grps = len(network_info_list) / 6 - i = 0 - while i < no_grps: - k = i * 6 - network_details.append(( - network_info_list[k].strip().lower(), - network_info_list[k + 1].strip(), - network_info_list[k + 2].strip(), - network_info_list[k + 3].strip(), - network_info_list[k + 4].strip(), - network_info_list[k + 5].strip().split(','))) - i += 1 + for machine_id_str in machine_id[0].split('#'): + network_info_list = machine_id_str.split(';') + if len(network_info_list) % 6 != 0: + break + no_grps = len(network_info_list) / 6 + i = 0 + while i < no_grps: + k = i * 6 + network_details.append(( + network_info_list[k].strip().lower(), + network_info_list[k + 1].strip(), + network_info_list[k + 2].strip(), + network_info_list[k + 3].strip(), + network_info_list[k + 4].strip(), + network_info_list[k + 5].strip().split(','))) + i += 1 + logging.debug(_("NIC information from vmtools : %s") % network_details) return network_details @@ -279,6 +285,7 @@ def _filter_duplicates(all_entries): def _set_rhel_networking(network_details=None): + """Set IPv4 network settings for RHEL distros.""" network_details = network_details or [] all_dns_servers = [] for network_detail in network_details: @@ -320,31 +327,33 @@ def _set_rhel_networking(network_details=None): def _set_ubuntu_networking(network_details=None): + """Set IPv4 network settings for Ubuntu.""" network_details = network_details or [] - """ Set IPv4 network settings for Ubuntu """ all_dns_servers = [] - for network_detail in network_details: + interface_file_name = '/etc/network/interfaces' + # Remove file + os.remove(interface_file_name) + # Touch file + _execute(['touch', interface_file_name]) + interface_file = open(interface_file_name, 'w') + for device, network_detail in enumerate(network_details): mac_address, ip_address, subnet_mask, gateway, broadcast,\ dns_servers = network_detail all_dns_servers.extend(dns_servers) adapter_name, current_ip_address = \ _get_linux_adapter_name_and_ip_address(mac_address) - if adapter_name and not ip_address == current_ip_address: - interface_file_name = \ - '/etc/network/interfaces' - # Remove file - os.remove(interface_file_name) - # Touch file - _execute(['touch', interface_file_name]) - interface_file = open(interface_file_name, 'w') + if adapter_name: interface_file.write('\nauto %s' % adapter_name) interface_file.write('\niface %s inet static' % adapter_name) interface_file.write('\nbroadcast %s' % broadcast) interface_file.write('\ngateway %s' % gateway) interface_file.write('\nnetmask %s' % subnet_mask) - interface_file.write('\naddress %s' % ip_address) - interface_file.close() + interface_file.write('\naddress %s\n' % ip_address) + logging.debug(_("Successfully configured NIC %d with " + "NIC info %s") % (device, network_detail)) + interface_file.close() + if all_dns_servers: dns_file_name = "/etc/resolv.conf" os.remove(dns_file_name) @@ -355,7 +364,8 @@ def _set_ubuntu_networking(network_details=None): for dns_server in unique_entries: dns_file.write("\nnameserver %s" % dns_server) dns_file.close() - print "\nRestarting networking....\n" + + logging.debug(_("Restarting networking....\n")) _execute(['/etc/init.d/networking', 'restart']) -- cgit From 9b12a6c5ec11fd6ef3e110e6f0574762060ac809 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 6 Sep 2011 15:19:37 -0400 Subject: Fixes an issue where 'invalid literal for int' would occur when listing images after making a v1.1 server snapshot (with a UUID). v1.1 image id's are now treated as strings (not integer ID's). The v1.0 API still tries to treat image id's as integers but doesn't fail miserably if they are uuid's either. This should pave the way for image ID's as uuids and more closely matches the v1.1 spec with regards to images and the server refs they contain. --- nova/api/openstack/common.py | 24 ++------- nova/api/openstack/views/images.py | 10 ++++ nova/tests/api/openstack/test_common.py | 40 ++++++++++---- nova/tests/api/openstack/test_images.py | 95 +++++++++++++++++++++++++-------- 4 files changed, 118 insertions(+), 51 deletions(-) diff --git a/nova/api/openstack/common.py b/nova/api/openstack/common.py index d743a66ef..dba3ec8e9 100644 --- a/nova/api/openstack/common.py +++ b/nova/api/openstack/common.py @@ -185,30 +185,16 @@ def limited_by_marker(items, request, max_limit=FLAGS.osapi_max_limit): def get_id_from_href(href): - """Return the id portion of a url as an int. + """Return the id or uuid portion of a url. Given: 'http://www.foo.com/bar/123?q=4' - Returns: 123 + Returns: '123' - In order to support local hrefs, the href argument can be just an id: - Given: '123' - Returns: 123 + Given: 'http://www.foo.com/bar/abc123?q=4' + Returns: 'abc123' """ - LOG.debug(_("Attempting to treat %(href)s as an integer ID.") % locals()) - - try: - return int(href) - except ValueError: - pass - - LOG.debug(_("Attempting to treat %(href)s as a URL.") % locals()) - - try: - return int(urlparse.urlsplit(href).path.split('/')[-1]) - except ValueError as error: - LOG.debug(_("Failed to parse ID from %(href)s: %(error)s") % locals()) - raise + return urlparse.urlsplit("%s" % href).path.split('/')[-1] def remove_version_from_href(href): diff --git a/nova/api/openstack/views/images.py b/nova/api/openstack/views/images.py index 21f1b2d3e..20c99124b 100644 --- a/nova/api/openstack/views/images.py +++ b/nova/api/openstack/views/images.py @@ -70,6 +70,7 @@ class ViewBuilder(object): } self._build_server(image, image_obj) + self._build_image_id(image, image_obj) if detail: image.update({ @@ -95,6 +96,12 @@ class ViewBuilderV10(ViewBuilder): except (KeyError, ValueError): pass + def _build_image_id(self, image, image_obj): + try: + image['id'] = int(image_obj['id']) + except ValueError: + pass + class ViewBuilderV11(ViewBuilder): """OpenStack API v1.1 Image Builder""" @@ -118,6 +125,9 @@ class ViewBuilderV11(ViewBuilder): except KeyError: return + def _build_image_id(self, image, image_obj): + image['id'] = "%s" % image_obj['id'] + def generate_href(self, image_id): """Return an href string pointing to this object.""" return os.path.join(self.base_url, self.project_id, diff --git a/nova/tests/api/openstack/test_common.py b/nova/tests/api/openstack/test_common.py index b422bc4d1..f519ea72b 100644 --- a/nova/tests/api/openstack/test_common.py +++ b/nova/tests/api/openstack/test_common.py @@ -237,21 +237,41 @@ class MiscFunctionsTest(test.TestCase): common.remove_version_from_href, fixture) - def test_get_id_from_href(self): + def test_get_id_from_href_with_int_url(self): fixture = 'http://www.testsite.com/dir/45' actual = common.get_id_from_href(fixture) - expected = 45 + expected = '45' self.assertEqual(actual, expected) - def test_get_id_from_href_bad_request(self): - fixture = 'http://45' - self.assertRaises(ValueError, - common.get_id_from_href, - fixture) + def test_get_id_from_href_with_int(self): + fixture = '45' + actual = common.get_id_from_href(fixture) + expected = '45' + self.assertEqual(actual, expected) - def test_get_id_from_href_int(self): - fixture = 1 - self.assertEqual(fixture, common.get_id_from_href(fixture)) + def test_get_id_from_href_with_int_url_query(self): + fixture = 'http://www.testsite.com/dir/45?asdf=jkl' + actual = common.get_id_from_href(fixture) + expected = '45' + self.assertEqual(actual, expected) + + def test_get_id_from_href_with_uuid_url(self): + fixture = 'http://www.testsite.com/dir/abc123' + actual = common.get_id_from_href(fixture) + expected = "abc123" + self.assertEqual(actual, expected) + + def test_get_id_from_href_with_uuid_url_query(self): + fixture = 'http://www.testsite.com/dir/abc123?asdf=jkl' + actual = common.get_id_from_href(fixture) + expected = "abc123" + self.assertEqual(actual, expected) + + def test_get_id_from_href_with_uuid(self): + fixture = 'abc123' + actual = common.get_id_from_href(fixture) + expected = 'abc123' + self.assertEqual(actual, expected) def test_get_version_from_href(self): fixture = 'http://www.testsite.com/v1.1/images' diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 2a7cfc382..6448e9986 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -365,7 +365,8 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): {'id': 125, 'name': 'saving snapshot'}, {'id': 126, 'name': 'active snapshot'}, {'id': 127, 'name': 'killed snapshot'}, - {'id': 129, 'name': None}] + {'id': 128, 'name': 'active UUID snapshot'}, + {'id': 130, 'name': None}] self.assertDictListMatch(response_list, expected) @@ -403,14 +404,14 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): expected_image = { "image": { - "id": 124, + "id": '124', "name": "queued snapshot", "updated": self.NOW_API_FORMAT, "created": self.NOW_API_FORMAT, "status": "QUEUED", "progress": 0, 'server': { - 'id': 42, + 'id': '42', "links": [{ "rel": "self", "href": server_href, @@ -458,7 +459,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): self.assertEqual(expected_image.toxml(), actual_image.toxml()) def test_get_image_xml_no_name(self): - request = webob.Request.blank('/v1.0/images/129') + request = webob.Request.blank('/v1.0/images/130') request.accept = "application/xml" response = request.get_response(fakes.wsgi_app()) @@ -466,7 +467,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): expected_now = self.NOW_API_FORMAT expected_image = minidom.parseString(""" - Date: Tue, 13 Sep 2011 15:48:10 -0400 Subject: Update test_libvirt so that flags and fakes are used instead of mocks for utils.import_class and utils.import_object. Fixes #lp849329. --- nova/tests/fake_network.py | 30 ++++++++++++++++ nova/tests/test_libvirt.py | 87 +++++++++++++++++----------------------------- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/nova/tests/fake_network.py b/nova/tests/fake_network.py index 1ecb99b31..142206755 100644 --- a/nova/tests/fake_network.py +++ b/nova/tests/fake_network.py @@ -25,6 +25,36 @@ HOST = "testhost" FLAGS = flags.FLAGS +class FakeIptablesFirewallDriver(object): + def __init__(self, **kwargs): + pass + + def setattr(self, key, val): + self.__setattr__(key, val) + + def apply_instance_filter(self, instance, network_info): + pass + + +class FakeVIFDriver(object): + + def __init__(self, **kwargs): + pass + + def setattr(self, key, val): + self.__setattr__(key, val) + + def plug(self, instance, network, mapping): + return { + 'id': 'fake', + 'bridge_name': 'fake', + 'mac_address': 'fake', + 'ip_address': 'fake', + 'dhcp_server': 'fake', + 'extra_params': 'fake', + } + + class FakeModel(dict): """Represent a model from the db""" def __init__(self, *args, **kwargs): diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 233ee14de..8193d6ec2 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -51,6 +51,32 @@ def _concurrency(wait, done, target): done.send() +class FakeVirtDomain(object): + + def __init__(self, fake_xml=None): + if fake_xml: + self._fake_dom_xml = fake_xml + else: + self._fake_dom_xml=""" + + + + + + + + """ + + def snapshotCreateXML(self, *args): + return None + + def createWithFlags(self, launch_flags): + pass + + def XMLDesc(self, *args): + return self._fake_dom_xml + + class CacheConcurrencyTestCase(test.TestCase): def setUp(self): super(CacheConcurrencyTestCase, self).setUp() @@ -152,70 +178,23 @@ class LibvirtConnTestCase(test.TestCase): # A fake libvirt.virConnect class FakeLibvirtConnection(object): - pass - - # A fake connection.IptablesFirewallDriver - class FakeIptablesFirewallDriver(object): - - def __init__(self, **kwargs): - pass - - def setattr(self, key, val): - self.__setattr__(key, val) - - # A fake VIF driver - class FakeVIFDriver(object): - - def __init__(self, **kwargs): - pass - - def setattr(self, key, val): - self.__setattr__(key, val) - - def plug(self, instance, network, mapping): - return { - 'id': 'fake', - 'bridge_name': 'fake', - 'mac_address': 'fake', - 'ip_address': 'fake', - 'dhcp_server': 'fake', - 'extra_params': 'fake', - } + def defineXML(self, xml): + return FakeVirtDomain() # Creating mocks fake = FakeLibvirtConnection() - fakeip = FakeIptablesFirewallDriver - fakevif = FakeVIFDriver() # Customizing above fake if necessary for key, val in kwargs.items(): fake.__setattr__(key, val) - # Inevitable mocks for connection.LibvirtConnection - self.mox.StubOutWithMock(connection.utils, 'import_class') - connection.utils.import_class(mox.IgnoreArg()).AndReturn(fakeip) - self.mox.StubOutWithMock(connection.utils, 'import_object') - connection.utils.import_object(mox.IgnoreArg()).AndReturn(fakevif) + self.flags(image_service='nova.image.fake.FakeImageService') + self.flags(firewall_driver="nova.tests.fake_network.FakeIptablesFirewallDriver") + self.flags(libvirt_vif_driver="nova.tests.fake_network.FakeVIFDriver") + self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') connection.LibvirtConnection._conn = fake def fake_lookup(self, instance_name): - - class FakeVirtDomain(object): - - def snapshotCreateXML(self, *args): - return None - - def XMLDesc(self, *args): - return """ - - - - - - - - """ - return FakeVirtDomain() def fake_execute(self, *args): @@ -797,8 +776,6 @@ class LibvirtConnTestCase(test.TestCase): shutil.rmtree(os.path.join(FLAGS.instances_path, instance.name)) shutil.rmtree(os.path.join(FLAGS.instances_path, '_base')) - self.assertTrue(count) - def test_get_host_ip_addr(self): conn = connection.LibvirtConnection(False) ip = conn.get_host_ip_addr() -- cgit From de3d94726c980f40181693256c0f650d492451ef Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 13 Sep 2011 16:38:46 -0700 Subject: makes sure floating addresses are associated with host on associate so they come back --- nova/db/api.py | 8 +++++--- nova/db/sqlalchemy/api.py | 5 ++++- nova/network/manager.py | 3 ++- nova/tests/db/fakes.py | 4 +++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index c03a86671..5b57b178e 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -261,11 +261,13 @@ def floating_ip_disassociate(context, address): return IMPL.floating_ip_disassociate(context, address) -def floating_ip_fixed_ip_associate(context, floating_address, fixed_address): +def floating_ip_fixed_ip_associate(context, floating_address, + fixed_address, host): """Associate an floating ip to a fixed_ip by address.""" return IMPL.floating_ip_fixed_ip_associate(context, floating_address, - fixed_address) + fixed_address, + host) def floating_ip_get_all(context): @@ -365,7 +367,7 @@ def fixed_ip_get_all(context): def fixed_ip_get_all_by_instance_host(context, host): """Get all allocated fixed ips filtered by instance host.""" - return IMPL.fixed_ip_get_all_instance_by_host(context, host) + return IMPL.fixed_ip_get_all_by_instance_host(context, host) def fixed_ip_get_by_address(context, address): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 523258841..9c3d8413e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -529,7 +529,8 @@ def floating_ip_count_by_project(context, project_id): @require_context -def floating_ip_fixed_ip_associate(context, floating_address, fixed_address): +def floating_ip_fixed_ip_associate(context, floating_address, + fixed_address, host): session = get_session() with session.begin(): # TODO(devcamcar): How to ensure floating_id belongs to user? @@ -540,6 +541,7 @@ def floating_ip_fixed_ip_associate(context, floating_address, fixed_address): fixed_address, session=session) floating_ip_ref.fixed_ip = fixed_ip_ref + floating_ip_ref.host = host floating_ip_ref.save(session=session) @@ -583,6 +585,7 @@ def floating_ip_disassociate(context, address): else: fixed_ip_address = None floating_ip_ref.fixed_ip = None + floating_ip_ref.host = None floating_ip_ref.save(session=session) return fixed_ip_address diff --git a/nova/network/manager.py b/nova/network/manager.py index 05d928fab..aa6711b48 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -289,7 +289,8 @@ class FloatingIP(object): self.db.floating_ip_fixed_ip_associate(context, floating_address, - fixed_address) + fixed_address, + self.host) self.driver.bind_floating_ip(floating_address) self.driver.ensure_floating_forward(floating_address, fixed_address) diff --git a/nova/tests/db/fakes.py b/nova/tests/db/fakes.py index 19028a451..cdbfba63a 100644 --- a/nova/tests/db/fakes.py +++ b/nova/tests/db/fakes.py @@ -125,10 +125,11 @@ def stub_out_db_network_api(stubs): if ips[0]['fixed_ip']: fixed_ip_address = ips[0]['fixed_ip']['address'] ips[0]['fixed_ip'] = None + ips[0]['host'] = None return fixed_ip_address def fake_floating_ip_fixed_ip_associate(context, floating_address, - fixed_address): + fixed_address, host): float = filter(lambda i: i['address'] == floating_address, floating_ips) fixed = filter(lambda i: i['address'] == fixed_address, @@ -136,6 +137,7 @@ def stub_out_db_network_api(stubs): if float and fixed: float[0]['fixed_ip'] = fixed[0] float[0]['fixed_ip_id'] = fixed[0]['id'] + float[0]['host'] = host def fake_floating_ip_get_all_by_host(context, host): # TODO(jkoelker): Once we get the patches that remove host from -- cgit From a85a2c2e82fa8820b04f669c92a3500c7c6cebe2 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 13 Sep 2011 20:38:26 -0400 Subject: pep8 fixes. --- nova/tests/test_libvirt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py index 8193d6ec2..a30b00dbe 100644 --- a/nova/tests/test_libvirt.py +++ b/nova/tests/test_libvirt.py @@ -57,7 +57,7 @@ class FakeVirtDomain(object): if fake_xml: self._fake_dom_xml = fake_xml else: - self._fake_dom_xml=""" + self._fake_dom_xml = """ @@ -188,7 +188,8 @@ class LibvirtConnTestCase(test.TestCase): fake.__setattr__(key, val) self.flags(image_service='nova.image.fake.FakeImageService') - self.flags(firewall_driver="nova.tests.fake_network.FakeIptablesFirewallDriver") + fw_driver = "nova.tests.fake_network.FakeIptablesFirewallDriver" + self.flags(firewall_driver=fw_driver) self.flags(libvirt_vif_driver="nova.tests.fake_network.FakeVIFDriver") self.mox.StubOutWithMock(connection.LibvirtConnection, '_conn') -- cgit From 4eb704e9024111c80f6f7c83810a08d7eec5c4af Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 13 Sep 2011 21:13:58 -0400 Subject: last of the api.openstack.test_images merge fixes. --- nova/tests/api/openstack/test_images.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 6890e0e9e..e054ffd03 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -821,7 +821,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }], }, { - 'id': 128, + 'id': '128', 'name': 'deleted snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', @@ -832,7 +832,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'status': 'DELETED', 'progress': 0, 'server': { - 'id': 42, + 'id': '42', "links": [{ "rel": "self", "href": server_href, @@ -852,7 +852,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }], }, { - 'id': 129, + 'id': '129', 'name': 'pending_delete snapshot', 'metadata': { u'instance_ref': u'http://localhost/v1.1/servers/42', @@ -863,7 +863,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): 'status': 'DELETED', 'progress': 0, 'server': { - 'id': 42, + 'id': '42', "links": [{ "rel": "self", "href": server_href, @@ -914,7 +914,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): }], }, { - 'id': 132, + 'id': '132', 'name': None, 'metadata': {}, 'updated': self.NOW_API_FORMAT, @@ -1224,7 +1224,7 @@ class ImageControllerWithGlanceServiceTest(test.TestCase): # Snapshots for User 1 server_ref = 'http://localhost/v1.1/servers/42' snapshot_properties = {'instance_ref': server_ref, 'user_id': 'fake'} - statuses = ('queued', 'saving', 'active','killed', + statuses = ('queued', 'saving', 'active', 'killed', 'deleted', 'pending_delete') for status in statuses: add_fixture(id=image_id, name='%s snapshot' % status, -- cgit From 7a02394009aae85f722430682f8360371121504b Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Wed, 14 Sep 2011 11:33:36 -0400 Subject: Make tests pass. --- nova/tests/api/openstack/test_images.py | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/nova/tests/api/openstack/test_images.py b/nova/tests/api/openstack/test_images.py index 45ca73073..27ae8f2bd 100644 --- a/nova/tests/api/openstack/test_images.py +++ b/nova/tests/api/openstack/test_images.py @@ -77,14 +77,14 @@ class ImagesTest(test.TestCase): response_dict = json.loads(response.body) response_list = response_dict["images"] - expected = [{'id': '123', 'name': 'public image'}, - {'id': '124', 'name': 'queued snapshot'}, - {'id': '125', 'name': 'saving snapshot'}, - {'id': '126', 'name': 'active snapshot'}, - {'id': '127', 'name': 'killed snapshot'}, - {'id': '128', 'name': 'deleted snapshot'}, - {'id': '129', 'name': 'pending_delete snapshot'}, - {'id': '130', 'name': None}] + expected = [{'id': 123, 'name': 'public image'}, + {'id': 124, 'name': 'queued snapshot'}, + {'id': 125, 'name': 'saving snapshot'}, + {'id': 126, 'name': 'active snapshot'}, + {'id': 127, 'name': 'killed snapshot'}, + {'id': 128, 'name': 'deleted snapshot'}, + {'id': 129, 'name': 'pending_delete snapshot'}, + {'id': 130, 'name': None}] self.assertDictListMatch(response_list, expected) @@ -99,7 +99,7 @@ class ImagesTest(test.TestCase): expected_image = { "image": { - "id": "123", + "id": 123, "name": "public image", "updated": NOW_API_FORMAT, "created": NOW_API_FORMAT, @@ -406,7 +406,7 @@ class ImagesTest(test.TestCase): response_list = response_dict["images"] expected = [{ - 'id': '123', + 'id': 123, 'name': 'public image', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -414,7 +414,7 @@ class ImagesTest(test.TestCase): 'progress': 100, }, { - 'id': '124', + 'id': 124, 'name': 'queued snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -422,7 +422,7 @@ class ImagesTest(test.TestCase): 'progress': 0, }, { - 'id': '125', + 'id': 125, 'name': 'saving snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -430,7 +430,7 @@ class ImagesTest(test.TestCase): 'progress': 0, }, { - 'id': '126', + 'id': 126, 'name': 'active snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -438,7 +438,7 @@ class ImagesTest(test.TestCase): 'progress': 100, }, { - 'id': '127', + 'id': 127, 'name': 'killed snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -446,7 +446,7 @@ class ImagesTest(test.TestCase): 'progress': 0, }, { - 'id': '128', + 'id': 128, 'name': 'deleted snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -454,7 +454,7 @@ class ImagesTest(test.TestCase): 'progress': 0, }, { - 'id': '129', + 'id': 129, 'name': 'pending_delete snapshot', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -462,7 +462,7 @@ class ImagesTest(test.TestCase): 'progress': 0, }, { - 'id': '130', + 'id': 130, 'name': None, 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, @@ -914,7 +914,7 @@ class ImagesTest(test.TestCase): app = fakes.wsgi_app(fake_auth_context=self._get_fake_context()) res = req.get_response(app) image_meta = json.loads(res.body)['image'] - expected = {'id': '123', 'name': 'public image', + expected = {'id': 123, 'name': 'public image', 'updated': NOW_API_FORMAT, 'created': NOW_API_FORMAT, 'status': 'ACTIVE', 'progress': 100} @@ -934,7 +934,7 @@ class ImagesTest(test.TestCase): response = req.get_response(fakes.wsgi_app()) self.assertEqual(200, response.status_int) image_meta = json.loads(response.body)['image'] - self.assertEqual('123', image_meta['serverId']) + self.assertEqual(123, image_meta['serverId']) self.assertEqual('Snapshot 1', image_meta['name']) def test_create_snapshot_no_name(self): -- cgit