Refactor VIFPortIDMixin: factor out common methods

This change is part of a series of changes which aim to refactor the
VIFPortIDMixin mixin class. The refactor aims to reduce code
duplication, and separate the neutron interactions from the more general
VIF attach/detach behaviour.

The neutron separation will be made possible by introducing an
inheritence hierarchy to the mixin class.  VIFPortIDMixin will become a
general VIF attach/detach class, with no neutron interaction.
NeutronVIFPortIDMixin will inherit from VIFPortIDMixin and override the
subclass behaviour to interact with neutron where appropriate.

Once this refactor is complete, a new fake network interface will be
added with similar VIF attach/detach behaviour to the flat and neutron
network interfaces, but without any interaction with neutron. This
interface will be useful in testing environments.

This change refactors the NeutronVIFPortIDMixin class by factoring out
some common methods into a parent class, VIFPortIDMixin. This will help
to avoid code duplication between the classes.

Change-Id: I69a104ec967572f65566b0e0d67145d135be3ac9
This commit is contained in:
Mark Goddard 2017-07-21 08:47:53 +01:00
parent 759ed5d2ea
commit e28ba70620
2 changed files with 552 additions and 329 deletions

View File

@ -296,7 +296,108 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
raise exception.NetworkError(msg)
class NeutronVIFPortIDMixin(object):
class VIFPortIDMixin(object):
"""VIF port ID mixin class for non-neutron network interfaces.
Mixin class that provides VIF-related network interface methods for
non-neutron network interfaces. There are no effects due to VIF
attach/detach that are external to ironic.
NOTE: This does not yet support the full set of VIF methods, as it does
not provide vif_attach, vif_detach, port_changed, or portgroup_changed.
"""
@staticmethod
def _save_vif_to_port_like_obj(port_like_obj, vif_id):
"""Save the ID of a VIF to a port or portgroup.
:param port_like_obj: port-like object to save to.
:param vif_id: VIF ID to save.
"""
int_info = port_like_obj.internal_info
int_info[TENANT_VIF_KEY] = vif_id
port_like_obj.internal_info = int_info
port_like_obj.save()
@staticmethod
def _clear_vif_from_port_like_obj(port_like_obj):
"""Clear the VIF ID field from a port or portgroup.
:param port_like_obj: port-like object to clear from.
"""
int_info = port_like_obj.internal_info
extra = port_like_obj.extra
int_info.pop(TENANT_VIF_KEY, None)
extra.pop('vif_port_id', None)
port_like_obj.extra = extra
port_like_obj.internal_info = int_info
port_like_obj.save()
def _get_port_like_obj_by_vif_id(self, task, vif_id):
"""Lookup a port or portgroup by its attached VIF ID.
:param task: A TaskManager instance.
:param vif_id: ID of the attached VIF.
:returns: A Port or Portgroup object to which the VIF is attached.
:raises: VifNotAttached if the VIF is not attached.
"""
for port_like_obj in task.portgroups + task.ports:
vif_port_id = self._get_vif_id_by_port_like_obj(port_like_obj)
if vif_port_id == vif_id:
return port_like_obj
raise exception.VifNotAttached(vif=vif_id, node=task.node.uuid)
@staticmethod
def _get_vif_id_by_port_like_obj(port_like_obj):
"""Lookup the VIF attached to a port or portgroup.
:param port_like_obj: A port or portgroup to check.
:returns: The ID of the attached VIF, or None.
"""
# FIXME(sambetts) Remove this when we no longer support a nova
# driver that uses port.extra
return (port_like_obj.internal_info.get(TENANT_VIF_KEY) or
port_like_obj.extra.get('vif_port_id'))
def vif_list(self, task):
"""List attached VIF IDs for a node
:param task: A TaskManager instance.
:returns: List of VIF dictionaries, each dictionary will have an 'id'
entry with the ID of the VIF.
"""
vifs = []
for port_like_obj in task.ports + task.portgroups:
vif_id = self._get_vif_id_by_port_like_obj(port_like_obj)
if vif_id:
vifs.append({'id': vif_id})
return vifs
def get_current_vif(self, task, p_obj):
"""Returns the currently used VIF associated with port or portgroup
We are booting the node only in one network at a time, and presence of
cleaning_vif_port_id means we're doing cleaning, of
provisioning_vif_port_id - provisioning.
Otherwise it's a tenant network
:param task: A TaskManager instance.
:param p_obj: Ironic port or portgroup object.
:returns: VIF ID associated with p_obj or None.
"""
return (p_obj.internal_info.get('cleaning_vif_port_id') or
p_obj.internal_info.get('provisioning_vif_port_id') or
self._get_vif_id_by_port_like_obj(p_obj) or None)
class NeutronVIFPortIDMixin(VIFPortIDMixin):
"""VIF port ID mixin class for neutron network interfaces.
Mixin class that provides VIF-related network interface methods for neutron
network interfaces. On VIF attach/detach, the associated neutron port will
be updated.
"""
def port_changed(self, task, port_obj):
"""Handle any actions required when a port changes
@ -313,8 +414,7 @@ class NeutronVIFPortIDMixin(object):
if port_obj.portgroup_id:
portgroup_obj = [pg for pg in task.portgroups if
pg.id == port_obj.portgroup_id][0]
vif = (port_obj.internal_info.get(TENANT_VIF_KEY) or
port_obj.extra.get('vif_port_id'))
vif = self._get_vif_id_by_port_like_obj(port_obj)
if 'address' in port_obj.obj_what_changed():
if vif:
neutron.update_port_address(vif, port_obj.address)
@ -380,8 +480,7 @@ class NeutronVIFPortIDMixin(object):
# Do not touch neutron port if we removed address on portgroup.
if ('address' in portgroup_obj.obj_what_changed() and
portgroup_obj.address):
pg_vif = (portgroup_obj.internal_info.get(TENANT_VIF_KEY) or
portgroup_obj.extra.get('vif_port_id'))
pg_vif = self._get_vif_id_by_port_like_obj(portgroup_obj)
if pg_vif:
neutron.update_port_address(pg_vif, portgroup_obj.address)
@ -399,8 +498,7 @@ class NeutronVIFPortIDMixin(object):
ports = [p for p in task.ports if
p.portgroup_id == portgroup_obj.id]
for p in ports:
vif = p.internal_info.get(
TENANT_VIF_KEY, p.extra.get('vif_port_id'))
vif = self._get_vif_id_by_port_like_obj(p)
reason = []
if p.pxe_enabled:
reason.append("'pxe_enabled' is set to True")
@ -415,21 +513,6 @@ class NeutronVIFPortIDMixin(object):
'reason': ', '.join(reason)})
raise exception.Conflict(msg)
def vif_list(self, task):
"""List attached VIF IDs for a node
:param task: A TaskManager instance.
:returns: List of VIF dictionaries, each dictionary will have an 'id'
entry with the ID of the VIF.
"""
vifs = []
for port_like_obj in task.ports + task.portgroups:
vif_id = port_like_obj.internal_info.get(
TENANT_VIF_KEY, port_like_obj.extra.get('vif_port_id'))
if vif_id:
vifs.append({'id': vif_id})
return vifs
def vif_attach(self, task, vif_info):
"""Attach a virtual network interface to a node
@ -520,10 +603,8 @@ class NeutronVIFPortIDMixin(object):
'vif': vif_id, 'port': vif_id,
'mac': port_like_obj.address})
int_info = port_like_obj.internal_info
int_info[TENANT_VIF_KEY] = vif_id
port_like_obj.internal_info = int_info
port_like_obj.save()
self._save_vif_to_port_like_obj(port_like_obj, vif_id)
# NOTE(vsaienko) allow to attach VIF to active instance.
if task.node.provision_state == states.ACTIVE:
plug_port_to_tenant_network(task, port_like_obj, client=client)
@ -536,44 +617,12 @@ class NeutronVIFPortIDMixin(object):
:raises: VifNotAttached if VIF not attached.
:raises: NetworkError: if unbind Neutron port failed.
"""
# NOTE(mgoddard): Lookup the port first to check that the VIF is
# attached, and fail if not.
port_like_obj = self._get_port_like_obj_by_vif_id(task, vif_id)
# NOTE(vsaienko) We picking object to attach on vif-attach side.
# Here we should only detach VIF and shouldn't duplicate/follow
# attach rules, just walk over all objects and detach VIF.
for port_like_obj in task.portgroups + task.ports:
# FIXME(sambetts) Remove this when we no longer support a nova
# driver that uses port.extra
vif_port_id = port_like_obj.internal_info.get(
TENANT_VIF_KEY, port_like_obj.extra.get("vif_port_id"))
if vif_port_id == vif_id:
int_info = port_like_obj.internal_info
extra = port_like_obj.extra
int_info.pop(TENANT_VIF_KEY, None)
extra.pop('vif_port_id', None)
port_like_obj.extra = extra
port_like_obj.internal_info = int_info
port_like_obj.save()
# NOTE(vsaienko) allow to unplug VIFs from ACTIVE instance.
if task.node.provision_state == states.ACTIVE:
neutron.unbind_neutron_port(vif_port_id)
break
else:
raise exception.VifNotAttached(vif=vif_id, node=task.node.uuid)
self._clear_vif_from_port_like_obj(port_like_obj)
def get_current_vif(self, task, p_obj):
"""Returns the currently used VIF associated with port or portgroup
We are booting the node only in one network at a time, and presence of
cleaning_vif_port_id means we're doing cleaning, of
provisioning_vif_port_id - provisioning.
Otherwise it's a tenant network
:param task: A TaskManager instance.
:param p_obj: Ironic port or portgroup object.
:returns: VIF ID associated with p_obj or None.
"""
return (p_obj.internal_info.get('cleaning_vif_port_id') or
p_obj.internal_info.get('provisioning_vif_port_id') or
p_obj.internal_info.get(TENANT_VIF_KEY) or
p_obj.extra.get('vif_port_id') or None)
# NOTE(vsaienko) allow to unplug VIFs from ACTIVE instance.
if task.node.provision_state == states.ACTIVE:
neutron.unbind_neutron_port(vif_id)

View File

@ -436,13 +436,13 @@ class TestCommonFunctions(db_base.DbTestCase):
task, self.port)
class TestNeutronVifPortIDMixin(db_base.DbTestCase):
class TestVifPortIDMixin(db_base.DbTestCase):
def setUp(self):
super(TestNeutronVifPortIDMixin, self).setUp()
super(TestVifPortIDMixin, self).setUp()
self.config(enabled_drivers=['fake'])
mgr_utils.mock_the_extension_manager()
self.interface = common.NeutronVIFPortIDMixin()
self.interface = common.VIFPortIDMixin()
self.node = obj_utils.create_test_node(self.context,
network_interface='neutron')
self.port = obj_utils.create_test_port(
@ -450,8 +450,121 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase):
address='52:54:00:cf:2d:32',
extra={'vif_port_id': uuidutils.generate_uuid(),
'client-id': 'fake1'})
self.neutron_port = {'id': '132f871f-eaec-4fed-9475-0d54465e0f00',
'mac_address': '52:54:00:cf:2d:32'}
def test__save_vif_to_port_like_obj_port(self):
self.port.extra = {}
self.port.save()
vif_id = "fake_vif_id"
self.interface._save_vif_to_port_like_obj(self.port, vif_id)
self.port.refresh()
self.assertIn(common.TENANT_VIF_KEY, self.port.internal_info)
self.assertEqual(vif_id,
self.port.internal_info[common.TENANT_VIF_KEY])
self.assertEqual({}, self.port.extra)
def test__save_vif_to_port_like_obj_portgroup(self):
vif_id = "fake_vif_id"
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id)
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid()
)
self.interface._save_vif_to_port_like_obj(pg, vif_id)
pg.refresh()
self.assertIn(common.TENANT_VIF_KEY, pg.internal_info)
self.assertEqual(vif_id,
pg.internal_info[common.TENANT_VIF_KEY])
self.assertEqual({}, pg.extra)
def test__clear_vif_from_port_like_obj_in_extra_port(self):
self.interface._clear_vif_from_port_like_obj(self.port)
self.port.refresh()
self.assertNotIn('vif_port_id', self.port.extra)
self.assertNotIn(common.TENANT_VIF_KEY, self.port.internal_info)
def test__clear_vif_from_port_like_obj_in_internal_info_port(self):
self.port.internal_info = {
common.TENANT_VIF_KEY: self.port.extra['vif_port_id']}
self.port.extra = {}
self.port.save()
self.interface._clear_vif_from_port_like_obj(self.port)
self.port.refresh()
self.assertNotIn('vif_port_id', self.port.extra)
self.assertNotIn(common.TENANT_VIF_KEY, self.port.internal_info)
def test__clear_vif_from_port_like_obj_in_extra_portgroup(self):
vif_id = uuidutils.generate_uuid()
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id,
extra={'vif_port_id': vif_id})
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid()
)
self.interface._clear_vif_from_port_like_obj(pg)
pg.refresh()
self.assertNotIn('vif_port_id', pg.extra)
self.assertNotIn(common.TENANT_VIF_KEY, pg.internal_info)
def test__clear_vif_from_port_like_obj_in_internal_info_portgroup(self):
vif_id = uuidutils.generate_uuid()
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id,
internal_info={common.TENANT_VIF_KEY: vif_id})
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid()
)
self.interface._clear_vif_from_port_like_obj(pg)
pg.refresh()
self.assertNotIn('vif_port_id', pg.extra)
self.assertNotIn(common.TENANT_VIF_KEY, pg.internal_info)
def test__get_port_like_obj_by_vif_id_in_extra(self):
vif_id = self.port.extra["vif_port_id"]
with task_manager.acquire(self.context, self.node.id) as task:
result = self.interface._get_port_like_obj_by_vif_id(task, vif_id)
self.assertEqual(self.port.id, result.id)
def test__get_port_like_obj_by_vif_id_in_internal_info(self):
vif_id = self.port.extra["vif_port_id"]
self.port.internal_info = {common.TENANT_VIF_KEY: vif_id}
self.port.extra = {}
self.port.save()
with task_manager.acquire(self.context, self.node.id) as task:
result = self.interface._get_port_like_obj_by_vif_id(task, vif_id)
self.assertEqual(self.port.id, result.id)
def test__get_port_like_obj_by_vif_id_not_attached(self):
vif_id = self.port.extra["vif_port_id"]
self.port.extra = {}
self.port.save()
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex(exception.VifNotAttached,
"it is not attached to it.",
self.interface._get_port_like_obj_by_vif_id,
task, vif_id)
def test__get_vif_id_by_port_like_obj_in_extra(self):
vif_id = self.port.extra["vif_port_id"]
result = self.interface._get_vif_id_by_port_like_obj(self.port)
self.assertEqual(vif_id, result)
def test__get_vif_id_by_port_like_obj_in_internal_info(self):
vif_id = self.port.extra["vif_port_id"]
self.port.internal_info = {common.TENANT_VIF_KEY: vif_id}
self.port.extra = {}
self.port.save()
result = self.interface._get_vif_id_by_port_like_obj(self.port)
self.assertEqual(vif_id, result)
def test__get_vif_id_by_port_like_obj_not_attached(self):
self.port.extra = {}
self.port.save()
result = self.interface._get_vif_id_by_port_like_obj(self.port)
self.assertIsNone(result)
def test_vif_list_extra(self):
vif_id = uuidutils.generate_uuid()
@ -495,265 +608,6 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase):
vifs = self.interface.vif_list(task)
self.assertEqual([{'id': vif_id}], vifs)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach(self, mock_gpbpi, mock_upa, mock_client, mock_gfp):
self.port.extra = {}
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
self.port.refresh()
self.assertEqual("fake_vif_id", self.port.internal_info.get(
common.TENANT_VIF_KEY))
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_with_physnet(self, mock_gpbpi, mock_upa, mock_client,
mock_gfp):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1'}
mock_gfp.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
self.port.refresh()
self.assertEqual("fake_vif_id", self.port.internal_info.get(
common.TENANT_VIF_KEY))
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_gfp.assert_called_once_with(task, 'fake_vif_id', {'physnet1'})
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_portgroup_no_address(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp):
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id, address=None)
mock_gfp.return_value = pg
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
uuid=uuidutils.generate_uuid(), portgroup_id=pg.id)
vif = {'id': "fake_vif_id"}
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
pg.refresh()
self.assertEqual(vif['id'],
pg.internal_info[common.TENANT_VIF_KEY])
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
self.assertFalse(mock_client.return_value.show_port.called)
self.assertFalse(mock_upa.called)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_update_port_exception(self, mock_gpbpi, mock_upa,
mock_client):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1'}
mock_upa.side_effect = (
exception.FailedToUpdateMacOnPort(port_id='fake'))
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex(
exception.NetworkError, "can not update Neutron port",
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_neutron_absent(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
mock_client.return_value.show_port.side_effect = (
neutron_exceptions.NeutronClientException)
mock_gpbpi.side_effect = exception.NetworkError
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
self.assertFalse(mock_upa.called)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client')
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_portgroup_physnet_inconsistent(self, mock_gpbpi,
mock_upa, mock_client,
mock_gfp):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'anyphysnet'}
mock_gfp.side_effect = exception.PortgroupPhysnetInconsistent(
portgroup='fake-portgroup-id', physical_networks='physnet1')
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(
exception.PortgroupPhysnetInconsistent,
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
self.assertFalse(mock_upa.called)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client')
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_multiple_segment_mappings(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
obj_utils.create_test_port(
self.context, node_id=self.node.id, uuid=uuidutils.generate_uuid(),
address='52:54:00:cf:2d:33', physical_network='physnet2')
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1', 'physnet2'}
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(
exception.VifInvalidForAttach,
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
self.assertFalse(mock_gfp.called)
self.assertFalse(mock_upa.called)
def test_vif_detach_in_extra(self):
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(
task, self.port.extra['vif_port_id'])
self.port.refresh()
self.assertFalse('vif_port_id' in self.port.extra)
self.assertFalse(common.TENANT_VIF_KEY in self.port.internal_info)
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
def test_vif_detach_in_internal_info(self, mock_unp):
self.port.internal_info = {
common.TENANT_VIF_KEY: self.port.extra['vif_port_id']}
self.port.extra = {}
self.port.save()
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(
task, self.port.internal_info[common.TENANT_VIF_KEY])
self.port.refresh()
self.assertFalse('vif_port_id' in self.port.extra)
self.assertFalse(common.TENANT_VIF_KEY in self.port.internal_info)
self.assertFalse(mock_unp.called)
def test_vif_detach_in_extra_portgroup(self):
vif_id = uuidutils.generate_uuid()
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id,
extra={'vif_port_id': vif_id})
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid()
)
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, vif_id)
pg.refresh()
self.assertFalse('vif_port_id' in pg.extra)
self.assertFalse(common.TENANT_VIF_KEY in pg.internal_info)
def test_vif_detach_in_extra_portgroup_manually_attached_to_port(self):
vif_id = uuidutils.generate_uuid()
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id)
port = obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid(),
extra={'vif_port_id': vif_id}
)
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, vif_id)
port.refresh()
pg.refresh()
self.assertFalse('vif_port_id' in port.extra)
self.assertFalse(common.TENANT_VIF_KEY in pg.internal_info)
def test_vif_detach_in_internal_info_portgroup(self):
vif_id = uuidutils.generate_uuid()
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id,
internal_info={common.TENANT_VIF_KEY: vif_id})
obj_utils.create_test_port(
self.context, node_id=self.node.id, address='52:54:00:cf:2d:01',
portgroup_id=pg.id, uuid=uuidutils.generate_uuid()
)
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, vif_id)
pg.refresh()
self.assertFalse('vif_port_id' in pg.extra)
self.assertFalse(common.TENANT_VIF_KEY in pg.internal_info)
def test_vif_detach_not_attached(self):
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex(
exception.VifNotAttached, "it is not attached to it.",
self.interface.vif_detach, task, 'aaa')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
def test_vif_detach_active_node(self, mock_unp):
vif_id = self.port.extra['vif_port_id']
self.port.internal_info = {
common.TENANT_VIF_KEY: vif_id}
self.port.extra = {}
self.port.save()
self.node.provision_state = states.ACTIVE
self.node.save()
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(
task, self.port.internal_info[common.TENANT_VIF_KEY])
self.port.refresh()
mock_unp.assert_called_once_with(vif_id)
def test_get_current_vif_extra_vif_port_id(self):
extra = {'vif_port_id': 'foo'}
self.port.extra = extra
@ -797,6 +651,326 @@ class TestNeutronVifPortIDMixin(db_base.DbTestCase):
vif = self.interface.get_current_vif(task, self.port)
self.assertIsNone(vif)
class TestNeutronVifPortIDMixin(db_base.DbTestCase):
def setUp(self):
super(TestNeutronVifPortIDMixin, self).setUp()
self.config(enabled_drivers=['fake'])
mgr_utils.mock_the_extension_manager()
self.interface = common.NeutronVIFPortIDMixin()
self.node = obj_utils.create_test_node(self.context,
network_interface='neutron')
self.port = obj_utils.create_test_port(
self.context, node_id=self.node.id,
address='52:54:00:cf:2d:32',
extra={'vif_port_id': uuidutils.generate_uuid(),
'client-id': 'fake1'})
self.neutron_port = {'id': '132f871f-eaec-4fed-9475-0d54465e0f00',
'mac_address': '52:54:00:cf:2d:32'}
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach(self, mock_gpbpi, mock_upa, mock_client, mock_gfp,
mock_save):
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
mock_save.assert_called_once_with(self.port, "fake_vif_id")
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_failure(self, mock_gpbpi, mock_upa, mock_client,
mock_gfp, mock_save):
vif = {'id': "fake_vif_id"}
mock_gfp.side_effect = exception.NoFreePhysicalPorts(vif='fake-vif')
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(exception.NoFreePhysicalPorts,
self.interface.vif_attach, task, vif)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
self.assertFalse(mock_save.called)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_with_physnet(self, mock_gpbpi, mock_upa, mock_client,
mock_gfp, mock_save):
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1'}
mock_gfp.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_gfp.assert_called_once_with(task, 'fake_vif_id', {'physnet1'})
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
mock_save.assert_called_once_with(self.port, "fake_vif_id")
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'plug_port_to_tenant_network', autospec=True)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_active_node(self, mock_gpbpi, mock_upa, mock_client,
mock_gfp, mock_plug, mock_save):
self.node.provision_state = states.ACTIVE
self.node.save()
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
mock_save.assert_called_once_with(self.port, "fake_vif_id")
mock_plug.assert_called_once_with(task, self.port, mock.ANY)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'plug_port_to_tenant_network', autospec=True)
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_active_node_failure(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp, mock_plug,
mock_save):
self.node.provision_state = states.ACTIVE
self.node.save()
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
mock_plug.side_effect = exception.NetworkError
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(exception.NetworkError,
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
mock_upa.assert_called_once_with("fake_vif_id", self.port.address)
mock_save.assert_called_once_with(self.port, "fake_vif_id")
mock_plug.assert_called_once_with(task, self.port, mock.ANY)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_portgroup_no_address(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp, mock_save):
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id, address=None)
mock_gfp.return_value = pg
vif = {'id': "fake_vif_id"}
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
self.assertFalse(mock_gpbpi.called)
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
self.assertFalse(mock_client.return_value.show_port.called)
self.assertFalse(mock_upa.called)
mock_save.assert_called_once_with(pg, "fake_vif_id")
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_update_port_exception(self, mock_gpbpi, mock_upa,
mock_client, mock_save):
self.port.extra = {}
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1'}
mock_upa.side_effect = (
exception.FailedToUpdateMacOnPort(port_id='fake'))
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex(
exception.NetworkError, "can not update Neutron port",
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
self.assertFalse(mock_save.called)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_neutron_absent(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp, mock_save):
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gfp.return_value = self.port
mock_client.return_value.show_port.side_effect = (
neutron_exceptions.NeutronClientException)
mock_gpbpi.side_effect = exception.NetworkError
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_attach(task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
mock_gfp.assert_called_once_with(task, 'fake_vif_id', set())
mock_client.return_value.show_port.assert_called_once_with(
'fake_vif_id')
self.assertFalse(mock_upa.called)
self.assertTrue(mock_save.called)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client')
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_portgroup_physnet_inconsistent(self, mock_gpbpi,
mock_upa, mock_client,
mock_gfp, mock_save):
self.port.physical_network = 'physnet1'
self.port.save()
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'anyphysnet'}
mock_gfp.side_effect = exception.PortgroupPhysnetInconsistent(
portgroup='fake-portgroup-id', physical_networks='physnet1')
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(
exception.PortgroupPhysnetInconsistent,
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
self.assertFalse(mock_upa.called)
self.assertFalse(mock_save.called)
@mock.patch.object(common.VIFPortIDMixin, '_save_vif_to_port_like_obj')
@mock.patch.object(common, 'get_free_port_like_object', autospec=True)
@mock.patch.object(neutron_common, 'get_client')
@mock.patch.object(neutron_common, 'update_port_address')
@mock.patch.object(neutron_common, 'get_physnets_by_port_uuid',
autospec=True)
def test_vif_attach_multiple_segment_mappings(self, mock_gpbpi, mock_upa,
mock_client, mock_gfp,
mock_save):
self.port.physical_network = 'physnet1'
self.port.save()
obj_utils.create_test_port(
self.context, node_id=self.node.id, uuid=uuidutils.generate_uuid(),
address='52:54:00:cf:2d:33', physical_network='physnet2')
vif = {'id': "fake_vif_id"}
mock_gpbpi.return_value = {'physnet1', 'physnet2'}
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(
exception.VifInvalidForAttach,
self.interface.vif_attach, task, vif)
mock_client.assert_called_once_with()
mock_gpbpi.assert_called_once_with(mock_client.return_value,
'fake_vif_id')
self.assertFalse(mock_gfp.called)
self.assertFalse(mock_upa.called)
self.assertFalse(mock_save.called)
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach(self, mock_get, mock_unp, mock_clear):
mock_get.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, 'fake_vif_id')
mock_get.assert_called_once_with(task, 'fake_vif_id')
self.assertFalse(mock_unp.called)
mock_clear.assert_called_once_with(self.port)
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach_portgroup(self, mock_get, mock_unp, mock_clear):
pg = obj_utils.create_test_portgroup(
self.context, node_id=self.node.id)
mock_get.return_value = pg
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, 'fake_vif_id')
mock_get.assert_called_once_with(task, 'fake_vif_id')
self.assertFalse(mock_unp.called)
mock_clear.assert_called_once_with(pg)
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach_not_attached(self, mock_get, mock_unp, mock_clear):
mock_get.side_effect = exception.VifNotAttached(vif='fake-vif',
node='fake-node')
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex(
exception.VifNotAttached, "it is not attached to it.",
self.interface.vif_detach, task, 'fake_vif_id')
mock_get.assert_called_once_with(task, 'fake_vif_id')
self.assertFalse(mock_unp.called)
self.assertFalse(mock_clear.called)
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach_active_node(self, mock_get, mock_unp, mock_clear):
self.node.provision_state = states.ACTIVE
self.node.save()
mock_get.return_value = self.port
with task_manager.acquire(self.context, self.node.id) as task:
self.interface.vif_detach(task, 'fake_vif_id')
mock_get.assert_called_once_with(task, 'fake_vif_id')
mock_clear.assert_called_once_with(self.port)
mock_unp.assert_called_once_with('fake_vif_id')
@mock.patch.object(common.VIFPortIDMixin, '_clear_vif_from_port_like_obj')
@mock.patch.object(neutron_common, 'unbind_neutron_port', autospec=True)
@mock.patch.object(common.VIFPortIDMixin, '_get_port_like_obj_by_vif_id')
def test_vif_detach_active_node_failure(self, mock_get, mock_unp,
mock_clear):
self.node.provision_state = states.ACTIVE
self.node.save()
mock_get.return_value = self.port
mock_unp.side_effect = exception.NetworkError
with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaises(exception.NetworkError,
self.interface.vif_detach, task, 'fake_vif_id')
mock_get.assert_called_once_with(task, 'fake_vif_id')
mock_clear.assert_called_once_with(self.port)
mock_unp.assert_called_once_with('fake_vif_id')
@mock.patch.object(common_utils, 'warn_about_deprecated_extra_vif_port_id',
autospec=True)
@mock.patch.object(neutron_common, 'update_port_address', autospec=True)