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
This commit is contained in:
Michael J Fork
2013-01-22 22:25:38 +00:00
parent fe16fded3d
commit 1abe88c44a
8 changed files with 99 additions and 23 deletions

View File

@@ -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)

View File

@@ -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):

View File

@@ -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',

View File

@@ -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'])

View File

@@ -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([])

View File

@@ -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):

View File

@@ -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',

View File

@@ -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,