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:
@@ -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)
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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([])
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user