Use auth values from neutron conf when managing Neutron ports

Using the auth values from the neutron section of the Ironic
configuration prevents issues where a non-admin user is not
allowed to manage Neutron ports.

Change-Id: I66cf1acba10a7b7b82f5a2a55453c3d5a29a086e
Story: #2006506
Task: #38789
This commit is contained in:
Tzu-Mainn Chen 2020-02-18 19:21:12 +00:00
parent b148cabdb2
commit d15d2f09fb
10 changed files with 318 additions and 127 deletions

View File

@ -12,6 +12,7 @@
import copy import copy
from keystoneauth1 import loading as ks_loading
import netaddr import netaddr
from neutronclient.common import exceptions as neutron_exceptions from neutronclient.common import exceptions as neutron_exceptions
from neutronclient.v2_0 import client as clientv20 from neutronclient.v2_0 import client as clientv20
@ -79,6 +80,49 @@ def get_client(token=None, context=None):
timeout=CONF.neutron.request_timeout) timeout=CONF.neutron.request_timeout)
def _get_conf_client(context):
"""Retrieve a neutron client connection using conf parameters.
:param context: request context,
instance of ironic.common.context.RequestContext
:returns: A neutron client.
"""
auth = ks_loading.load_auth_from_conf_options(CONF, 'neutron')
session = ks_loading.load_session_from_conf_options(
CONF,
'neutron',
auth=auth)
endpoint = keystone.get_endpoint('neutron', session=session,
auth=auth)
return clientv20.Client(session=session,
auth=auth,
endpoint_override=endpoint,
retries=CONF.neutron.retries,
global_request_id=context.global_id,
timeout=CONF.neutron.request_timeout)
def update_neutron_port(context, port_id, update_body, client=None):
"""Undate a neutron port
Uses neutron client from conf client to update a neutron client
an unbound state.
:param context: request context,
instance of ironic.common.context.RequestContext
:param port_id: Neutron port ID.
:param update_body: Body of update
:param client: Optional Neutron client
"""
if not client:
# verify that user can see the port before updating it
get_client(context=context).show_port(port_id)
client = _get_conf_client(context)
return client.update_port(port_id, update_body)
def unbind_neutron_port(port_id, client=None, context=None): def unbind_neutron_port(port_id, client=None, context=None):
"""Unbind a neutron port """Unbind a neutron port
@ -92,20 +136,17 @@ def unbind_neutron_port(port_id, client=None, context=None):
:raises: NetworkError :raises: NetworkError
""" """
if not client:
client = get_client(context=context)
body_unbind = {'port': {'binding:host_id': '', body_unbind = {'port': {'binding:host_id': '',
'binding:profile': {}}} 'binding:profile': {}}}
body_reset_mac = {'port': {'mac_address': None}} body_reset_mac = {'port': {'mac_address': None}}
try: try:
client.update_port(port_id, body_unbind) update_neutron_port(context, port_id, body_unbind, client)
# NOTE(hjensas): We need to reset the mac address in a separate step. # NOTE(hjensas): We need to reset the mac address in a separate step.
# Exception PortBound will be raised by neutron as it refuses to # Exception PortBound will be raised by neutron as it refuses to
# update the mac address of a bound port if we attempt to unbind and # update the mac address of a bound port if we attempt to unbind and
# reset the mac in the same call. # reset the mac in the same call.
client.update_port(port_id, body_reset_mac) update_neutron_port(context, port_id, body_reset_mac, client)
# NOTE(vsaienko): Ignore if port was deleted before calling vif detach. # NOTE(vsaienko): Ignore if port was deleted before calling vif detach.
except neutron_exceptions.PortNotFoundClient: except neutron_exceptions.PortNotFoundClient:
LOG.info('Port %s was not found while unbinding.', port_id) LOG.info('Port %s was not found while unbinding.', port_id)
@ -142,10 +183,10 @@ def update_port_address(port_id, address, context=None):
msg = (_("Failed to remove the current binding from " msg = (_("Failed to remove the current binding from "
"Neutron port %s, while updating its MAC " "Neutron port %s, while updating its MAC "
"address.") % port_id) "address.") % port_id)
unbind_neutron_port(port_id, client=client, context=context) unbind_neutron_port(port_id, context=context)
msg = (_("Failed to update MAC address on Neutron port %s.") % port_id) msg = (_("Failed to update MAC address on Neutron port %s.") % port_id)
client.update_port(port_id, port_req_body) update_neutron_port(context, port_id, port_req_body)
# Restore original binding:profile and host_id # Restore original binding:profile and host_id
if binding_host_id: if binding_host_id:
@ -154,7 +195,7 @@ def update_port_address(port_id, address, context=None):
port_req_body = {'port': {'binding:host_id': binding_host_id, port_req_body = {'port': {'binding:host_id': binding_host_id,
'binding:profile': binding_profile}} 'binding:profile': binding_profile}}
client.update_port(port_id, port_req_body) update_neutron_port(context, port_id, port_req_body)
except (neutron_exceptions.NeutronClientException, exception.NetworkError): except (neutron_exceptions.NeutronClientException, exception.NetworkError):
LOG.exception(msg) LOG.exception(msg)
raise exception.FailedToUpdateMacOnPort(port_id=port_id) raise exception.FailedToUpdateMacOnPort(port_id=port_id)
@ -193,7 +234,7 @@ def _verify_security_groups(security_groups, client):
raise exception.NetworkError(msg) raise exception.NetworkError(msg)
def _add_ip_addresses_for_ipv6_stateful(port, client): def _add_ip_addresses_for_ipv6_stateful(context, port, client):
"""Add additional IP addresses to the ipv6 stateful neutron port """Add additional IP addresses to the ipv6 stateful neutron port
When network booting with DHCPv6-stateful we cannot control the CLID/IAID When network booting with DHCPv6-stateful we cannot control the CLID/IAID
@ -216,7 +257,7 @@ def _add_ip_addresses_for_ipv6_stateful(port, client):
fixed_ips.append({'subnet_id': subnet['id']}) fixed_ips.append({'subnet_id': subnet['id']})
body = {'port': {'fixed_ips': fixed_ips}} body = {'port': {'fixed_ips': fixed_ips}}
client.update_port(port['port']['id'], body) update_neutron_port(context, port['port']['id'], body)
def add_ports_to_network(task, network_uuid, security_groups=None): def add_ports_to_network(task, network_uuid, security_groups=None):
@ -254,8 +295,13 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
'network_id': network_uuid, 'network_id': network_uuid,
'admin_state_up': True, 'admin_state_up': True,
'binding:vnic_type': VNIC_BAREMETAL, 'binding:vnic_type': VNIC_BAREMETAL,
'device_owner': 'baremetal:none', }
}
# separate out fields that can only be updated by admins
update_body = {
'port': {
'binding:host_id': node.uuid, 'binding:host_id': node.uuid,
'device_owner': 'baremetal:none',
} }
} }
if security_groups: if security_groups:
@ -283,14 +329,15 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
for ironic_port in ports_to_create: for ironic_port in ports_to_create:
# Start with a clean state for each port # Start with a clean state for each port
port_body = copy.deepcopy(body) port_body = copy.deepcopy(body)
update_port_body = copy.deepcopy(update_body)
# Skip ports that are missing required information for deploy. # Skip ports that are missing required information for deploy.
if not validate_port_info(node, ironic_port): if not validate_port_info(node, ironic_port):
failures.append(ironic_port.uuid) failures.append(ironic_port.uuid)
continue continue
port_body['port']['mac_address'] = ironic_port.address update_port_body['port']['mac_address'] = ironic_port.address
binding_profile = {'local_link_information': binding_profile = {'local_link_information':
[portmap[ironic_port.uuid]]} [portmap[ironic_port.uuid]]}
port_body['port']['binding:profile'] = binding_profile update_port_body['port']['binding:profile'] = binding_profile
if not ironic_port.pxe_enabled: if not ironic_port.pxe_enabled:
LOG.debug("Adding port %(port)s to network %(net)s for " LOG.debug("Adding port %(port)s to network %(net)s for "
@ -306,7 +353,7 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
'port %(port_id)s, hostname %(hostname)s', 'port %(port_id)s, hostname %(hostname)s',
{'port_id': ironic_port.uuid, {'port_id': ironic_port.uuid,
'hostname': link_info['hostname']}) 'hostname': link_info['hostname']})
port_body['port']['binding:host_id'] = link_info['hostname'] update_port_body['port']['binding:host_id'] = link_info['hostname']
# TODO(hamdyk): use portbindings.VNIC_SMARTNIC from neutron-lib # TODO(hamdyk): use portbindings.VNIC_SMARTNIC from neutron-lib
port_body['port']['binding:vnic_type'] = VNIC_SMARTNIC port_body['port']['binding:vnic_type'] = VNIC_SMARTNIC
@ -319,11 +366,13 @@ def add_ports_to_network(task, network_uuid, security_groups=None):
port_body['port']['extra_dhcp_opts'] = extra_dhcp_opts port_body['port']['extra_dhcp_opts'] = extra_dhcp_opts
try: try:
if is_smart_nic: if is_smart_nic:
wait_for_host_agent(client, wait_for_host_agent(
port_body['port']['binding:host_id']) client, update_port_body['port']['binding:host_id'])
port = client.create_port(port_body) port = client.create_port(port_body)
update_neutron_port(task.context, port['port']['id'],
update_port_body)
if CONF.neutron.dhcpv6_stateful_address_count > 1: if CONF.neutron.dhcpv6_stateful_address_count > 1:
_add_ip_addresses_for_ipv6_stateful(port, client) _add_ip_addresses_for_ipv6_stateful(task.context, port, client)
if is_smart_nic: if is_smart_nic:
wait_for_port_status(client, port['port']['id'], 'ACTIVE') wait_for_port_status(client, port['port']['id'], 'ACTIVE')
except neutron_exceptions.NeutronClientException as e: except neutron_exceptions.NeutronClientException as e:

