summaryrefslogtreecommitdiffstats
path: root/nova
diff options
context:
space:
mode:
authorMichael J Fork <mjfork@us.ibm.com>2013-01-22 22:25:38 +0000
committerMichael J Fork <mjfork@us.ibm.com>2013-02-07 11:50:39 +0000
commit1abe88c44a7e86e80f56f25dfd71f6df44f25289 (patch)
treef5640236a9331ce5ff597e7ee07f3f25567ef24b /nova
parentfe16fded3d4ecfe71d7d0d0d4adbbb8d3cc0da48 (diff)
downloadnova-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
Diffstat (limited to 'nova')
-rw-r--r--nova/network/api.py2
-rw-r--r--nova/network/manager.py52
-rw-r--r--nova/network/rpcapi.py6
-rw-r--r--nova/tests/api/ec2/test_cloud.py3
-rw-r--r--nova/tests/network/test_api.py2
-rw-r--r--nova/tests/network/test_manager.py51
-rw-r--r--nova/tests/network/test_rpcapi.py3
-rw-r--r--nova/tests/test_xenapi.py3
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,