diff options
-rw-r--r-- | nova/compute/manager.py | 9 | ||||
-rw-r--r-- | nova/network/api.py | 8 | ||||
-rw-r--r-- | nova/network/quantumv2/api.py | 10 | ||||
-rw-r--r-- | nova/tests/compute/test_compute.py | 24 | ||||
-rw-r--r-- | nova/tests/network/test_api.py | 22 | ||||
-rw-r--r-- | nova/tests/network/test_quantumv2.py | 8 | ||||
-rw-r--r-- | nova/virt/driver.py | 29 |
7 files changed, 103 insertions, 7 deletions
diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 4fa88084b..a4f17ee3d 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -678,9 +678,9 @@ class ComputeManager(manager.SchedulerDependentManager): try: limits = filter_properties.get('limits', {}) with rt.instance_claim(context, instance, limits): - + macs = self.driver.macs_for_instance(instance) network_info = self._allocate_network(context, instance, - requested_networks) + requested_networks, macs) block_device_info = self._prep_block_device(context, instance, bdms) instance = self._spawn(context, instance, image_meta, @@ -911,7 +911,7 @@ class ComputeManager(manager.SchedulerDependentManager): expected_task_state=(task_states.SCHEDULING, None)) - def _allocate_network(self, context, instance, requested_networks): + def _allocate_network(self, context, instance, requested_networks, macs): """Allocate networks for an instance and return the network info.""" instance = self._instance_update(context, instance['uuid'], vm_state=vm_states.BUILDING, @@ -922,7 +922,8 @@ class ComputeManager(manager.SchedulerDependentManager): # allocate and get network info network_info = self.network_api.allocate_for_instance( context, instance, vpn=is_vpn, - requested_networks=requested_networks) + requested_networks=requested_networks, + macs=macs) except Exception: LOG.exception(_('Instance failed network setup'), instance=instance) diff --git a/nova/network/api.py b/nova/network/api.py index 159faf6b3..976be93ed 100644 --- a/nova/network/api.py +++ b/nova/network/api.py @@ -184,9 +184,15 @@ class API(base.Base): @refresh_cache def allocate_for_instance(self, context, instance, vpn, - requested_networks): + requested_networks, macs=None): """Allocates all network structures for an instance. + TODO(someone): document the rest of these parameters. + + :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 = {} diff --git a/nova/network/quantumv2/api.py b/nova/network/quantumv2/api.py index e04d10edb..780952bab 100644 --- a/nova/network/quantumv2/api.py +++ b/nova/network/quantumv2/api.py @@ -104,7 +104,15 @@ class API(base.Base): return nets def allocate_for_instance(self, context, instance, **kwargs): - """Allocate all network resources for the instance.""" + """Allocate all network resources for the instance. + + TODO(someone): document the rest of these parameters. + + :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: QuantumV2 does not yet honour mac address limits. + """ quantum = quantumv2.get_client(context) LOG.debug(_('allocate_for_instance() for %s'), instance['display_name']) diff --git a/nova/tests/compute/test_compute.py b/nova/tests/compute/test_compute.py index 08d9451b3..7285c385f 100644 --- a/nova/tests/compute/test_compute.py +++ b/nova/tests/compute/test_compute.py @@ -1510,6 +1510,27 @@ class ComputeTestCase(BaseTestCase): instance=instance) self.compute.terminate_instance(self.context, instance=instance) + def test_run_instance_queries_macs(self): + # run_instance should ask the driver for node mac addresses and pass + # that to the network_api in use. + fake_network.unset_stub_network_methods(self.stubs) + instance = jsonutils.to_primitive(self._create_fake_instance()) + + macs = set(['01:23:45:67:89:ab']) + self.mox.StubOutWithMock(self.compute.network_api, + "allocate_for_instance") + self.compute.network_api.allocate_for_instance( + mox.IgnoreArg(), + mox.IgnoreArg(), + requested_networks=None, + vpn=False, macs=macs).AndReturn( + fake_network.fake_get_instance_nw_info(self.stubs, 1, 1, + spectacular=True)) + self.mox.StubOutWithMock(self.compute.driver, "macs_for_instance") + self.compute.driver.macs_for_instance(instance).AndReturn(macs) + self.mox.ReplayAll() + self.compute.run_instance(self.context, instance=instance) + def test_instance_set_to_error_on_uncaught_exception(self): # Test that instance is set to error state when exception is raised. instance = jsonutils.to_primitive(self._create_fake_instance()) @@ -1520,7 +1541,8 @@ class ComputeTestCase(BaseTestCase): mox.IgnoreArg(), mox.IgnoreArg(), requested_networks=None, - vpn=False).AndRaise(rpc_common.RemoteError()) + vpn=False, + macs=None).AndRaise(rpc_common.RemoteError()) fake_network.unset_stub_network_methods(self.stubs) diff --git a/nova/tests/network/test_api.py b/nova/tests/network/test_api.py index 94cccd9d9..959c5a472 100644 --- a/nova/tests/network/test_api.py +++ b/nova/tests/network/test_api.py @@ -17,8 +17,11 @@ """Tests for network API.""" +import itertools import random +import mox + from nova import context from nova import exception from nova import network @@ -37,6 +40,25 @@ class ApiTestCase(test.TestCase): self.context = context.RequestContext('fake-user', 'fake-project') + def test_allocate_for_instance_handles_macs_passed(self): + # If a macs argument is supplied to the 'nova-network' API, it is just + # ignored. This test checks that the call down to the rpcapi layer + # doesn't pass macs down: nova-network doesn't support hypervisor + # mac address limits (today anyhow). + macs = set(['ab:cd:ef:01:23:34']) + 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'], + itertools.repeat(mox.IgnoreArg()))) + self.network_api.network_rpcapi.allocate_for_instance( + mox.IgnoreArg(), **kwargs).AndReturn([]) + self.mox.ReplayAll() + instance = dict(id='id', uuid='uuid', project_id='project_id', + host='host', instance_type={'rxtx_factor': 0}) + self.network_api.allocate_for_instance( + 'context', instance, 'vpn', 'requested_networks', macs=macs) + def _do_test_associate_floating_ip(self, orig_instance_uuid): """Test post-association logic.""" diff --git a/nova/tests/network/test_quantumv2.py b/nova/tests/network/test_quantumv2.py index 004e76071..f92dba443 100644 --- a/nova/tests/network/test_quantumv2.py +++ b/nova/tests/network/test_quantumv2.py @@ -420,6 +420,14 @@ class TestQuantumv2(test.TestCase): # Allocate one port in two networks env. self._allocate_for_instance(2) + def test_allocate_for_instance_accepts_macs_kwargs_None(self): + # The macs kwarg should be accepted as None. + self._allocate_for_instance(1, macs=None) + + def test_allocate_for_instance_accepts_macs_kwargs_set(self): + # The macs kwarg should be accepted, as a set. + self._allocate_for_instance(1, macs=set(['ab:cd:ef:01:23:45'])) + def test_allocate_for_instance_with_requested_networks(self): # specify only first and last network requested_networks = [ diff --git a/nova/virt/driver.py b/nova/virt/driver.py index e396de6a0..a8f779e66 100644 --- a/nova/virt/driver.py +++ b/nova/virt/driver.py @@ -732,6 +732,35 @@ class ComputeDriver(object): # related helpers. raise NotImplementedError(self.legacy_nwinfo) + def macs_for_instance(self, instance): + """What MAC addresses must this instance have? + + Some hypervisors (such as bare metal) cannot do freeform virtualisation + of MAC addresses. This method allows drivers to return a set of MAC + addresses that the instance is to have. allocate_for_instance will take + this into consideration when provisioning networking for the instance. + + Mapping of MAC addresses to actual networks (or permitting them to be + freeform) is up to the network implementation layer. For instance, + with openflow switches, fixed MAC addresses can still be virtualised + onto any L2 domain, with arbitrary VLANs etc, but regular switches + require pre-configured MAC->network mappings that will match the + actual configuration. + + Most hypervisors can use the default implementation which returns None. + Hypervisors with MAC limits should return a set of MAC addresses, which + will be supplied to the allocate_for_instance call by the compute + manager, and it is up to that call to ensure that all assigned network + details are compatible with the set of MAC addresses. + + This is called during spawn_instance by the compute manager. + + :return: None, or a set of MAC ids (e.g. set(['12:34:56:78:90:ab'])). + None means 'no constraints', a set means 'these and only these + MAC addresses'. + """ + return None + def manage_image_cache(self, context, all_instances): """ Manage the driver's local image cache. |