View File

@ -61,8 +61,7 @@ class NeutronDHCPApi(base.BaseDHCP):
port_id, dhcp_options, token=token, context=context) port_id, dhcp_options, token=token, context=context)
port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}} port_req_body = {'port': {'extra_dhcp_opts': dhcp_options}}
try: try:
neutron.get_client(token=token, context=context).update_port( neutron.update_neutron_port(context, port_id, port_req_body)
port_id, port_req_body)
except neutron_client_exc.NeutronClientException: except neutron_client_exc.NeutronClientException:
LOG.exception("Failed to update Neutron port %s.", port_id) LOG.exception("Failed to update Neutron port %s.", port_id)
raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id) raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id)

View File

@ -283,7 +283,7 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None):
neutron.wait_for_host_agent(client, body['port']['binding:host_id']) neutron.wait_for_host_agent(client, body['port']['binding:host_id'])
try: try:
client.update_port(vif_id, body) neutron.update_neutron_port(task.context, vif_id, body)
if is_smart_nic: if is_smart_nic:
neutron.wait_for_port_status(client, vif_id, 'ACTIVE') neutron.wait_for_port_status(client, vif_id, 'ACTIVE')
except neutron_exceptions.ConnectionFailed as e: except neutron_exceptions.ConnectionFailed as e:

View File

