diff options
| author | Michael J Fork <mjfork@us.ibm.com> | 2013-01-22 22:25:38 +0000 |
|---|---|---|
| committer | Michael J Fork <mjfork@us.ibm.com> | 2013-02-07 11:50:39 +0000 |
| commit | 1abe88c44a7e86e80f56f25dfd71f6df44f25289 (patch) | |
| tree | f5640236a9331ce5ff597e7ee07f3f25567ef24b | |
| parent | fe16fded3d4ecfe71d7d0d0d4adbbb8d3cc0da48 (diff) | |
| download | nova-1abe88c44a7e86e80f56f25dfd71f6df44f25289.tar.gz nova-1abe88c44a7e86e80f56f25dfd71f6df44f25289.tar.xz nova-1abe88c44a7e86e80f56f25dfd71f6df44f25289.zip | |
Support hypervisor supplied macs in nova-network
When hypervisors supply a set of MAC addresses for instances, have
nova-network use the supplied values when creating VIFs.
This function is required for the PowerVM driver to be properly
supported.
Change-Id: I922797af6b2b829561f61095bef2098c2d31cde5
| -rw-r--r-- | nova/network/api.py | 2 | ||||
| -rw-r--r-- | nova/network/manager.py | 52 | ||||
| -rw-r--r-- | nova/network/rpcapi.py | 6 | ||||
| -rw-r--r-- | nova/tests/api/ec2/test_cloud.py | 3 | ||||
| -rw-r--r-- | nova/tests/network/test_api.py | 2 | ||||
| -rw-r--r-- | nova/tests/network/test_manager.py | 51 | ||||
| -rw-r--r-- | nova/tests/network/test_rpcapi.py | 3 | ||||
| -rw-r--r-- | nova/tests/test_xenapi.py | 3 |
8 files changed, 99 insertions, 23 deletions
diff --git a/nova/network/api.py b/nova/network/api.py index dd7458b1e..0e19badc2 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -247,7 +247,6 @@ class API(base.Base): :param macs: None or a set of MAC addresses that the instance should use. macs is supplied by the hypervisor driver (contrast with requested_networks which is user supplied). - NB: macs is ignored by nova-network. :returns: network info as from get_instance_nw_info() below """ args = {} @@ -258,6 +257,7 @@ class API(base.Base): args['project_id'] = instance['project_id'] args['host'] = instance['host'] args['rxtx_factor'] = instance['instance_type']['rxtx_factor'] + args['macs'] = macs nw_info = self.network_rpcapi.allocate_for_instance(context, **args) return network_model.NetworkInfo.hydrate(nw_info) diff --git a/nova/network/manager.py b/nova/network/manager.py index b88fe78fc..60aa01494 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -62,6 +62,7 @@ from nova.network import floating_ips from nova.network import model as network_model from nova.network import rpcapi as network_rpcapi from nova.openstack.common import cfg +from nova.openstack.common import excutils from nova.openstack.common import importutils from nova.openstack.common import jsonutils from nova.openstack.common import lockutils @@ -274,7 +275,7 @@ class NetworkManager(manager.SchedulerDependentManager): The one at a time part is to flatten the layout to help scale """ - RPC_API_VERSION = '1.7' + RPC_API_VERSION = '1.8' # If True, this manager requires VIF to create a bridge. SHOULD_CREATE_BRIDGE = False @@ -490,6 +491,7 @@ class NetworkManager(manager.SchedulerDependentManager): rxtx_factor = kwargs['rxtx_factor'] requested_networks = kwargs.get('requested_networks') vpn = kwargs['vpn'] + macs = kwargs['macs'] admin_context = context.elevated() LOG.debug(_("network allocations"), instance_uuid=instance_uuid, context=context) @@ -500,7 +502,17 @@ class NetworkManager(manager.SchedulerDependentManager): for network in networks] LOG.debug(_('networks retrieved for instance: |%(networks_list)s|'), locals(), context=context, instance_uuid=instance_uuid) - self._allocate_mac_addresses(context, instance_uuid, networks) + + try: + self._allocate_mac_addresses(context, instance_uuid, networks, + macs) + except Exception as e: + with excutils.save_and_reraise_exception(): + # If we fail to allocate any one mac address, clean up all + # allocated VIFs + self.db.virtual_interface_delete_by_instance(context, + instance_uuid) + self._allocate_fixed_ips(admin_context, instance_id, host, networks, vpn=vpn, requested_networks=requested_networks) @@ -708,25 +720,43 @@ class NetworkManager(manager.SchedulerDependentManager): return subnets - def _allocate_mac_addresses(self, context, instance_uuid, networks): + def _allocate_mac_addresses(self, context, instance_uuid, networks, macs): """Generates mac addresses and creates vif rows in db for them.""" - for network in networks: - self.add_virtual_interface(context, instance_uuid, network['id']) + # make a copy we can mutate + if macs is not None: + available_macs = set(macs) - def add_virtual_interface(self, context, instance_uuid, network_id): - vif = {'address': utils.generate_mac_address(), + for network in networks: + if macs is None: + self._add_virtual_interface(context, instance_uuid, + network['id']) + else: + try: + mac = available_macs.pop() + except KeyError: + raise exception.VirtualInterfaceCreateException() + self._add_virtual_interface(context, instance_uuid, + network['id'], mac) + + def _add_virtual_interface(self, context, instance_uuid, network_id, + mac=None): + vif = {'address': mac, 'instance_uuid': instance_uuid, 'network_id': network_id, 'uuid': str(uuid.uuid4())} - # try FLAG times to create a vif record with a unique mac_address - for i in xrange(CONF.create_unique_mac_address_attempts): + + if mac is None: + vif['address'] = utils.generate_mac_address() + attempts = CONF.create_unique_mac_address_attempts + else: + attempts = 1 + + for i in range(attempts): try: return self.db.virtual_interface_create(context, vif) except exception.VirtualInterfaceCreateException: vif['address'] = utils.generate_mac_address() else: - self.db.virtual_interface_delete_by_instance(context, - instance_uuid) raise exception.VirtualInterfaceMacAddressException() def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): diff --git a/nova/network/rpcapi.py b/nova/network/rpcapi.py index a7bffe17a..a9056845d 100644 --- a/nova/network/rpcapi.py +++ b/nova/network/rpcapi.py @@ -46,6 +46,7 @@ class NetworkAPI(rpc_proxy.RpcProxy): 1.5 - Adds associate 1.6 - Adds instance_uuid to _{dis,}associate_floating_ip 1.7 - Adds method get_floating_ip_pools to replace get_floating_pools + 1.8 - Adds macs to allocate_for_instance ''' # @@ -151,11 +152,12 @@ class NetworkAPI(rpc_proxy.RpcProxy): def allocate_for_instance(self, ctxt, instance_id, instance_uuid, project_id, host, rxtx_factor, vpn, - requested_networks): + requested_networks, macs=None): return self.call(ctxt, self.make_msg('allocate_for_instance', instance_id=instance_id, instance_uuid=instance_uuid, project_id=project_id, host=host, rxtx_factor=rxtx_factor, - vpn=vpn, requested_networks=requested_networks)) + vpn=vpn, requested_networks=requested_networks, macs=macs), + version='1.8') def deallocate_for_instance(self, ctxt, instance_id, project_id, host): return self.call(ctxt, self.make_msg('deallocate_for_instance', diff --git a/nova/tests/api/ec2/test_cloud.py b/nova/tests/api/ec2/test_cloud.py index c60a0148e..ec0bd3743 100644 --- a/nova/tests/api/ec2/test_cloud.py +++ b/nova/tests/api/ec2/test_cloud.py @@ -269,7 +269,8 @@ class CloudTestCase(test.TestCase): host=inst['host'], vpn=None, rxtx_factor=3, - project_id=project_id) + project_id=project_id, + macs=None) fixed_ips = nw_info.fixed_ips() ec2_id = ec2utils.id_to_ec2_inst_id(inst['uuid']) diff --git a/nova/tests/network/test_api.py b/nova/tests/network/test_api.py index a0179ff94..8e9abe1db 100644 --- a/nova/tests/network/test_api.py +++ b/nova/tests/network/test_api.py @@ -75,7 +75,7 @@ class ApiTestCase(test.TestCase): self.mox.StubOutWithMock( self.network_api.network_rpcapi, "allocate_for_instance") kwargs = dict(zip(['host', 'instance_id', 'instance_uuid', - 'project_id', 'requested_networks', 'rxtx_factor', 'vpn'], + 'project_id', 'requested_networks', 'rxtx_factor', 'vpn', 'macs'], itertools.repeat(mox.IgnoreArg()))) self.network_api.network_rpcapi.allocate_for_instance( mox.IgnoreArg(), **kwargs).AndReturn([]) diff --git a/nova/tests/network/test_manager.py b/nova/tests/network/test_manager.py index 9d467bcb1..0dcd02f78 100644 --- a/nova/tests/network/test_manager.py +++ b/nova/tests/network/test_manager.py @@ -1585,9 +1585,8 @@ class TestFloatingIPManager(floating_ips.FloatingIP, class AllocateTestCase(test.TestCase): - def test_allocate_for_instance(self): - address = "10.10.10.10" - self.flags(auto_assign_floating_ip=True) + def setUp(self): + super(AllocateTestCase, self).setUp() self.conductor = self.start_service( 'conductor', manager=CONF.conductor.manager) self.compute = self.start_service('compute') @@ -1599,6 +1598,10 @@ class AllocateTestCase(test.TestCase): self.project_id, is_admin=True) + def test_allocate_for_instance(self): + address = "10.10.10.10" + self.flags(auto_assign_floating_ip=True) + db.floating_ip_create(self.context, {'address': address, 'pool': 'nova'}) @@ -1613,7 +1616,7 @@ class AllocateTestCase(test.TestCase): nw_info = self.network.allocate_for_instance(self.context, instance_id=inst['id'], instance_uuid=inst['uuid'], host=inst['host'], vpn=None, rxtx_factor=3, - project_id=project_id) + project_id=project_id, macs=None) self.assertEquals(1, len(nw_info)) fixed_ip = nw_info.fixed_ips()[0]['address'] self.assertTrue(utils.is_valid_ipv4(fixed_ip)) @@ -1623,6 +1626,44 @@ class AllocateTestCase(test.TestCase): host=self.network.host, project_id=project_id) + def test_allocate_for_instance_with_mac(self): + available_macs = set(['ca:fe:de:ad:be:ef']) + inst = db.instance_create(self.context, {'host': self.compute.host, + 'display_name': HOST, + 'instance_type_id': 1}) + networks = db.network_get_all(self.context) + for network in networks: + db.network_update(self.context, network['id'], + {'host': self.network.host}) + project_id = self.context.project_id + nw_info = self.network.allocate_for_instance(self.context, + instance_id=inst['id'], instance_uuid=inst['uuid'], + host=inst['host'], vpn=None, rxtx_factor=3, + project_id=project_id, macs=available_macs) + assigned_macs = [vif['address'] for vif in nw_info] + self.assertEquals(1, len(assigned_macs)) + self.assertEquals(available_macs.pop(), assigned_macs[0]) + self.network.deallocate_for_instance(self.context, + instance_id=inst['id'], + host=self.network.host, + project_id=project_id) + + def test_allocate_for_instance_not_enough_macs(self): + available_macs = set() + inst = db.instance_create(self.context, {'host': self.compute.host, + 'display_name': HOST, + 'instance_type_id': 1}) + networks = db.network_get_all(self.context) + for network in networks: + db.network_update(self.context, network['id'], + {'host': self.network.host}) + project_id = self.context.project_id + self.assertRaises(exception.VirtualInterfaceCreateException, + self.network.allocate_for_instance, self.context, + instance_id=inst['id'], instance_uuid=inst['uuid'], + host=inst['host'], vpn=None, rxtx_factor=3, + project_id=project_id, macs=available_macs) + class FloatingIPTestCase(test.TestCase): """Tests nova.network.manager.FloatingIP.""" @@ -2052,7 +2093,7 @@ class FloatingIPTestCase(test.TestCase): # Attempt to add another and make sure that both MACs are consumed # by the retry loop - self.network.add_virtual_interface(ctxt, 'fake_uuid', 'fake_net') + self.network._add_virtual_interface(ctxt, 'fake_uuid', 'fake_net') self.assertEqual(macs, []) def test_deallocate_client_exceptions(self): diff --git a/nova/tests/network/test_rpcapi.py b/nova/tests/network/test_rpcapi.py index 5ba7459fb..132d5a433 100644 --- a/nova/tests/network/test_rpcapi.py +++ b/nova/tests/network/test_rpcapi.py @@ -161,7 +161,8 @@ class NetworkRpcAPITestCase(test.TestCase): self._test_network_api('allocate_for_instance', rpc_method='call', instance_id='fake_id', instance_uuid='fake_uuid', project_id='fake_id', host='fake_host', - rxtx_factor='fake_factor', vpn=False, requested_networks={}) + rxtx_factor='fake_factor', vpn=False, requested_networks={}, + macs=set(), version="1.8") def test_deallocate_for_instance(self): self._test_network_api('deallocate_for_instance', rpc_method='call', diff --git a/nova/tests/test_xenapi.py b/nova/tests/test_xenapi.py index c480d5c5f..9285da2b2 100644 --- a/nova/tests/test_xenapi.py +++ b/nova/tests/test_xenapi.py @@ -855,7 +855,8 @@ class XenAPIVMTestCase(stubs.XenAPITestBase): host=CONF.host, vpn=None, rxtx_factor=3, - project_id=self.project_id) + project_id=self.project_id, + macs=None) self._test_spawn(IMAGE_MACHINE, IMAGE_KERNEL, IMAGE_RAMDISK, |