@ -55,7 +55,6 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
def _bind_flat_ports(self, task): def _bind_flat_ports(self, task):
LOG.debug("Binding flat network ports") LOG.debug("Binding flat network ports")
client = neutron.get_client(context=task.context)
for port_like_obj in task.ports + task.portgroups: for port_like_obj in task.ports + task.portgroups:
vif_port_id = ( vif_port_id = (
port_like_obj.internal_info.get(common.TENANT_VIF_KEY) port_like_obj.internal_info.get(common.TENANT_VIF_KEY)
@ -71,7 +70,8 @@ class FlatNetwork(common.NeutronVIFPortIDMixin,
} }
} }
try: try:
client.update_port(vif_port_id, body) neutron.update_neutron_port(task.context,
vif_port_id, body)
except neutron_exceptions.NeutronClientException as e: except neutron_exceptions.NeutronClientException as e:
msg = (_('Unable to set binding:host_id for ' msg = (_('Unable to set binding:host_id for '
'neutron port %(port_id)s. Error: ' 'neutron port %(port_id)s. Error: '

View File

@ -145,6 +145,104 @@ class TestNeutronClient(base.TestCase):
self.assertEqual(0, mock_sauth.call_count) self.assertEqual(0, mock_sauth.call_count)
class TestNeutronConfClient(base.TestCase):
def setUp(self):
super(TestNeutronConfClient, self).setUp()
# NOTE(pas-ha) register keystoneauth dynamic options manually
plugin = kaloading.get_plugin_loader('password')
opts = kaloading.get_auth_plugin_conf_options(plugin)
self.cfg_fixture.register_opts(opts, group='neutron')
self.config(retries=2,
group='neutron')
self.config(username='test-admin-user',
project_name='test-admin-tenant',
password='test-admin-password',
auth_url='test-auth-uri',
auth_type='password',
interface='internal',
service_type='network',
timeout=10,
group='neutron')
# force-reset the global session object
neutron._NEUTRON_SESSION = None
self.context = context.RequestContext(global_request_id='global')
@mock.patch('keystoneauth1.loading.load_auth_from_conf_options',
autospec=True, return_value=mock.sentinel.auth)
@mock.patch('keystoneauth1.loading.load_session_from_conf_options',
autospec=True, return_value=mock.sentinel.session)
@mock.patch('ironic.common.keystone.get_endpoint', autospec=True,
return_value='neutron_url')
@mock.patch.object(client.Client, "__init__", return_value=None,
autospec=True)
def test_get_neutron_conf_client(self, mock_client, mock_get_endpoint,
mock_session, mock_auth):
neutron._get_conf_client(self.context)
mock_client.assert_called_once_with(mock.ANY, # this is 'self'
session=mock.sentinel.session,
auth=mock.sentinel.auth, retries=2,
endpoint_override='neutron_url',
global_request_id='global',
timeout=45)
class TestUpdateNeutronPort(base.TestCase):
def setUp(self):
super(TestUpdateNeutronPort, self).setUp()
self.uuid = uuidutils.generate_uuid()
self.context = context.RequestContext()
self.update_body = {'port': {}}
@mock.patch.object(neutron, 'get_client', autospec=True)
@mock.patch.object(neutron, '_get_conf_client', autospec=True)
def test_update_neutron_port(self, conf_client_mock, client_mock):
client_mock.return_value.show_port.return_value = {'port': {}}
conf_client_mock.return_value.update_port.return_value = {'port': {}}
neutron.update_neutron_port(self.context, self.uuid, self.update_body)
client_mock.assert_called_once_with(context=self.context)
client_mock.return_value.show_port.assert_called_once_with(self.uuid)
conf_client_mock.assert_called_once_with(self.context)
conf_client_mock.return_value.update_port.assert_called_once_with(
self.uuid, self.update_body)
@mock.patch.object(neutron, 'get_client', autospec=True)
@mock.patch.object(neutron, '_get_conf_client', autospec=True)
def test_update_neutron_port_with_client(self, conf_client_mock,
client_mock):
client_mock.return_value.show_port.return_value = {'port': {}}
conf_client_mock.return_value.update_port.return_value = {'port': {}}
client = mock.Mock()
client.update_port.return_value = {'port': {}}
neutron.update_neutron_port(self.context, self.uuid, self.update_body,
client)
self.assertFalse(client_mock.called)
self.assertFalse(conf_client_mock.called)
client.update_port.assert_called_once_with(self.uuid, self.update_body)
@mock.patch.object(neutron, 'get_client', autospec=True)
@mock.patch.object(neutron, '_get_conf_client', autospec=True)
def test_update_neutron_port_with_exception(self, conf_client_mock,
client_mock):
client_mock.return_value.show_port.side_effect = \
neutron_client_exc.NeutronClientException
conf_client_mock.return_value.update_port.return_value = {'port': {}}
self.assertRaises(
neutron_client_exc.NeutronClientException,
neutron.update_neutron_port,
self.context, self.uuid, self.update_body)
client_mock.assert_called_once_with(context=self.context)
client_mock.return_value.show_port.assert_called_once_with(self.uuid)
self.assertFalse(conf_client_mock.called)
class TestNeutronNetworkActions(db_base.DbTestCase): class TestNeutronNetworkActions(db_base.DbTestCase):
_CLIENT_ID = ( _CLIENT_ID = (
@ -172,7 +270,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
patcher.start() patcher.start()
self.addCleanup(patcher.stop) self.addCleanup(patcher.stop)
def _test_add_ports_to_network(self, is_client_id, @mock.patch.object(neutron, 'update_neutron_port', autospec=True)
def _test_add_ports_to_network(self, update_mock, is_client_id,
security_groups=None, security_groups=None,
add_all_ports=False): add_all_ports=False):
# Ports will be created only if pxe_enabled is True # Ports will be created only if pxe_enabled is True
@ -192,14 +291,18 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
extra['client-id'] = self._CLIENT_ID extra['client-id'] = self._CLIENT_ID
port.extra = extra port.extra = extra
port.save() port.save()
expected_body = { expected_create_body = {
'port': { 'port': {
'network_id': self.network_uuid, 'network_id': self.network_uuid,
'admin_state_up': True, 'admin_state_up': True,
'binding:vnic_type': 'baremetal', 'binding:vnic_type': 'baremetal',
'device_id': self.node.uuid,
}
}
expected_update_body = {
'port': {
'device_owner': 'baremetal:none', 'device_owner': 'baremetal:none',
'binding:host_id': self.node.uuid, 'binding:host_id': self.node.uuid,
'device_id': self.node.uuid,
'mac_address': port.address, 'mac_address': port.address,
'binding:profile': { 'binding:profile': {
'local_link_information': [port.local_link_connection] 'local_link_information': [port.local_link_connection]
@ -207,16 +310,17 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
} }
} }
if security_groups: if security_groups:
expected_body['port']['security_groups'] = security_groups expected_create_body['port']['security_groups'] = security_groups
if is_client_id: if is_client_id:
expected_body['port']['extra_dhcp_opts'] = ( expected_create_body['port']['extra_dhcp_opts'] = (
[{'opt_name': '61', 'opt_value': self._CLIENT_ID}]) [{'opt_name': '61', 'opt_value': self._CLIENT_ID}])
if add_all_ports: if add_all_ports:
expected_body2 = copy.deepcopy(expected_body) expected_create_body2 = copy.deepcopy(expected_create_body)
expected_body2['port']['mac_address'] = port2.address expected_update_body2 = copy.deepcopy(expected_update_body)
expected_body2['fixed_ips'] = [] expected_update_body2['port']['mac_address'] = port2.address
expected_create_body2['fixed_ips'] = []
neutron_port2 = {'id': '132f871f-eaec-4fed-9475-0d54465e0f01', neutron_port2 = {'id': '132f871f-eaec-4fed-9475-0d54465e0f01',
'mac_address': port2.address, 'mac_address': port2.address,
'fixed_ips': []} 'fixed_ips': []}
@ -237,12 +341,21 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
task, self.network_uuid, security_groups=security_groups) task, self.network_uuid, security_groups=security_groups)
self.assertEqual(expected, ports) self.assertEqual(expected, ports)
if add_all_ports: if add_all_ports:
calls = [mock.call(expected_body), create_calls = [mock.call(expected_create_body),
mock.call(expected_body2)] mock.call(expected_create_body2)]
self.client_mock.create_port.assert_has_calls(calls) update_calls = [
mock.call(self.context, self.neutron_port['id'],
expected_update_body),
mock.call(self.context, neutron_port2['id'],
expected_update_body2)]
self.client_mock.create_port.assert_has_calls(create_calls)
update_mock.assert_has_calls(update_calls)
else: else:
self.client_mock.create_port.assert_called_once_with( self.client_mock.create_port.assert_called_once_with(
expected_body) expected_create_body)
update_mock.assert_called_once_with(
self.context, self.neutron_port['id'],
expected_update_body)
def test_add_ports_to_network(self): def test_add_ports_to_network(self):
self._test_add_ports_to_network(is_client_id=False, self._test_add_ports_to_network(is_client_id=False,
@ -261,7 +374,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
self._test_add_ports_to_network(is_client_id=False, self._test_add_ports_to_network(is_client_id=False,
security_groups=sg_ids) security_groups=sg_ids)
def test__add_ip_addresses_for_ipv6_stateful(self): @mock.patch.object(neutron, 'update_neutron_port', autospec=True)
def test__add_ip_addresses_for_ipv6_stateful(self, mock_update):
subnet_id = uuidutils.generate_uuid() subnet_id = uuidutils.generate_uuid()
self.client_mock.show_subnet.return_value = { self.client_mock.show_subnet.return_value = {
'subnet': { 'subnet': {
@ -285,11 +399,12 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
} }
neutron._add_ip_addresses_for_ipv6_stateful( neutron._add_ip_addresses_for_ipv6_stateful(
self.context,
{'port': self.neutron_port}, {'port': self.neutron_port},
self.client_mock self.client_mock
) )
self.client_mock.update_port.assert_called_once_with( mock_update.assert_called_once_with(
self.neutron_port['id'], expected_body) self.context, self.neutron_port['id'], expected_body)
def test_verify_sec_groups(self): def test_verify_sec_groups(self):
sg_ids = [] sg_ids = []
@ -371,20 +486,25 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
def test_add_ports_with_client_id_to_network(self): def test_add_ports_with_client_id_to_network(self):
self._test_add_ports_to_network(is_client_id=True) self._test_add_ports_to_network(is_client_id=True)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'validate_port_info', autospec=True) @mock.patch.object(neutron, 'validate_port_info', autospec=True)
def test_add_ports_to_network_instance_uuid(self, vpi_mock): def test_add_ports_to_network_instance_uuid(self, vpi_mock, update_mock):
self.node.instance_uuid = uuidutils.generate_uuid() self.node.instance_uuid = uuidutils.generate_uuid()
self.node.network_interface = 'neutron' self.node.network_interface = 'neutron'
self.node.save() self.node.save()
port = self.ports[0] port = self.ports[0]
expected_body = { expected_create_body = {
'port': { 'port': {
'network_id': self.network_uuid, 'network_id': self.network_uuid,
'admin_state_up': True, 'admin_state_up': True,
'binding:vnic_type': 'baremetal', 'binding:vnic_type': 'baremetal',
'device_id': self.node.instance_uuid,
}
}
expected_update_body = {
'port': {
'device_owner': 'baremetal:none', 'device_owner': 'baremetal:none',
'binding:host_id': self.node.uuid, 'binding:host_id': self.node.uuid,
'device_id': self.node.instance_uuid,
'mac_address': port.address, 'mac_address': port.address,
'binding:profile': { 'binding:profile': {
'local_link_information': [port.local_link_connection] 'local_link_information': [port.local_link_connection]
@ -398,7 +518,11 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid) as task: with task_manager.acquire(self.context, self.node.uuid) as task:
ports = neutron.add_ports_to_network(task, self.network_uuid) ports = neutron.add_ports_to_network(task, self.network_uuid)
self.assertEqual(expected, ports) self.assertEqual(expected, ports)
self.client_mock.create_port.assert_called_once_with(expected_body) self.client_mock.create_port.assert_called_once_with(
expected_create_body)
update_mock.assert_called_once_with(self.context,
self.neutron_port['id'],
expected_update_body)
self.assertTrue(vpi_mock.called) self.assertTrue(vpi_mock.called)
@mock.patch.object(neutron, 'rollback_ports', autospec=True) @mock.patch.object(neutron, 'rollback_ports', autospec=True)
@ -413,8 +537,9 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
self.network_uuid) self.network_uuid)
rollback_mock.assert_called_once_with(task, self.network_uuid) rollback_mock.assert_called_once_with(task, self.network_uuid)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'LOG', autospec=True) @mock.patch.object(neutron, 'LOG', autospec=True)
def test_add_network_create_some_ports_fail(self, log_mock): def test_add_network_create_some_ports_fail(self, log_mock, update_mock):
object_utils.create_test_port( object_utils.create_test_port(
self.context, node_id=self.node.id, self.context, node_id=self.node.id,
uuid=uuidutils.generate_uuid(), uuid=uuidutils.generate_uuid(),
@ -788,10 +913,11 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
neutron.wait_for_port_status, neutron.wait_for_port_status,
self.client_mock, 'port_id', 'DOWN') self.client_mock, 'port_id', 'DOWN')
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron, 'wait_for_port_status', autospec=True) @mock.patch.object(neutron, 'wait_for_port_status', autospec=True)
def test_add_smartnic_port_to_network( def test_add_smartnic_port_to_network(
self, wait_port_mock, wait_agent_mock): self, wait_port_mock, wait_agent_mock, update_mock):
# Ports will be created only if pxe_enabled is True # Ports will be created only if pxe_enabled is True
self.node.network_interface = 'neutron' self.node.network_interface = 'neutron'
self.node.save() self.node.save()
@ -809,14 +935,18 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
port.is_smartnic = True port.is_smartnic = True
port.save() port.save()
expected_body = { expected_create_body = {
'port': { 'port': {
'network_id': self.network_uuid, 'network_id': self.network_uuid,
'admin_state_up': True, 'admin_state_up': True,
'binding:vnic_type': 'smart-nic', 'binding:vnic_type': 'smart-nic',
'device_id': self.node.uuid,
}
}
expected_update_body = {
'port': {
'device_owner': 'baremetal:none', 'device_owner': 'baremetal:none',
'binding:host_id': port.local_link_connection['hostname'], 'binding:host_id': port.local_link_connection['hostname'],
'device_id': self.node.uuid,
'mac_address': port.address, 'mac_address': port.address,
'binding:profile': { 'binding:profile': {
'local_link_information': [port.local_link_connection] 'local_link_information': [port.local_link_connection]
@ -832,7 +962,9 @@ class TestNeutronNetworkActions(db_base.DbTestCase):
ports = neutron.add_ports_to_network(task, self.network_uuid) ports = neutron.add_ports_to_network(task, self.network_uuid)
self.assertEqual(expected, ports) self.assertEqual(expected, ports)
self.client_mock.create_port.assert_called_once_with( self.client_mock.create_port.assert_called_once_with(
expected_body) expected_create_body)
update_mock.assert_called_once_with(
self.context, self.neutron_port['id'], expected_update_body)
wait_agent_mock.assert_called_once_with( wait_agent_mock.assert_called_once_with(
self.client_mock, 'hostname') self.client_mock, 'hostname')
wait_port_mock.assert_called_once_with( wait_port_mock.assert_called_once_with(
@ -935,18 +1067,20 @@ class TestUpdatePortAddress(base.TestCase):
super(TestUpdatePortAddress, self).setUp() super(TestUpdatePortAddress, self).setUp()
self.context = context.RequestContext() self.context = context.RequestContext()
def test_update_port_address(self, mock_client): @mock.patch.object(neutron, 'update_neutron_port', autospec=True)
def test_update_port_address(self, mock_unp, mock_client):
address = 'fe:54:00:77:07:d9' address = 'fe:54:00:77:07:d9'
port_id = 'fake-port-id' port_id = 'fake-port-id'
expected = {'port': {'mac_address': address}} expected = {'port': {'mac_address': address}}
mock_client.return_value.show_port.return_value = {} mock_client.return_value.show_port.return_value = {}
neutron.update_port_address(port_id, address, context=self.context) neutron.update_port_address(port_id, address, context=self.context)
mock_client.return_value.update_port.assert_called_once_with(port_id, mock_unp.assert_called_once_with(self.context, port_id, expected)
expected)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'unbind_neutron_port', autospec=True) @mock.patch.object(neutron, 'unbind_neutron_port', autospec=True)
def test_update_port_address_with_binding(self, mock_unp, mock_client): def test_update_port_address_with_binding(self, mock_unp, mock_update,
mock_client):
address = 'fe:54:00:77:07:d9' address = 'fe:54:00:77:07:d9'
port_id = 'fake-port-id' port_id = 'fake-port-id'
@ -954,19 +1088,22 @@ class TestUpdatePortAddress(base.TestCase):
'port': {'binding:host_id': 'host', 'port': {'binding:host_id': 'host',
'binding:profile': 'foo'}} 'binding:profile': 'foo'}}
calls = [mock.call(port_id, {'port': {'mac_address': address}}), calls = [mock.call(self.context, port_id,
mock.call(port_id, {'port': {'binding:host_id': 'host', {'port': {'mac_address': address}}),
mock.call(self.context, port_id,
{'port': {'binding:host_id': 'host',
'binding:profile': 'foo'}})] 'binding:profile': 'foo'}})]
neutron.update_port_address(port_id, address, context=self.context) neutron.update_port_address(port_id, address, context=self.context)
mock_unp.assert_called_once_with( mock_unp.assert_called_once_with(
port_id, port_id,
client=mock_client(context=self.context),
context=self.context) context=self.context)
mock_client.return_value.update_port.assert_has_calls(calls) mock_update.assert_has_calls(calls)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'unbind_neutron_port', autospec=True) @mock.patch.object(neutron, 'unbind_neutron_port', autospec=True)
def test_update_port_address_without_binding(self, mock_unp, mock_client): def test_update_port_address_without_binding(self, mock_unp, mock_update,
mock_client):
address = 'fe:54:00:77:07:d9' address = 'fe:54:00:77:07:d9'
port_id = 'fake-port-id' port_id = 'fake-port-id'
expected = {'port': {'mac_address': address}} expected = {'port': {'mac_address': address}}
@ -975,7 +1112,7 @@ class TestUpdatePortAddress(base.TestCase):
neutron.update_port_address(port_id, address, context=self.context) neutron.update_port_address(port_id, address, context=self.context)
self.assertFalse(mock_unp.called) self.assertFalse(mock_unp.called)
mock_client.return_value.update_port.assert_any_call(port_id, expected) mock_update.assert_any_call(self.context, port_id, expected)
def test_update_port_address_show_failed(self, mock_client): def test_update_port_address_show_failed(self, mock_client):
address = 'fe:54:00:77:07:d9' address = 'fe:54:00:77:07:d9'
@ -1002,17 +1139,18 @@ class TestUpdatePortAddress(base.TestCase):
port_id, address, context=self.context) port_id, address, context=self.context)
mock_unp.assert_called_once_with( mock_unp.assert_called_once_with(
port_id, port_id,
client=mock_client(context=self.context),
context=self.context) context=self.context)
self.assertFalse(mock_client.return_value.update_port.called) self.assertFalse(mock_client.return_value.update_port.called)
@mock.patch.object(neutron, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron, 'unbind_neutron_port', autospec=True) @mock.patch.object(neutron, 'unbind_neutron_port', autospec=True)
def test_update_port_address_with_exception(self, mock_unp, def test_update_port_address_with_exception(self, mock_unp,
mock_update,
mock_client): mock_client):
address = 'fe:54:00:77:07:d9' address = 'fe:54:00:77:07:d9'
port_id = 'fake-port-id' port_id = 'fake-port-id'
mock_client.return_value.show_port.return_value = {} mock_client.return_value.show_port.return_value = {}
mock_client.return_value.update_port.side_effect = ( mock_update.side_effect = (
neutron_client_exc.NeutronClientException()) neutron_client_exc.NeutronClientException())
self.assertRaises(exception.FailedToUpdateMacOnPort, self.assertRaises(exception.FailedToUpdateMacOnPort,
@ -1020,14 +1158,14 @@ class TestUpdatePortAddress(base.TestCase):
port_id, address, context=self.context) port_id, address, context=self.context)
@mock.patch.object(neutron, 'get_client', autospec=True) @mock.patch.object(neutron, 'update_neutron_port', autospec=True)
class TestUnbindPort(base.TestCase): class TestUnbindPort(base.TestCase):
def setUp(self): def setUp(self):
super(TestUnbindPort, self).setUp() super(TestUnbindPort, self).setUp()
self.context = context.RequestContext() self.context = context.RequestContext()
def test_unbind_neutron_port_client_passed(self, mock_client): def test_unbind_neutron_port_client_passed(self, mock_unp):
port_id = 'fake-port-id' port_id = 'fake-port-id'
body_unbind = { body_unbind = {
'port': { 'port': {
@ -1040,20 +1178,18 @@ class TestUnbindPort(base.TestCase):
'mac_address': None 'mac_address': None
} }
} }
client = mock.MagicMock()
update_calls = [ update_calls = [
mock.call(port_id, body_unbind), mock.call(self.context, port_id, body_unbind, client),
mock.call(port_id, body_reset_mac) mock.call(self.context, port_id, body_reset_mac, client)
] ]
neutron.unbind_neutron_port(port_id, neutron.unbind_neutron_port(port_id, client, context=self.context)
mock_client(context=self.context), self.assertEqual(2, mock_unp.call_count)
context=self.context) mock_unp.assert_has_calls(update_calls)
self.assertEqual(1, mock_client.call_count)
mock_client.return_value.update_port.assert_has_calls(update_calls)
@mock.patch.object(neutron, 'LOG', autospec=True) @mock.patch.object(neutron, 'LOG', autospec=True)
def test_unbind_neutron_port_failure(self, mock_log, mock_client): def test_unbind_neutron_port_failure(self, mock_log, mock_unp):
mock_client.return_value.update_port.side_effect = ( mock_unp.side_effect = (neutron_client_exc.NeutronClientException())
neutron_client_exc.NeutronClientException())
body = { body = {
'port': { 'port': {
'binding:host_id': '', 'binding:host_id': '',
@ -1063,12 +1199,10 @@ class TestUnbindPort(base.TestCase):
port_id = 'fake-port-id' port_id = 'fake-port-id'
self.assertRaises(exception.NetworkError, neutron.unbind_neutron_port, self.assertRaises(exception.NetworkError, neutron.unbind_neutron_port,
port_id, context=self.context) port_id, context=self.context)
mock_client.assert_called_once_with(context=self.context) mock_unp.assert_called_once_with(self.context, port_id, body, None)
mock_client.return_value.update_port.assert_called_once_with(port_id,
body)
mock_log.exception.assert_called_once() mock_log.exception.assert_called_once()
def test_unbind_neutron_port(self, mock_client): def test_unbind_neutron_port(self, mock_unp):
port_id = 'fake-port-id' port_id = 'fake-port-id'
body_unbind = { body_unbind = {
'port': { 'port': {
@ -1082,17 +1216,16 @@ class TestUnbindPort(base.TestCase):
} }
} }
update_calls = [ update_calls = [
mock.call(port_id, body_unbind), mock.call(self.context, port_id, body_unbind, None),
mock.call(port_id, body_reset_mac) mock.call(self.context, port_id, body_reset_mac, None)
] ]
neutron.unbind_neutron_port(port_id, context=self.context) neutron.unbind_neutron_port(port_id, context=self.context)
mock_client.assert_called_once_with(context=self.context) mock_unp.assert_has_calls(update_calls)
mock_client.return_value.update_port.assert_has_calls(update_calls)
@mock.patch.object(neutron, 'LOG', autospec=True) @mock.patch.object(neutron, 'LOG', autospec=True)
def test_unbind_neutron_port_not_found(self, mock_log, mock_client): def test_unbind_neutron_port_not_found(self, mock_log, mock_unp):
port_id = 'fake-port-id' port_id = 'fake-port-id'
mock_client.return_value.update_port.side_effect = ( mock_unp.side_effect = (
neutron_client_exc.PortNotFoundClient()) neutron_client_exc.PortNotFoundClient())
body = { body = {
'port': { 'port': {
@ -1101,9 +1234,7 @@ class TestUnbindPort(base.TestCase):
} }
} }
neutron.unbind_neutron_port(port_id, context=self.context) neutron.unbind_neutron_port(port_id, context=self.context)
mock_client.assert_called_once_with(context=self.context) mock_unp.assert_called_once_with(self.context, port_id, body, None)
mock_client.return_value.update_port.assert_called_once_with(port_id,
body)
mock_log.info.assert_called_once_with('Port %s was not found while ' mock_log.info.assert_called_once_with('Port %s was not found while '
'unbinding.', port_id) 'unbinding.', port_id)

View File

@ -48,8 +48,8 @@ class TestNeutron(db_base.DbTestCase):
dhcp_factory.DHCPFactory._dhcp_provider = None dhcp_factory.DHCPFactory._dhcp_provider = None
@mock.patch('ironic.common.neutron.get_client', autospec=True) @mock.patch('ironic.common.neutron.update_neutron_port', autospec=True)
def test_update_port_dhcp_opts(self, client_mock): def test_update_port_dhcp_opts(self, update_mock):
opts = [{'opt_name': 'bootfile-name', opts = [{'opt_name': 'bootfile-name',
'opt_value': 'pxelinux.0'}, 'opt_value': 'pxelinux.0'},
{'opt_name': 'tftp-server', {'opt_name': 'tftp-server',
@ -63,14 +63,14 @@ class TestNeutron(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid) as task: with task_manager.acquire(self.context, self.node.uuid) as task:
api.provider.update_port_dhcp_opts(port_id, opts, api.provider.update_port_dhcp_opts(port_id, opts,
context=task.context) context=task.context)
client_mock.return_value.update_port.assert_called_once_with( update_mock.assert_called_once_with(
port_id, expected) task.context, port_id, expected)
@mock.patch('ironic.common.neutron.get_client', autospec=True) @mock.patch('ironic.common.neutron.update_neutron_port', autospec=True)
def test_update_port_dhcp_opts_with_exception(self, client_mock): def test_update_port_dhcp_opts_with_exception(self, update_mock):
opts = [{}] opts = [{}]
port_id = 'fake-port-id' port_id = 'fake-port-id'
client_mock.return_value.update_port.side_effect = ( update_mock.side_effect = (
neutron_client_exc.NeutronClientException()) neutron_client_exc.NeutronClientException())
api = dhcp_factory.DHCPFactory() api = dhcp_factory.DHCPFactory()

View File

@ -400,22 +400,26 @@ class TestCommonFunctions(db_base.DbTestCase):
common.get_free_port_like_object, common.get_free_port_like_object,
task, self.vif_id, {'physnet2'}) task, self.vif_id, {'physnet2'})
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True) @mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_port_to_tenant_network_client(self, mock_gc): def test_plug_port_to_tenant_network_client(self, mock_gc, mock_update):
self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id} self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id}
self.port.save() self.port.save()
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
common.plug_port_to_tenant_network(task, self.port, common.plug_port_to_tenant_network(task, self.port,
client=mock.MagicMock()) client=mock.MagicMock())
self.assertFalse(mock_gc.called) self.assertFalse(mock_gc.called)
self.assertTrue(mock_update.called)
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True) @mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_port_to_tenant_network_no_client(self, mock_gc): def test_plug_port_to_tenant_network_no_client(self, mock_gc, mock_update):
self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id} self.port.internal_info = {common.TENANT_VIF_KEY: self.vif_id}
self.port.save() self.port.save()
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
common.plug_port_to_tenant_network(task, self.port) common.plug_port_to_tenant_network(task, self.port)
self.assertTrue(mock_gc.called) self.assertTrue(mock_gc.called)
self.assertTrue(mock_update.called)
@mock.patch.object(neutron_common, 'get_client', autospec=True) @mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_port_to_tenant_network_no_tenant_vif(self, mock_gc): def test_plug_port_to_tenant_network_no_tenant_vif(self, mock_gc):
@ -432,9 +436,10 @@ class TestCommonFunctions(db_base.DbTestCase):
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron_common, 'wait_for_port_status', autospec=True) @mock.patch.object(neutron_common, 'wait_for_port_status', autospec=True)
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True) @mock.patch.object(neutron_common, 'get_client', autospec=True)
def test_plug_port_to_tenant_network_smartnic_port( def test_plug_port_to_tenant_network_smartnic_port(
self, mock_gc, wait_port_mock, wait_agent_mock): self, mock_gc, mock_update, wait_port_mock, wait_agent_mock):
nclient = mock.MagicMock() nclient = mock.MagicMock()
mock_gc.return_value = nclient mock_gc.return_value = nclient
local_link_connection = self.port.local_link_connection local_link_connection = self.port.local_link_connection
@ -449,6 +454,7 @@ class TestCommonFunctions(db_base.DbTestCase):
nclient, 'hostname') nclient, 'hostname')
wait_port_mock.assert_called_once_with( wait_port_mock.assert_called_once_with(
nclient, self.vif_id, 'ACTIVE') nclient, self.vif_id, 'ACTIVE')
self.assertTrue(mock_update.called)
class TestVifPortIDMixin(db_base.DbTestCase): class TestVifPortIDMixin(db_base.DbTestCase):

View File

@ -170,10 +170,8 @@ class TestFlatInterface(db_base.DbTestCase):
self.port.refresh() self.port.refresh()
self.assertNotIn('cleaning_vif_port_id', self.port.internal_info) self.assertNotIn('cleaning_vif_port_id', self.port.internal_info)
@mock.patch.object(neutron, 'get_client') @mock.patch.object(neutron, 'update_neutron_port')
def test__bind_flat_ports_set_binding_host_id(self, client_mock): def test__bind_flat_ports_set_binding_host_id(self, update_mock):
upd_mock = mock.Mock()
client_mock.return_value.update_port = upd_mock
extra = {'vif_port_id': 'foo'} extra = {'vif_port_id': 'foo'}
utils.create_test_port(self.context, node_id=self.node.id, utils.create_test_port(self.context, node_id=self.node.id,
address='52:54:00:cf:2d:33', extra=extra, address='52:54:00:cf:2d:33', extra=extra,
@ -183,12 +181,10 @@ class TestFlatInterface(db_base.DbTestCase):
'mac_address': '52:54:00:cf:2d:33'}} 'mac_address': '52:54:00:cf:2d:33'}}
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
self.interface._bind_flat_ports(task) self.interface._bind_flat_ports(task)
upd_mock.assert_called_once_with('foo', exp_body) update_mock.assert_called_once_with(self.context, 'foo', exp_body)
@mock.patch.object(neutron, 'get_client') @mock.patch.object(neutron, 'update_neutron_port')
def test__bind_flat_ports_set_binding_host_id_portgroup(self, client_mock): def test__bind_flat_ports_set_binding_host_id_portgroup(self, update_mock):
upd_mock = mock.Mock()
client_mock.return_value.update_port = upd_mock
internal_info = {'tenant_vif_port_id': 'foo'} internal_info = {'tenant_vif_port_id': 'foo'}
utils.create_test_portgroup( utils.create_test_portgroup(
self.context, node_id=self.node.id, internal_info=internal_info, self.context, node_id=self.node.id, internal_info=internal_info,
@ -204,8 +200,9 @@ class TestFlatInterface(db_base.DbTestCase):
'mac_address': '52:54:00:cf:2d:31'}} 'mac_address': '52:54:00:cf:2d:31'}}
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
self.interface._bind_flat_ports(task) self.interface._bind_flat_ports(task)
upd_mock.assert_has_calls([ update_mock.assert_has_calls([
mock.call('bar', exp_body1), mock.call('foo', exp_body2)]) mock.call(self.context, 'bar', exp_body1),
mock.call(self.context, 'foo', exp_body2)])
@mock.patch.object(neutron, 'unbind_neutron_port') @mock.patch.object(neutron, 'unbind_neutron_port')
def test__unbind_flat_ports(self, unbind_neutron_port_mock): def test__unbind_flat_ports(self, unbind_neutron_port_mock):
@ -234,10 +231,9 @@ class TestFlatInterface(db_base.DbTestCase):
[mock.call('foo', context=self.context), [mock.call('foo', context=self.context),
mock.call('bar', context=self.context)]) mock.call('bar', context=self.context)])
@mock.patch.object(neutron, 'get_client') @mock.patch.object(neutron, 'update_neutron_port')
def test__bind_flat_ports_set_binding_host_id_raise(self, client_mock): def test__bind_flat_ports_set_binding_host_id_raise(self, update_mock):
client_mock.return_value.update_port.side_effect = \ update_mock.side_effect = (neutron_exceptions.ConnectionFailed())
(neutron_exceptions.ConnectionFailed())
extra = {'vif_port_id': 'foo'} extra = {'vif_port_id': 'foo'}
utils.create_test_port(self.context, node_id=self.node.id, utils.create_test_port(self.context, node_id=self.node.id,
address='52:54:00:cf:2d:33', extra=extra, address='52:54:00:cf:2d:33', extra=extra,

View File

@ -572,10 +572,11 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
log_mock.error.call_args[0][0]) log_mock.error.call_args[0][0])
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron_common, 'update_neutron_port')
@mock.patch.object(neutron_common, 'get_client') @mock.patch.object(neutron_common, 'get_client')
@mock.patch.object(neutron, 'LOG') @mock.patch.object(neutron, 'LOG')
def test_configure_tenant_networks_multiple_ports_one_vif_id( def test_configure_tenant_networks_multiple_ports_one_vif_id(
self, log_mock, client_mock, wait_agent_mock): self, log_mock, client_mock, update_mock, wait_agent_mock):
expected_body = { expected_body = {
'port': { 'port': {
'binding:vnic_type': 'baremetal', 'binding:vnic_type': 'baremetal',
@ -585,20 +586,20 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
'mac_address': '52:54:00:cf:2d:32' 'mac_address': '52:54:00:cf:2d:32'
} }
} }
upd_mock = mock.Mock()
client_mock.return_value.update_port = upd_mock
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
self.interface.configure_tenant_networks(task) self.interface.configure_tenant_networks(task)
client_mock.assert_called_once_with(context=task.context) client_mock.assert_called_once_with(context=task.context)
upd_mock.assert_called_once_with(self.port.extra['vif_port_id'], update_mock.assert_called_once_with(self.context,
expected_body) self.port.extra['vif_port_id'],
expected_body)
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron_common, 'update_neutron_port')
@mock.patch.object(neutron_common, 'get_client') @mock.patch.object(neutron_common, 'get_client')
def test_configure_tenant_networks_update_fail(self, client_mock, def test_configure_tenant_networks_update_fail(self, client_mock,
update_mock,
wait_agent_mock): wait_agent_mock):
client = client_mock.return_value update_mock.side_effect = neutron_exceptions.ConnectionFailed(
client.update_port.side_effect = neutron_exceptions.ConnectionFailed(
reason='meow') reason='meow')
with task_manager.acquire(self.context, self.node.id) as task: with task_manager.acquire(self.context, self.node.id) as task:
self.assertRaisesRegex( self.assertRaisesRegex(
@ -607,12 +608,12 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
client_mock.assert_called_once_with(context=task.context) client_mock.assert_called_once_with(context=task.context)
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron_common, 'update_neutron_port')
@mock.patch.object(neutron_common, 'get_client') @mock.patch.object(neutron_common, 'get_client')
def _test_configure_tenant_networks(self, client_mock, wait_agent_mock, def _test_configure_tenant_networks(self, client_mock, update_mock,
wait_agent_mock,
is_client_id=False, is_client_id=False,
vif_int_info=False): vif_int_info=False):
upd_mock = mock.Mock()
client_mock.return_value.update_port = upd_mock
if vif_int_info: if vif_int_info:
kwargs = {'internal_info': { kwargs = {'internal_info': {
'tenant_vif_port_id': uuidutils.generate_uuid()}} 'tenant_vif_port_id': uuidutils.generate_uuid()}}
@ -668,9 +669,9 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
else: else:
portid1 = self.port.extra['vif_port_id'] portid1 = self.port.extra['vif_port_id']
portid2 = second_port.extra['vif_port_id'] portid2 = second_port.extra['vif_port_id']
upd_mock.assert_has_calls( update_mock.assert_has_calls(
[mock.call(portid1, port1_body), [mock.call(self.context, portid1, port1_body),
mock.call(portid2, port2_body)], mock.call(self.context, portid2, port2_body)],
any_order=True any_order=True
) )
@ -693,11 +694,12 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
self._test_configure_tenant_networks(is_client_id=True) self._test_configure_tenant_networks(is_client_id=True)
@mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True)
@mock.patch.object(neutron_common, 'update_neutron_port', autospec=True)
@mock.patch.object(neutron_common, 'get_client', autospec=True) @mock.patch.object(neutron_common, 'get_client', autospec=True)
@mock.patch.object(neutron_common, 'get_local_group_information', @mock.patch.object(neutron_common, 'get_local_group_information',
autospec=True) autospec=True)
def test_configure_tenant_networks_with_portgroups( def test_configure_tenant_networks_with_portgroups(
self, glgi_mock, client_mock, wait_agent_mock): self, glgi_mock, client_mock, update_mock, wait_agent_mock):
pg = utils.create_test_portgroup( pg = utils.create_test_portgroup(
self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32', self.context, node_id=self.node.id, address='ff:54:00:cf:2d:32',
extra={'vif_port_id': uuidutils.generate_uuid()}) extra={'vif_port_id': uuidutils.generate_uuid()})
@ -717,8 +719,6 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
'port_id': 'Ethernet1/2', 'port_id': 'Ethernet1/2',
'switch_info': 'switch2'} 'switch_info': 'switch2'}
) )
upd_mock = mock.Mock()
client_mock.return_value.update_port = upd_mock
local_group_info = {'a': 'b'} local_group_info = {'a': 'b'}
glgi_mock.return_value = local_group_info glgi_mock.return_value = local_group_info
expected_body = { expected_body = {
@ -747,9 +747,11 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
self.interface.configure_tenant_networks(task) self.interface.configure_tenant_networks(task)
client_mock.assert_called_once_with(context=task.context) client_mock.assert_called_once_with(context=task.context)
glgi_mock.assert_called_once_with(task, pg) glgi_mock.assert_called_once_with(task, pg)
upd_mock.assert_has_calls( update_mock.assert_has_calls(
[mock.call(self.port.extra['vif_port_id'], call1_body), [mock.call(self.context, self.port.extra['vif_port_id'],
mock.call(pg.extra['vif_port_id'], call2_body)] call1_body),
mock.call(self.context, pg.extra['vif_port_id'],
call2_body)]
) )
def test_need_power_on_true(self): def test_need_power_on_true(self):

View File

@ -0,0 +1,8 @@
---
features:
- |
Changes neutron port updates to use auth values from Ironic's neutron
conf, preventing issues that can arise when a non-admin user manages
Ironic nodes. A check is added to the port update function to verify that
the user can actually see the port. This adds an additional Neutron
request call to all port updates.