From ff00047b4aa00b9def929e396693dc4d520460e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Thu, 4 Jun 2020 22:22:06 +0200 Subject: [PATCH] Switch Ironic to openstacksdk for Neutron This patch removes the dependency from ironicclient to communicate with neutron in favor of openstacksdk. Also: * Use import keystoneauth1.loading as ks_loading accross the project. * Refactor to have one 'get_client' function, removing the '_get_config_client' method. Setting config_client=True when calling 'get_client' returns a client using auth options values from conf parameters. Depends-On: https://review.opendev.org/735601 Change-Id: Ib6c0fa2acfc33deb9c5b36ae724d5d8304d1dd29 --- doc/source/admin/report.txt | 2 +- ironic/common/keystone.py | 24 +- ironic/common/neutron.py | 330 +++---- ironic/conf/auth.py | 18 +- ironic/conf/neutron.py | 6 +- ironic/conf/opts.py | 2 +- ironic/dhcp/neutron.py | 38 +- ironic/drivers/modules/network/common.py | 39 +- ironic/drivers/modules/network/flat.py | 16 +- .../tests/unit/common/test_glance_service.py | 6 +- ironic/tests/unit/common/test_keystone.py | 6 +- ironic/tests/unit/common/test_neutron.py | 840 +++++++----------- ironic/tests/unit/conf/test_auth.py | 6 +- ironic/tests/unit/dhcp/test_neutron.py | 41 +- .../unit/drivers/modules/network/test_flat.py | 22 +- .../drivers/modules/network/test_neutron.py | 127 ++- ironic/tests/unit/stubs.py | 182 ++++ lower-constraints.txt | 3 +- ...nt-with-openstacksdk-20c06d9d0316c2df.yaml | 10 + requirements.txt | 3 +- 20 files changed, 828 insertions(+), 893 deletions(-) create mode 100644 releasenotes/notes/replace-neutronclient-with-openstacksdk-20c06d9d0316c2df.yaml diff --git a/doc/source/admin/report.txt b/doc/source/admin/report.txt index c3cfdbf3e4..02d8897171 100644 --- a/doc/source/admin/report.txt +++ b/doc/source/admin/report.txt @@ -233,7 +233,6 @@ default: iso8601=WARNING keystoneauth.session=INFO keystonemiddleware.auth_token=INFO - neutronclient=WARNING oslo_messaging=INFO paramiko=WARNING qpid.messaging=INFO @@ -241,6 +240,7 @@ default: sqlalchemy=WARNING stevedore=INFO urllib3.connectionpool=WARNING + openstack=WARNING default_management_interface = None default_network_interface = None default_portgroup_mode = active-backup diff --git a/ironic/common/keystone.py b/ironic/common/keystone.py index 233533b552..fae8d90f38 100644 --- a/ironic/common/keystone.py +++ b/ironic/common/keystone.py @@ -16,8 +16,8 @@ import functools -from keystoneauth1 import exceptions as kaexception -from keystoneauth1 import loading as kaloading +from keystoneauth1 import exceptions as ks_exception +from keystoneauth1 import loading as ks_loading from keystoneauth1 import service_token from keystoneauth1 import token_endpoint from oslo_log import log as logging @@ -35,15 +35,15 @@ def ks_exceptions(f): def wrapper(*args, **kwargs): try: return f(*args, **kwargs) - except kaexception.EndpointNotFound: + except ks_exception.EndpointNotFound: service_type = kwargs.get('service_type', 'baremetal') endpoint_type = kwargs.get('endpoint_type', 'internal') raise exception.CatalogNotFound( service_type=service_type, endpoint_type=endpoint_type) - except (kaexception.Unauthorized, kaexception.AuthorizationFailure): + except (ks_exception.Unauthorized, ks_exception.AuthorizationFailure): raise exception.KeystoneUnauthorized() - except (kaexception.NoMatchingPlugin, - kaexception.MissingRequiredOptions) as e: + except (ks_exception.NoMatchingPlugin, + ks_exception.MissingRequiredOptions) as e: raise exception.ConfigInvalid(str(e)) except Exception as e: LOG.exception('Keystone request failed: %(msg)s', @@ -63,7 +63,7 @@ def get_session(group, **session_kwargs): :param group: name of the config section to load session options from """ - return kaloading.load_session_from_conf_options( + return ks_loading.load_session_from_conf_options( CONF, group, **session_kwargs) @@ -81,9 +81,9 @@ def get_auth(group, **auth_kwargs): """ try: - auth = kaloading.load_auth_from_conf_options(CONF, group, - **auth_kwargs) - except kaexception.MissingRequiredOptions: + auth = ks_loading.load_auth_from_conf_options(CONF, group, + **auth_kwargs) + except ks_exception.MissingRequiredOptions: LOG.error('Failed to load auth plugin from group %s', group) raise return auth @@ -100,8 +100,8 @@ def get_adapter(group, **adapter_kwargs): :param group: name of the config section to load adapter options from """ - return kaloading.load_adapter_from_conf_options(CONF, group, - **adapter_kwargs) + return ks_loading.load_adapter_from_conf_options(CONF, group, + **adapter_kwargs) def get_endpoint(group, **adapter_kwargs): diff --git a/ironic/common/neutron.py b/ironic/common/neutron.py index 15a4201b58..c6c84f3df3 100644 --- a/ironic/common/neutron.py +++ b/ironic/common/neutron.py @@ -13,11 +13,9 @@ import copy import ipaddress -from keystoneauth1 import loading as ks_loading -from neutronclient.common import exceptions as neutron_exceptions -from neutronclient.v2_0 import client as clientv20 +import openstack +from openstack.connection import exceptions as openstack_exc from oslo_log import log -from oslo_utils import uuidutils import retrying from ironic.api.controllers.v1 import types @@ -43,9 +41,6 @@ VNIC_SMARTNIC = 'smart-nic' PHYSNET_PARAM_NAME = 'provider:physical_network' """Name of the neutron network API physical network parameter.""" -SEGMENTS_PARAM_NAME = 'segments' -"""Name of the neutron network API segments parameter.""" - def _get_neutron_session(): global _NEUTRON_SESSION @@ -56,54 +51,35 @@ def _get_neutron_session(): return _NEUTRON_SESSION -# TODO(pas-ha) remove deprecated options handling in Rocky -# until then it might look ugly due to all if's. -def get_client(token=None, context=None): +def get_client(token=None, context=None, auth_from_config=False): + """Retrieve a neutron client connection. + + :param context: request context, + instance of ironic.common.context.RequestContext + :param auth_from_config: (boolean) When True, use auth values from + conf parameters + :returns: A neutron client. + """ if not context: context = ironic_context.RequestContext(auth_token=token) - # NOTE(pas-ha) neutronclient supports passing both session - # and the auth to client separately, makes things easier session = _get_neutron_session() service_auth = keystone.get_auth('neutron') - endpoint = keystone.get_endpoint('neutron', session=session, auth=service_auth) user_auth = None - if CONF.neutron.auth_type != 'none' and context.auth_token: + if (not auth_from_config and CONF.neutron.auth_type != 'none' + and context.auth_token): user_auth = keystone.get_service_auth(context, endpoint, service_auth) - return clientv20.Client(session=session, - auth=user_auth or service_auth, - endpoint_override=endpoint, - retries=CONF.neutron.retries, - global_request_id=context.global_id, - timeout=CONF.neutron.request_timeout) + + sess = keystone.get_session('neutron', timeout=CONF.neutron.timeout, + auth=user_auth or service_auth) + conn = openstack.connection.Connection(session=sess, oslo_conf=CONF) + + return conn.global_request(context.global_id).network -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): +def update_neutron_port(context, port_id, attrs, client=None): """Undate a neutron port Uses neutron client from conf client to update a neutron client @@ -112,15 +88,20 @@ def update_neutron_port(context, port_id, update_body, client=None): :param context: request context, instance of ironic.common.context.RequestContext :param port_id: Neutron port ID. - :param update_body: Body of update + :param attrs: The attributes to update on the port :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) + get_client(context=context).get_port(port_id) + # Set user_auth=False to ensure auth values from ironic.conf is used + # prevents issues where a non-admin user is not allowed to manage + # Neutron ports. + client = get_client(context=context, auth_from_config=True) - return client.update_port(port_id, update_body) + attrs = attrs.get('port', attrs) + + return client.update_port(port_id, **attrs) def unbind_neutron_port(port_id, client=None, context=None): @@ -136,21 +117,20 @@ def unbind_neutron_port(port_id, client=None, context=None): :raises: NetworkError """ - body_unbind = {'port': {'binding:host_id': '', - 'binding:profile': {}}} - body_reset_mac = {'port': {'mac_address': None}} + attrs_unbind = {'binding:host_id': '', 'binding:profile': {}} + attrs_reset_mac = {'mac_address': None} try: - update_neutron_port(context, port_id, body_unbind, client) + update_neutron_port(context, port_id, attrs_unbind, client) # NOTE(hjensas): We need to reset the mac address in a separate step. # 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 # reset the mac in the same call. - update_neutron_port(context, port_id, body_reset_mac, client) + update_neutron_port(context, port_id, attrs_reset_mac, client) # NOTE(vsaienko): Ignore if port was deleted before calling vif detach. - except neutron_exceptions.PortNotFoundClient: + except openstack_exc.ResourceNotFound: LOG.info('Port %s was not found while unbinding.', port_id) - except neutron_exceptions.NeutronClientException as e: + except openstack_exc.OpenStackCloudException as e: msg = (_('Unable to clear binding profile for ' 'neutron port %(port_id)s. Error: ' '%(err)s') % {'port_id': port_id, 'err': e}) @@ -168,12 +148,12 @@ def update_port_address(port_id, address, context=None): :raises: FailedToUpdateMacOnPort """ client = get_client(context=context) - port_req_body = {'port': {'mac_address': address}} + port_attrs = {'mac_address': address} try: msg = (_("Failed to get the current binding on Neutron " "port %s.") % port_id) - port = client.show_port(port_id).get('port', {}) + port = client.get_port(port_id) binding_host_id = port.get('binding:host_id') binding_profile = port.get('binding:profile') @@ -186,17 +166,17 @@ def update_port_address(port_id, address, context=None): unbind_neutron_port(port_id, context=context) msg = (_("Failed to update MAC address on Neutron port %s.") % port_id) - update_neutron_port(context, port_id, port_req_body) + update_neutron_port(context, port_id, port_attrs) # Restore original binding:profile and host_id if binding_host_id: msg = (_("Failed to update binding:host_id and profile on Neutron " "port %s.") % port_id) - port_req_body = {'port': {'binding:host_id': binding_host_id, - 'binding:profile': binding_profile}} + port_attrs = {'binding:host_id': binding_host_id, + 'binding:profile': binding_profile} - update_neutron_port(context, port_id, port_req_body) - except (neutron_exceptions.NeutronClientException, exception.NetworkError): + update_neutron_port(context, port_id, port_attrs) + except (openstack_exc.OpenStackCloudException, exception.NetworkError): LOG.exception(msg) raise exception.FailedToUpdateMacOnPort(port_id=port_id) @@ -213,20 +193,18 @@ def _verify_security_groups(security_groups, client): if not security_groups: return try: - neutron_sec_groups = ( - client.list_security_groups(id=security_groups, fields='id').get( - 'security_groups', [])) - except neutron_exceptions.NeutronClientException as e: + neutron_sec_groups = set( + x.id for x in client.security_groups(id=security_groups)) + except openstack_exc.OpenStackCloudException as e: msg = (_("Could not retrieve security groups from neutron: %(exc)s") % {'exc': e}) LOG.exception(msg) raise exception.NetworkError(msg) - if set(security_groups).issubset(x['id'] for x in neutron_sec_groups): + if set(security_groups).issubset(neutron_sec_groups): return - missing_sec_groups = set(security_groups).difference( - x['id'] for x in neutron_sec_groups) + missing_sec_groups = set(security_groups).difference(neutron_sec_groups) msg = (_('Could not find these security groups (specified via ironic ' 'config) in neutron: %(ir-sg)s') % {'ir-sg': list(missing_sec_groups)}) @@ -245,20 +223,17 @@ def _add_ip_addresses_for_ipv6_stateful(context, port, client): :param port: A neutron port :param client: Neutron client """ - fixed_ips = port['port']['fixed_ips'] + fixed_ips = port.fixed_ips if (not fixed_ips - or ipaddress.ip_address( - fixed_ips[0]['ip_address']).version != 6): + or ipaddress.ip_address(fixed_ips[0]['ip_address']).version != 6): return - - subnet = client.show_subnet( - port['port']['fixed_ips'][0]['subnet_id']).get('subnet') - if subnet and subnet['ipv6_address_mode'] == 'dhcpv6-stateful': + subnet = client.get_subnet(fixed_ips[0]['subnet_id']) + if subnet and subnet.ipv6_address_mode == 'dhcpv6-stateful': for i in range(1, CONF.neutron.dhcpv6_stateful_address_count): fixed_ips.append({'subnet_id': subnet['id']}) - body = {'port': {'fixed_ips': fixed_ips}} - update_neutron_port(context, port['port']['id'], body) + attrs = {'fixed_ips': fixed_ips} + update_neutron_port(context, port.id, attrs, client=client) def add_ports_to_network(task, network_uuid, security_groups=None): @@ -291,27 +266,22 @@ def add_ports_to_network(task, network_uuid, security_groups=None): {'net_iface': task.driver.network.__class__.__name__, 'node': node.uuid, 'network_uuid': network_uuid}) - body = { - 'port': { - 'network_id': network_uuid, - 'admin_state_up': True, - 'binding:vnic_type': VNIC_BAREMETAL, - } - } + attrs = {'network_id': network_uuid, + 'admin_state_up': True, + 'binding:vnic_type': VNIC_BAREMETAL, + } # separate out fields that can only be updated by admins - update_body = { - 'port': { - 'binding:host_id': node.uuid, - 'device_owner': 'baremetal:none', - } - } + update_attrs = {'binding:host_id': node.uuid, + 'device_owner': 'baremetal:none', + } + if security_groups: - body['port']['security_groups'] = security_groups + attrs['security_groups'] = security_groups # Since instance_uuid will not be available during cleaning # operations, we need to check that and populate them only when # available - body['port']['device_id'] = node.instance_uuid or node.uuid + attrs['device_id'] = node.instance_uuid or node.uuid ports = {} failures = [] @@ -329,23 +299,24 @@ def add_ports_to_network(task, network_uuid, security_groups=None): for ironic_port in ports_to_create: # Start with a clean state for each port - port_body = copy.deepcopy(body) - update_port_body = copy.deepcopy(update_body) + port_attrs = copy.deepcopy(attrs) + update_port_attrs = copy.deepcopy(update_attrs) + # Skip ports that are missing required information for deploy. if not validate_port_info(node, ironic_port): failures.append(ironic_port.uuid) continue - update_port_body['port']['mac_address'] = ironic_port.address + + update_port_attrs['mac_address'] = ironic_port.address binding_profile = {'local_link_information': [portmap[ironic_port.uuid]]} - update_port_body['port']['binding:profile'] = binding_profile + update_port_attrs['binding:profile'] = binding_profile if not ironic_port.pxe_enabled: LOG.debug("Adding port %(port)s to network %(net)s for " "provisioning without an IP allocation.", - {'port': ironic_port.uuid, - 'net': network_uuid}) - port_body['fixed_ips'] = [] + {'port': ironic_port.uuid, 'net': network_uuid}) + port_attrs['fixed_ips'] = [] is_smart_nic = is_smartnic_port(ironic_port) if is_smart_nic: @@ -354,29 +325,29 @@ def add_ports_to_network(task, network_uuid, security_groups=None): 'port %(port_id)s, hostname %(hostname)s', {'port_id': ironic_port.uuid, 'hostname': link_info['hostname']}) - update_port_body['port']['binding:host_id'] = link_info['hostname'] + update_port_attrs['binding:host_id'] = link_info['hostname'] # TODO(hamdyk): use portbindings.VNIC_SMARTNIC from neutron-lib - port_body['port']['binding:vnic_type'] = VNIC_SMARTNIC + port_attrs['binding:vnic_type'] = VNIC_SMARTNIC + client_id = ironic_port.extra.get('client-id') if client_id: - client_id_opt = {'opt_name': DHCP_CLIENT_ID, - 'opt_value': client_id} - extra_dhcp_opts = port_body['port'].get('extra_dhcp_opts', []) - extra_dhcp_opts.append(client_id_opt) - port_body['port']['extra_dhcp_opts'] = extra_dhcp_opts + extra_dhcp_opts = port_attrs.get('extra_dhcp_opts', []) + extra_dhcp_opts.append( + {'opt_name': DHCP_CLIENT_ID, 'opt_value': client_id}) + port_attrs['extra_dhcp_opts'] = extra_dhcp_opts + try: if is_smart_nic: wait_for_host_agent( - client, update_port_body['port']['binding:host_id']) - port = client.create_port(port_body) - update_neutron_port(task.context, port['port']['id'], - update_port_body) + client, update_port_attrs['binding:host_id']) + port = client.create_port(**port_attrs) + update_neutron_port(task.context, port.id, update_port_attrs) if CONF.neutron.dhcpv6_stateful_address_count > 1: _add_ip_addresses_for_ipv6_stateful(task.context, port, client) if is_smart_nic: - wait_for_port_status(client, port['port']['id'], 'ACTIVE') - except neutron_exceptions.NeutronClientException as e: + wait_for_port_status(client, port.id, 'ACTIVE') + except openstack_exc.OpenStackCloudException as e: failures.append(ironic_port.uuid) LOG.warning("Could not create neutron port for node's " "%(node)s port %(ir-port)s on the neutron " @@ -384,7 +355,7 @@ def add_ports_to_network(task, network_uuid, security_groups=None): {'net': network_uuid, 'node': node.uuid, 'ir-port': ironic_port.uuid, 'exc': e}) else: - ports[ironic_port.uuid] = port['port']['id'] + ports[ironic_port.uuid] = port.id if failures: if len(failures) == len(ports_to_create): @@ -439,15 +410,14 @@ def remove_neutron_ports(task, params): node_uuid = task.node.uuid try: - response = client.list_ports(**params) - except neutron_exceptions.NeutronClientException as e: + ports = client.ports(**params) + except openstack_exc.OpenStackCloudException as e: msg = (_('Could not get given network VIF for %(node)s ' 'from neutron, possible network issue. %(exc)s') % {'node': node_uuid, 'exc': e}) LOG.exception(msg) raise exception.NetworkError(msg) - ports = response.get('ports', []) if not ports: LOG.debug('No ports to remove for node %s', node_uuid) return @@ -460,14 +430,14 @@ def remove_neutron_ports(task, params): if is_smartnic_port(port): wait_for_host_agent(client, port['binding:host_id']) try: - client.delete_port(port['id']) + client.delete_port(port) # NOTE(mgoddard): Ignore if the port was deleted by nova. - except neutron_exceptions.PortNotFoundClient: - LOG.info('Port %s was not found while deleting.', port['id']) - except neutron_exceptions.NeutronClientException as e: + except openstack_exc.ResourceNotFound: + LOG.info('Port %s was not found while deleting.', port.id) + except openstack_exc.OpenStackCloudException as e: msg = (_('Could not remove VIF %(vif)s of node %(node)s, possibly ' 'a network issue: %(exc)s') % - {'vif': port['id'], 'node': node_uuid, 'exc': e}) + {'vif': port.id, 'node': node_uuid, 'exc': e}) LOG.exception(msg) raise exception.NetworkError(msg) @@ -508,11 +478,9 @@ def get_neutron_port_data(port_id, vif_id, client=None, context=None): client = get_client(context=context) try: - port_config = client.show_port( - vif_id, fields=['id', 'name', 'dns_assignment', 'fixed_ips', - 'mac_address', 'network_id']) + port_config = client.get_port(vif_id) - except neutron_exceptions.NeutronClientException as e: + except openstack_exc.OpenStackCloudException as e: msg = (_('Unable to get port info for %(port_id)s. Error: ' '%(err)s') % {'port_id': vif_id, 'err': e}) LOG.exception(msg) @@ -521,17 +489,14 @@ def get_neutron_port_data(port_id, vif_id, client=None, context=None): LOG.debug('Received port %(port)s data: %(info)s', {'port': vif_id, 'info': port_config}) - port_config = port_config['port'] - port_id = port_config['name'] or port_id - network_id = port_config.get('network_id') + network_id = port_config.network_id try: - network_config = client.show_network( - network_id, fields=['id', 'mtu', 'subnets']) + network_config = client.get_network(network_id) - except neutron_exceptions.NeutronClientException as e: + except openstack_exc.OpenStackCloudException as e: msg = (_('Unable to get network info for %(network_id)s. Error: ' '%(err)s') % {'network_id': network_id, 'err': e}) LOG.exception(msg) @@ -540,8 +505,6 @@ def get_neutron_port_data(port_id, vif_id, client=None, context=None): LOG.debug('Received network %(network)s data: %(info)s', {'network': network_id, 'info': network_config}) - network_config = network_config['network'] - subnets_config = {} network_data = { @@ -563,17 +526,14 @@ def get_neutron_port_data(port_id, vif_id, client=None, context=None): subnet_id = fixed_ip['subnet_id'] try: - subnet_config = client.show_subnet( - subnet_id, fields=['id', 'name', 'enable_dhcp', - 'dns_nameservers', 'host_routes', - 'ip_version', 'gateway_ip', 'cidr']) + subnet_config = client.get_subnet(subnet_id) LOG.debug('Received subnet %(subnet)s data: %(info)s', {'subnet': subnet_id, 'info': subnet_config}) - subnets_config[subnet_id] = subnet_config['subnet'] + subnets_config[subnet_id] = subnet_config - except neutron_exceptions.NeutronClientException as e: + except openstack_exc.OpenStackCloudException as e: msg = (_('Unable to get subnet info for %(subnet_id)s. Error: ' '%(err)s') % {'subnet_id': subnet_id, 'err': e}) LOG.exception(msg) @@ -582,7 +542,7 @@ def get_neutron_port_data(port_id, vif_id, client=None, context=None): subnet_config = subnets_config[subnet_id] subnet_network, netmask = _uncidr( - subnet_config['cidr'], subnet_config['ip_version'] == 6) + subnet_config.cidr, subnet_config.ip_version == 6) network = { 'id': fixed_ip['subnet_id'], @@ -728,8 +688,9 @@ def validate_network(uuid_or_name, net_type=_('network'), context=None): client = get_client(context=context) network = _get_network_by_uuid_or_name(client, uuid_or_name, - net_type=net_type, fields=['id']) - return network['id'] + net_type=net_type) + + return network.id def validate_port_info(node, port): @@ -781,12 +742,12 @@ def _validate_agent(client, **kwargs): :raises: NetworkError in case of failure contacting Neutron. """ try: - agents = client.list_agents(**kwargs)['agents'] + agents = client.agents(**kwargs) for agent in agents: - if agent['alive']: + if agent.is_alive: return True return False - except neutron_exceptions.NeutronClientException: + except openstack_exc.OpenStackCloudException: raise exception.NetworkError('Failed to contact Neutron server') @@ -807,67 +768,58 @@ def is_smartnic_port(port_data): return False -def _get_network_by_uuid_or_name(client, uuid_or_name, net_type=_('network'), - **params): +def _get_network_by_uuid_or_name(client, uuid_or_name, net_type=_('network')): """Return a neutron network by UUID or name. :param client: A Neutron client object. :param uuid_or_name: network UUID or name :param net_type: human-readable network type for error messages - :param params: Additional parameters to pass to the neutron client - list_networks method. :returns: A dict describing the neutron network. :raises: NetworkError on failure to contact Neutron :raises: InvalidParameterValue for missing or duplicated network """ - if uuidutils.is_uuid_like(uuid_or_name): - params['id'] = uuid_or_name - else: - params['name'] = uuid_or_name try: - networks = client.list_networks(**params) - except neutron_exceptions.NeutronClientException as exc: - raise exception.NetworkError(_('Could not retrieve network list: %s') % - exc) - - LOG.debug('Got list of networks matching %(cond)s: %(result)s', - {'cond': params, 'result': networks}) - networks = networks.get('networks', []) - if not networks: - raise exception.InvalidParameterValue( - _('%(type)s with name or UUID %(uuid_or_name)s was not found') % - {'type': net_type, 'uuid_or_name': uuid_or_name}) - elif len(networks) > 1: - network_ids = [n['id'] for n in networks] + network = client.find_network(uuid_or_name, ignore_missing=False) + except openstack_exc.DuplicateResource: + network_ids = [net.id for net in client.networks(name=uuid_or_name)] raise exception.InvalidParameterValue( _('More than one %(type)s was found for name %(name)s: %(nets)s') % {'name': uuid_or_name, 'nets': ', '.join(network_ids), 'type': net_type}) - return networks[0] + except openstack_exc.ResourceNotFound: + raise exception.InvalidParameterValue( + _('%(type)s with name or UUID %(uuid_or_name)s was not found') % + {'type': net_type, 'uuid_or_name': uuid_or_name}) + except openstack_exc.OpenStackCloudException as exc: + raise exception.NetworkError(_('Could not retrieve network: %s') % exc) + + LOG.debug('Got network matching %(uuid_or_name)s: %(result)s', + {'uuid_or_name': uuid_or_name, 'result': network}) + + return network -def _get_port_by_uuid(client, port_uuid, **params): +def _get_port_by_uuid(client, port_uuid): """Return a neutron port by UUID. :param client: A Neutron client object. :param port_uuid: UUID of a Neutron port to query. - :param params: Additional parameters to pass to the neutron client - show_port method. :returns: A dict describing the neutron port. :raises: InvalidParameterValue if the port does not exist. :raises: NetworkError on failure to contact Neutron. """ try: - port = client.show_port(port_uuid, **params) - except neutron_exceptions.PortNotFoundClient: + port = client.get_port(port_uuid) + except openstack_exc.ResourceNotFound: raise exception.InvalidParameterValue( _('Neutron port %(port_uuid)s was not found') % {'port_uuid': port_uuid}) - except neutron_exceptions.NeutronClientException as exc: + except openstack_exc.OpenStackCloudException as exc: raise exception.NetworkError(_('Could not retrieve neutron port: %s') % exc) - return port['port'] + + return port def get_physnets_by_port_uuid(client, port_uuid): @@ -882,27 +834,25 @@ def get_physnets_by_port_uuid(client, port_uuid): :raises: NetworkError if the network query fails. :raises: InvalidParameterValue for missing network. """ - port = _get_port_by_uuid(client, port_uuid, fields=['network_id']) - network_uuid = port['network_id'] + port = _get_port_by_uuid(client, port_uuid) + network_uuid = port.network_id - fields = [PHYSNET_PARAM_NAME, SEGMENTS_PARAM_NAME] - network = _get_network_by_uuid_or_name(client, network_uuid, fields=fields) + network = _get_network_by_uuid_or_name(client, network_uuid) - if SEGMENTS_PARAM_NAME in network: + if network.segments is not None: # A network with multiple segments will have a 'segments' parameter # which will contain a list of segments. Each segment should have a # 'provider:physical_network' parameter which contains the physical # network of the segment. - segments = network[SEGMENTS_PARAM_NAME] + return set(segment[PHYSNET_PARAM_NAME] + for segment in network.segments + if segment[PHYSNET_PARAM_NAME]) else: # A network with a single segment will have a # 'provider:physical_network' parameter which contains the network's # physical network. - segments = [network] - - return set(segment[PHYSNET_PARAM_NAME] - for segment in segments - if segment[PHYSNET_PARAM_NAME]) + return (set([network.provider_physical_network]) + if network.provider_physical_network else set()) @retrying.retry( @@ -963,10 +913,10 @@ def wait_for_port_status(client, port_id, status): """ LOG.debug('Validating Port %(port_id)s status is %(status)s', {'port_id': port_id, 'status': status}) - port_info = _get_port_by_uuid(client, port_id) + port = _get_port_by_uuid(client, port_id) LOG.debug('Port %(port_id)s status is: %(status)s', - {'port_id': port_id, 'status': port_info['status']}) - if port_info['status'] == status: + {'port_id': port_id, 'status': port.status}) + if port.status == status: return True raise exception.NetworkError( 'Port %(port_id)s failed to reach status %(status)s' % { diff --git a/ironic/conf/auth.py b/ironic/conf/auth.py index 122d84f97b..9a6e86b736 100644 --- a/ironic/conf/auth.py +++ b/ironic/conf/auth.py @@ -14,7 +14,7 @@ import copy -from keystoneauth1 import loading as kaloading +from keystoneauth1 import loading as ks_loading from oslo_config import cfg from oslo_log import log @@ -30,9 +30,9 @@ def register_auth_opts(conf, group, service_type=None): Registers only basic auth options shared by all auth plugins. The rest are registered at runtime depending on auth plugin used. """ - kaloading.register_session_conf_options(conf, group) - kaloading.register_auth_conf_options(conf, group) - kaloading.register_adapter_conf_options(conf, group) + ks_loading.register_session_conf_options(conf, group) + ks_loading.register_auth_conf_options(conf, group) + ks_loading.register_adapter_conf_options(conf, group) conf.set_default('valid_interfaces', DEFAULT_VALID_INTERFACES, group=group) # TODO(pas-ha) use os-service-type to try find the service_type by the # config group name assuming it is a project name (e.g. 'glance') @@ -56,16 +56,16 @@ def add_auth_opts(options, service_type=None): opts.append(new_opt) opts = copy.deepcopy(options) - opts.insert(0, kaloading.get_auth_common_conf_options()[0]) + opts.insert(0, ks_loading.get_auth_common_conf_options()[0]) # NOTE(dims): There are a lot of auth plugins, we just generate # the config options for a few common ones plugins = ['password', 'v2password', 'v3password'] for name in plugins: - plugin = kaloading.get_plugin_loader(name) - add_options(opts, kaloading.get_auth_plugin_conf_options(plugin)) - add_options(opts, kaloading.get_session_conf_options()) + plugin = ks_loading.get_plugin_loader(name) + add_options(opts, ks_loading.get_auth_plugin_conf_options(plugin)) + add_options(opts, ks_loading.get_session_conf_options()) if service_type: - adapter_opts = kaloading.get_adapter_conf_options( + adapter_opts = ks_loading.get_adapter_conf_options( include_deprecated=False) # adding defaults for valid interfaces cfg.set_defaults(adapter_opts, service_type=service_type, diff --git a/ironic/conf/neutron.py b/ironic/conf/neutron.py index d90395f52c..0acfab5aa6 100644 --- a/ironic/conf/neutron.py +++ b/ironic/conf/neutron.py @@ -29,7 +29,11 @@ opts = [ cfg.IntOpt('retries', default=3, mutable=True, - help=_('Client retries in the case of a failed request.')), + deprecated_for_removal=True, + deprecated_reason=_('Replaced by status_code_retries and ' + 'status_code_retry_delay.'), + help=_('DEPRECATED: Client retries in the case of a failed ' + 'request.')), cfg.StrOpt('cleaning_network', mutable=True, help=_('Neutron network UUID or name for the ramdisk to be ' diff --git a/ironic/conf/opts.py b/ironic/conf/opts.py index 2c4ec2d045..3d6e830eb3 100644 --- a/ironic/conf/opts.py +++ b/ironic/conf/opts.py @@ -96,10 +96,10 @@ def update_opt_defaults(): 'eventlet.wsgi.server=INFO', 'iso8601=WARNING', 'requests=WARNING', - 'neutronclient=WARNING', 'glanceclient=WARNING', 'urllib3.connectionpool=WARNING', 'keystonemiddleware.auth_token=INFO', 'keystoneauth.session=INFO', + 'openstack=WARNING', ] ) diff --git a/ironic/dhcp/neutron.py b/ironic/dhcp/neutron.py index bf42266c52..13317cd2eb 100644 --- a/ironic/dhcp/neutron.py +++ b/ironic/dhcp/neutron.py @@ -17,7 +17,7 @@ import ipaddress import time -from neutronclient.common import exceptions as neutron_client_exc +from openstack.connection import exceptions as openstack_exc from oslo_log import log as logging from ironic.common import exception @@ -62,11 +62,10 @@ class NeutronDHCPApi(base.BaseDHCP): super(NeutronDHCPApi, self).update_port_dhcp_opts( port_id, dhcp_options, token=token, context=context) try: - neutron_client = neutron.get_client(token=token, - context=context) + neutron_client = neutron.get_client(token=token, context=context) fip = None - port = neutron_client.show_port(port_id).get('port') + port = neutron_client.get_port(port_id) try: if port: # TODO(TheJulia): We need to retool this down the @@ -90,9 +89,9 @@ class NeutronDHCPApi(base.BaseDHCP): else: LOG.error('Requested to update port for port %s, ' 'however port lacks an IP address.', port_id) - port_req_body = {'port': {'extra_dhcp_opts': update_opts}} - neutron.update_neutron_port(context, port_id, port_req_body) - except neutron_client_exc.NeutronClientException: + port_attrs = {'extra_dhcp_opts': update_opts} + neutron.update_neutron_port(context, port_id, port_attrs) + except openstack_exc.OpenStackCloudException: LOG.exception("Failed to update Neutron port %s.", port_id) raise exception.FailedToUpdateDHCPOptOnPort(port_id=port_id) @@ -160,10 +159,10 @@ class NeutronDHCPApi(base.BaseDHCP): LOG.debug("Waiting %d seconds for Neutron.", port_delay) time.sleep(port_delay) - def _get_fixed_ip_address(self, port_uuid, client): + def _get_fixed_ip_address(self, port_id, client): """Get a Neutron port's fixed ip address. - :param port_uuid: Neutron port id. + :param port_id: Neutron port id. :param client: Neutron client instance. :returns: Neutron port ip address. :raises: NetworkError @@ -172,10 +171,10 @@ class NeutronDHCPApi(base.BaseDHCP): """ ip_address = None try: - neutron_port = client.show_port(port_uuid).get('port') - except neutron_client_exc.NeutronClientException: + neutron_port = client.get_port(port_id) + except openstack_exc.OpenStackCloudException: raise exception.NetworkError( - _('Could not retrieve neutron port: %s') % port_uuid) + _('Could not retrieve neutron port: %s') % port_id) fixed_ips = neutron_port.get('fixed_ips') @@ -192,18 +191,18 @@ class NeutronDHCPApi(base.BaseDHCP): return ip_address else: LOG.error("Neutron returned invalid IP " - "address %(ip_address)s on port %(port_uuid)s.", - {'ip_address': ip_address, - 'port_uuid': port_uuid}) - raise exception.InvalidIPAddress(ip_address=ip_address) + "address %(ip_address)s on port %(port_id)s.", + {'ip_address': ip_address, 'port_id': port_id}) + + raise exception.InvalidIPv4Address(ip_address=ip_address) except ValueError as exc: LOG.error("An Invalid IP address was supplied and failed " "basic validation: %s", exc) raise exception.InvalidIPAddress(ip_address=ip_address) else: LOG.error("No IP address assigned to Neutron port %s.", - port_uuid) - raise exception.FailedToGetIPAddressOnPort(port_id=port_uuid) + port_id) + raise exception.FailedToGetIPAddressOnPort(port_id=port_id) def _get_port_ip_address(self, task, p_obj, client): """Get ip address of ironic port/portgroup assigned by Neutron. @@ -244,8 +243,7 @@ class NeutronDHCPApi(base.BaseDHCP): ip_addresses = [] for obj in pobj_list: try: - vif_ip_address = self._get_port_ip_address(task, obj, - client) + vif_ip_address = self._get_port_ip_address(task, obj, client) ip_addresses.append(vif_ip_address) except (exception.FailedToGetIPAddressOnPort, exception.InvalidIPv4Address, diff --git a/ironic/drivers/modules/network/common.py b/ironic/drivers/modules/network/common.py index 2c3c4be0c0..da6cfff46d 100644 --- a/ironic/drivers/modules/network/common.py +++ b/ironic/drivers/modules/network/common.py @@ -15,7 +15,7 @@ import collections -from neutronclient.common import exceptions as neutron_exceptions +from openstack.connection import exceptions as openstack_exc from oslo_config import cfg from oslo_log import log @@ -267,47 +267,40 @@ def plug_port_to_tenant_network(task, port_like_obj, client=None): # NOTE(sambetts) Only update required binding: attributes, # because other port attributes may have been set by the user or # nova. - body = { - 'port': { - 'binding:vnic_type': neutron.VNIC_BAREMETAL, - 'binding:host_id': node.uuid, - 'mac_address': port_like_obj.address - } - } + port_attrs = {'binding:vnic_type': neutron.VNIC_BAREMETAL, + 'binding:host_id': node.uuid, + 'mac_address': port_like_obj.address} binding_profile = {'local_link_information': local_link_info} if local_group_info: binding_profile['local_group_information'] = local_group_info - body['port']['binding:profile'] = binding_profile + port_attrs['binding:profile'] = binding_profile if client_id_opt: - body['port']['extra_dhcp_opts'] = [client_id_opt] + port_attrs['extra_dhcp_opts'] = [client_id_opt] is_smart_nic = neutron.is_smartnic_port(port_like_obj) if is_smart_nic: link_info = local_link_info[0] LOG.debug('Setting hostname as host_id in case of Smart NIC, ' 'port %(port_id)s, hostname %(hostname)s', - {'port_id': vif_id, - 'hostname': link_info['hostname']}) - body['port']['binding:host_id'] = link_info['hostname'] - body['port']['binding:vnic_type'] = neutron.VNIC_SMARTNIC + {'port_id': vif_id, 'hostname': link_info['hostname']}) + port_attrs['binding:host_id'] = link_info['hostname'] + port_attrs['binding:vnic_type'] = neutron.VNIC_SMARTNIC if not client: client = neutron.get_client(context=task.context) if is_smart_nic: - neutron.wait_for_host_agent(client, body['port']['binding:host_id']) + neutron.wait_for_host_agent(client, port_attrs['binding:host_id']) try: - neutron.update_neutron_port(task.context, vif_id, body) + neutron.update_neutron_port(task.context, vif_id, port_attrs) if is_smart_nic: neutron.wait_for_port_status(client, vif_id, 'ACTIVE') - except neutron_exceptions.ConnectionFailed as e: + except openstack_exc.OpenStackCloudException as e: msg = (_('Could not add public network VIF %(vif)s ' 'to node %(node)s, possible network issue. %(exc)s') % - {'vif': vif_id, - 'node': node.uuid, - 'exc': e}) + {'vif': vif_id, 'node': node.uuid, 'exc': e}) LOG.error(msg) raise exception.NetworkError(msg) @@ -467,8 +460,7 @@ class NeutronVIFPortIDMixin(VIFPortIDMixin): original_port = objects.Port.get_by_id(context, port_obj.id) updated_client_id = port_obj.extra.get('client-id') - if (original_port.extra.get('client-id') - != updated_client_id): + if original_port.extra.get('client-id') != updated_client_id: # DHCP Option with opt_value=None will remove it # from the neutron port if vif: @@ -485,8 +477,7 @@ class NeutronVIFPortIDMixin(VIFPortIDMixin): "No VIF found for instance %(instance)s " "port %(port)s when attempting to update port " "client-id.", - {'port': port_uuid, - 'instance': node.instance_uuid}) + {'port': port_uuid, 'instance': node.instance_uuid}) if portgroup_obj and ((set(port_obj.obj_what_changed()) & {'pxe_enabled', 'portgroup_id'}) or vif): diff --git a/ironic/drivers/modules/network/flat.py b/ironic/drivers/modules/network/flat.py index b11de097e8..b5fd9fbcaf 100644 --- a/ironic/drivers/modules/network/flat.py +++ b/ironic/drivers/modules/network/flat.py @@ -14,7 +14,7 @@ Flat network interface. Useful for shared, flat networks. """ -from neutronclient.common import exceptions as neutron_exceptions +from openstack.connection import exceptions as openstack_exc from oslo_config import cfg from oslo_log import log @@ -62,17 +62,13 @@ class FlatNetwork(common.NeutronVIFPortIDMixin, ) if not vif_port_id: continue - body = { - 'port': { - 'binding:host_id': task.node.uuid, - 'binding:vnic_type': neutron.VNIC_BAREMETAL, - 'mac_address': port_like_obj.address - } - } + port_attrs = {'binding:host_id': task.node.uuid, + 'binding:vnic_type': neutron.VNIC_BAREMETAL, + 'mac_address': port_like_obj.address} try: neutron.update_neutron_port(task.context, - vif_port_id, body) - except neutron_exceptions.NeutronClientException as e: + vif_port_id, port_attrs) + except openstack_exc.OpenStackCloudException as e: msg = (_('Unable to set binding:host_id for ' 'neutron port %(port_id)s. Error: ' '%(err)s') % {'port_id': vif_port_id, 'err': e}) diff --git a/ironic/tests/unit/common/test_glance_service.py b/ironic/tests/unit/common/test_glance_service.py index c8532d1b84..5e9eb73118 100644 --- a/ironic/tests/unit/common/test_glance_service.py +++ b/ironic/tests/unit/common/test_glance_service.py @@ -21,7 +21,7 @@ from unittest import mock from glanceclient import client as glance_client from glanceclient import exc as glance_exc -from keystoneauth1 import loading as kaloading +from keystoneauth1 import loading as ks_loading from oslo_config import cfg from oslo_utils import uuidutils import retrying @@ -353,8 +353,8 @@ class CheckImageServiceTestCase(base.TestCase): self.context = context.RequestContext(global_request_id='global') self.service = image_service.GlanceImageService(None, self.context) # NOTE(pas-ha) register keystoneauth dynamic options manually - plugin = kaloading.get_plugin_loader('password') - opts = kaloading.get_auth_plugin_conf_options(plugin) + plugin = ks_loading.get_plugin_loader('password') + opts = ks_loading.get_auth_plugin_conf_options(plugin) self.cfg_fixture.register_opts(opts, group='glance') self.config(auth_type='password', auth_url='viking', diff --git a/ironic/tests/unit/common/test_keystone.py b/ironic/tests/unit/common/test_keystone.py index ffc08a7f7d..5cf0ba79e7 100644 --- a/ironic/tests/unit/common/test_keystone.py +++ b/ironic/tests/unit/common/test_keystone.py @@ -14,7 +14,7 @@ from unittest import mock -from keystoneauth1 import loading as kaloading +from keystoneauth1 import loading as ks_loading from oslo_config import cfg from oslo_config import fixture @@ -38,8 +38,8 @@ class KeystoneTestCase(base.TestCase): # NOTE(pas-ha) this is due to auth_plugin options # being dynamically registered on first load, # but we need to set the config before - plugin = kaloading.get_plugin_loader('password') - opts = kaloading.get_auth_plugin_conf_options(plugin) + plugin = ks_loading.get_plugin_loader('password') + opts = ks_loading.get_auth_plugin_conf_options(plugin) self.cfg_fixture.register_opts(opts, group=self.test_group) self.config(auth_url='http://127.0.0.1:9898', username='fake_user', diff --git a/ironic/tests/unit/common/test_neutron.py b/ironic/tests/unit/common/test_neutron.py index d290aaa61e..ed2a634243 100644 --- a/ironic/tests/unit/common/test_neutron.py +++ b/ironic/tests/unit/common/test_neutron.py @@ -16,9 +16,9 @@ import os import time from unittest import mock -from keystoneauth1 import loading as kaloading -from neutronclient.common import exceptions as neutron_client_exc -from neutronclient.v2_0 import client +from keystoneauth1 import loading as ks_loading +import openstack +from openstack.connection import exceptions as openstack_exc from oslo_utils import uuidutils from ironic.common import context @@ -28,7 +28,7 @@ from ironic.conductor import task_manager from ironic.tests import base from ironic.tests.unit.db import base as db_base from ironic.tests.unit.objects import utils as object_utils - +from ironic.tests.unit import stubs @mock.patch('ironic.common.keystone.get_service_auth', autospec=True, return_value=mock.sentinel.sauth) @@ -37,14 +37,14 @@ from ironic.tests.unit.objects import utils as object_utils @mock.patch('ironic.common.keystone.get_adapter', autospec=True) @mock.patch('ironic.common.keystone.get_session', autospec=True, return_value=mock.sentinel.session) -@mock.patch.object(client.Client, "__init__", return_value=None, autospec=True) +@mock.patch.object(openstack.connection, "Connection", autospec=True) class TestNeutronClient(base.TestCase): def setUp(self): super(TestNeutronClient, self).setUp() # NOTE(pas-ha) register keystoneauth dynamic options manually - plugin = kaloading.get_plugin_loader('password') - opts = kaloading.get_auth_plugin_conf_options(plugin) + plugin = ks_loading.get_plugin_loader('password') + opts = ks_loading.get_auth_plugin_conf_options(plugin) self.cfg_fixture.register_opts(opts, group='neutron') self.config(retries=2, group='neutron') @@ -64,12 +64,8 @@ class TestNeutronClient(base.TestCase): def _call_and_assert_client(self, client_mock, url, auth=mock.sentinel.auth): neutron.get_client(context=self.context) - client_mock.assert_called_once_with(mock.ANY, # this is 'self' - session=mock.sentinel.session, - auth=auth, retries=2, - endpoint_override=url, - global_request_id='global', - timeout=45) + client_mock.assert_called_once_with(oslo_conf=mock.ANY, + session=mock.sentinel.session) @mock.patch('ironic.common.context.RequestContext', autospec=True) def test_get_neutron_client_with_token(self, mock_ctxt, mock_client_init, @@ -77,55 +73,34 @@ class TestNeutronClient(base.TestCase): mock_auth, mock_sauth): mock_ctxt.return_value = ctxt = mock.Mock() ctxt.auth_token = 'test-token-123' - mock_adapter.return_value = adapter = mock.Mock() - adapter.get_endpoint.return_value = 'neutron_url' neutron.get_client(token='test-token-123') mock_ctxt.assert_called_once_with(auth_token='test-token-123') - mock_client_init.assert_called_once_with( - mock.ANY, # this is 'self' - session=mock.sentinel.session, - auth=mock.sentinel.sauth, - retries=2, - endpoint_override='neutron_url', - global_request_id=ctxt.global_id, - timeout=45) + mock_client_init.assert_called_once_with(oslo_conf=mock.ANY, + session=mock.sentinel.session) # testing handling of default url_timeout - mock_session.assert_called_once_with('neutron', timeout=10) - mock_adapter.assert_called_once_with('neutron', - session=mock.sentinel.session, - auth=mock.sentinel.auth) - mock_sauth.assert_called_once_with(mock_ctxt.return_value, - 'neutron_url', mock.sentinel.auth) + mock_session.assert_has_calls( + [mock.call('neutron', timeout=10), + mock.call('neutron', auth=mock.sentinel.sauth, timeout=10)]) def test_get_neutron_client_with_context(self, mock_client_init, mock_session, mock_adapter, mock_auth, mock_sauth): self.context = context.RequestContext(global_request_id='global', auth_token='test-token-123') - mock_adapter.return_value = adapter = mock.Mock() - adapter.get_endpoint.return_value = 'neutron_url' - self._call_and_assert_client(mock_client_init, 'neutron_url', - auth=mock.sentinel.sauth) + self._call_and_assert_client(mock_client_init, 'neutron_url') # testing handling of default url_timeout - mock_session.assert_called_once_with('neutron', timeout=10) - mock_adapter.assert_called_once_with('neutron', - session=mock.sentinel.session, - auth=mock.sentinel.auth) - mock_sauth.assert_called_once_with(self.context, 'neutron_url', - mock.sentinel.auth) + mock_session.assert_has_calls( + [mock.call('neutron', timeout=10), + mock.call('neutron', auth=mock.sentinel.sauth, timeout=10)]) def test_get_neutron_client_without_token(self, mock_client_init, mock_session, mock_adapter, mock_auth, mock_sauth): - mock_adapter.return_value = adapter = mock.Mock() - adapter.get_endpoint.return_value = 'neutron_url' self._call_and_assert_client(mock_client_init, 'neutron_url') - mock_session.assert_called_once_with('neutron', timeout=10) - mock_adapter.assert_called_once_with('neutron', - session=mock.sentinel.session, - auth=mock.sentinel.auth) - self.assertEqual(0, mock_sauth.call_count) + mock_session.assert_has_calls( + [mock.call('neutron', timeout=10), + mock.call('neutron', auth=mock.sentinel.auth, timeout=10)]) def test_get_neutron_client_noauth(self, mock_client_init, mock_session, mock_adapter, mock_auth, mock_sauth): @@ -133,60 +108,27 @@ class TestNeutronClient(base.TestCase): auth_type='none', timeout=10, group='neutron') - mock_adapter.return_value = adapter = mock.Mock() - adapter.get_endpoint.return_value = 'neutron_url' self._call_and_assert_client(mock_client_init, 'neutron_url') self.assertEqual('none', neutron.CONF.neutron.auth_type) - mock_session.assert_called_once_with('neutron', timeout=10) - mock_adapter.assert_called_once_with('neutron', - session=mock.sentinel.session, - auth=mock.sentinel.auth) - mock_auth.assert_called_once_with('neutron') - self.assertEqual(0, mock_sauth.call_count) + mock_session.assert_has_calls( + [mock.call('neutron', timeout=10), + mock.call('neutron', auth=mock.sentinel.auth, timeout=10)]) - -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) + def test_get_neutron_client_auth_from_config(self, mock_client_init, + mock_session, mock_adapter, + mock_auth, mock_sauth): + self.context = context.RequestContext(global_request_id='global', + auth_token='test-token-123') + neutron.get_client(context=self.context, auth_from_config=True) + mock_client_init.assert_called_once_with(oslo_conf=mock.ANY, + session=mock.sentinel.session) + mock_sauth.assert_not_called() + # testing handling of default url_timeout + mock_session.assert_has_calls( + [mock.call('neutron', timeout=10), + mock.call('neutron', auth=mock.sentinel.auth, timeout=10)]) class TestUpdateNeutronPort(base.TestCase): @@ -195,54 +137,49 @@ class TestUpdateNeutronPort(base.TestCase): self.uuid = uuidutils.generate_uuid() self.context = context.RequestContext() - self.update_body = {'port': {}} + self.port_attr = {'name': 'name_it'} @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': {}} + def test_update_neutron_port(self, client_mock): + client_mock.return_value.get_port.return_value = {} + client_mock.return_value.update_port.return_value = {'name': 'name_it'} - neutron.update_neutron_port(self.context, self.uuid, self.update_body) + neutron.update_neutron_port(self.context, self.uuid, self.port_attr) - 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) + client_mock.assert_any_call(context=self.context) + client_mock.assert_any_call(context=self.context, + auth_from_config=True) + client_mock.return_value.get_port.assert_called_once_with(self.uuid) + client_mock.return_value.update_port.assert_called_once_with( + self.uuid, **self.port_attr) @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': {}} + def test_update_neutron_port_with_client(self, client_mock): + client_mock.return_value.get_port.return_value = {} + client_mock.return_value.update_port.return_value = { + 'name': 'name_it'} client = mock.Mock() - client.update_port.return_value = {'port': {}} + client.update_port.return_value = {} - neutron.update_neutron_port(self.context, self.uuid, self.update_body, + neutron.update_neutron_port(self.context, self.uuid, self.port_attr, client) self.assertFalse(client_mock.called) - self.assertFalse(conf_client_mock.called) - client.update_port.assert_called_once_with(self.uuid, self.update_body) + client.update_port.assert_called_once_with(self.uuid, **self.port_attr) @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': {}} + def test_update_neutron_port_with_exception(self, client_mock): + client_mock.return_value.get_port.side_effect = \ + openstack_exc.OpenStackCloudException + client_mock.return_value.update_port.return_value = {} self.assertRaises( - neutron_client_exc.NeutronClientException, + openstack_exc.OpenStackCloudException, neutron.update_neutron_port, - self.context, self.uuid, self.update_body) + self.context, self.uuid, self.port_attr) 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) + client_mock.return_value.get_port.assert_called_once_with(self.uuid) class TestNeutronNetworkActions(db_base.DbTestCase): @@ -259,14 +196,13 @@ class TestNeutronNetworkActions(db_base.DbTestCase): address='52:54:00:cf:2d:32', extra={'vif_port_id': uuidutils.generate_uuid()} )] - # Very simple neutron port representation - self.neutron_port = {'id': '132f871f-eaec-4fed-9475-0d54465e0f00', - 'mac_address': '52:54:00:cf:2d:32', - 'fixed_ips': []} + self.neutron_port = stubs.FakeNeutronPort( + id='132f871f-eaec-4fed-9475-0d54465e0f00', + mac_address='52:54:00:cf:2d:32', + fixed_ips=[]) + self.network_uuid = uuidutils.generate_uuid() self.client_mock = mock.Mock() - self.client_mock.list_agents.return_value = { - 'agents': [{'alive': True}]} patcher = mock.patch('ironic.common.neutron.get_client', return_value=self.client_mock, autospec=True) patcher.start() @@ -278,7 +214,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(port_show_file, 'rb') as fl: self.port_data = json.load(fl) - self.client_mock.show_port.return_value = self.port_data + self.client_mock.get_port.return_value = stubs.FakeNeutronPort( + **self.port_data['port']) network_show_file = os.path.join( os.path.dirname(__file__), 'json_samples', @@ -286,7 +223,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(network_show_file, 'rb') as fl: self.network_data = json.load(fl) - self.client_mock.show_network.return_value = self.network_data + self.client_mock.get_network.return_value = stubs.FakeNeutronNetwork( + **self.network_data['network']) subnet_show_file = os.path.join( os.path.dirname(__file__), 'json_samples', @@ -294,7 +232,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(subnet_show_file, 'rb') as fl: self.subnet_data = json.load(fl) - self.client_mock.show_subnet.return_value = self.subnet_data + self.client_mock.get_subnet.return_value = stubs.FakeNeutronSubnet( + **self.subnet_data['subnet']) @mock.patch.object(neutron, 'update_neutron_port', autospec=True) def _test_add_ports_to_network(self, update_mock, is_client_id, @@ -317,49 +256,43 @@ class TestNeutronNetworkActions(db_base.DbTestCase): extra['client-id'] = self._CLIENT_ID port.extra = extra port.save() - expected_create_body = { - 'port': { - 'network_id': self.network_uuid, - 'admin_state_up': True, - 'binding:vnic_type': 'baremetal', - 'device_id': self.node.uuid, - } + expected_create_attrs = { + 'network_id': self.network_uuid, + 'admin_state_up': True, + 'binding:vnic_type': 'baremetal', + 'device_id': self.node.uuid } - expected_update_body = { - 'port': { - 'device_owner': 'baremetal:none', - 'binding:host_id': self.node.uuid, - 'mac_address': port.address, - 'binding:profile': { - 'local_link_information': [port.local_link_connection] - } + expected_update_attrs = { + 'device_owner': 'baremetal:none', + 'binding:host_id': self.node.uuid, + 'mac_address': port.address, + 'binding:profile': { + 'local_link_information': [port.local_link_connection] } } if security_groups: - expected_create_body['port']['security_groups'] = security_groups + expected_create_attrs['security_groups'] = security_groups if is_client_id: - expected_create_body['port']['extra_dhcp_opts'] = ( + expected_create_attrs['extra_dhcp_opts'] = ( [{'opt_name': '61', 'opt_value': self._CLIENT_ID}]) if add_all_ports: - expected_create_body2 = copy.deepcopy(expected_create_body) - expected_update_body2 = copy.deepcopy(expected_update_body) - expected_update_body2['port']['mac_address'] = port2.address - expected_create_body2['fixed_ips'] = [] - neutron_port2 = {'id': '132f871f-eaec-4fed-9475-0d54465e0f01', - 'mac_address': port2.address, - 'fixed_ips': []} - self.client_mock.create_port.side_effect = [ - {'port': self.neutron_port}, - {'port': neutron_port2} - ] - expected = {port.uuid: self.neutron_port['id'], - port2.uuid: neutron_port2['id']} + expected_create_attrs2 = copy.deepcopy(expected_create_attrs) + expected_update_attrs2 = copy.deepcopy(expected_update_attrs) + expected_update_attrs2['mac_address'] = port2.address + expected_create_attrs2['fixed_ips'] = [] + neutron_port2 = stubs.FakeNeutronPort( + id='132f871f-eaec-4fed-9475-0d54465e0f01', + mac_address=port2.address, + fixed_ips=[]) + self.client_mock.create_port.side_effect = [self.neutron_port, + neutron_port2] + expected = {port.uuid: self.neutron_port.id, + port2.uuid: neutron_port2.id} else: - self.client_mock.create_port.return_value = { - 'port': self.neutron_port} + self.client_mock.create_port.return_value = self.neutron_port expected = {port.uuid: self.neutron_port['id']} with task_manager.acquire(self.context, self.node.uuid) as task: @@ -367,21 +300,21 @@ class TestNeutronNetworkActions(db_base.DbTestCase): task, self.network_uuid, security_groups=security_groups) self.assertEqual(expected, ports) if add_all_ports: - create_calls = [mock.call(expected_create_body), - mock.call(expected_create_body2)] + create_calls = [mock.call(**expected_create_attrs), + mock.call(**expected_create_attrs2)] update_calls = [ mock.call(self.context, self.neutron_port['id'], - expected_update_body), + expected_update_attrs), mock.call(self.context, neutron_port2['id'], - expected_update_body2)] + expected_update_attrs2)] self.client_mock.create_port.assert_has_calls(create_calls) update_mock.assert_has_calls(update_calls) else: self.client_mock.create_port.assert_called_once_with( - expected_create_body) + **expected_create_attrs) update_mock.assert_called_once_with( self.context, self.neutron_port['id'], - expected_update_body) + expected_update_attrs) def test_add_ports_to_network(self): self._test_add_ports_to_network(is_client_id=False, @@ -403,66 +336,57 @@ class TestNeutronNetworkActions(db_base.DbTestCase): @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() - self.client_mock.show_subnet.return_value = { - 'subnet': { - 'id': subnet_id, - 'ip_version': 6, - 'ipv6_address_mode': 'dhcpv6-stateful' - } - } - self.neutron_port['fixed_ips'] = [{'subnet_id': subnet_id, - 'ip_address': '2001:db8::1'}] + self.client_mock.get_subnet.return_value = stubs.FakeNeutronSubnet( + id=subnet_id, + ip_version=6, + ipv6_address_mode='dhcpv6-stateful') + self.neutron_port.fixed_ips = [{'subnet_id': subnet_id, + 'ip_address': '2001:db8::1'}] expected_body = { - 'port': { - 'fixed_ips': [ - {'subnet_id': subnet_id, 'ip_address': '2001:db8::1'}, - {'subnet_id': subnet_id}, - {'subnet_id': subnet_id}, - {'subnet_id': subnet_id} - ] - } + 'fixed_ips': [ + {'subnet_id': subnet_id, 'ip_address': '2001:db8::1'}, + {'subnet_id': subnet_id}, + {'subnet_id': subnet_id}, + {'subnet_id': subnet_id} + ] } neutron._add_ip_addresses_for_ipv6_stateful( - self.context, - {'port': self.neutron_port}, - self.client_mock - ) + self.context, self.neutron_port, self.client_mock) mock_update.assert_called_once_with( - self.context, self.neutron_port['id'], expected_body) + self.context, self.neutron_port.id, expected_body, + client=self.client_mock) def test_verify_sec_groups(self): sg_ids = [] + expected_vals = [] for i in range(2): - sg_ids.append(uuidutils.generate_uuid()) - - expected_vals = {'security_groups': []} - for sg in sg_ids: - expected_vals['security_groups'].append({'id': sg}) + uuid = uuidutils.generate_uuid() + sg_ids.append(uuid) + expected_vals.append(stubs.FakeNeutronSecurityGroup(id=uuid)) client = mock.MagicMock() - client.list_security_groups.return_value = expected_vals + client.security_groups.return_value = iter(expected_vals) self.assertIsNone( neutron._verify_security_groups(sg_ids, client)) - client.list_security_groups.assert_called_once_with( - fields='id', id=sg_ids) + client.security_groups.assert_called_once_with(id=sg_ids) def test_verify_sec_groups_less_than_configured(self): sg_ids = [] + expected_vals = [] for i in range(2): - sg_ids.append(uuidutils.generate_uuid()) - - expected_vals = {'security_groups': [{'id': sg_ids[0]}]} + uuid = uuidutils.generate_uuid() + sg_ids.append(uuid) + expected_vals.append(stubs.FakeNeutronSecurityGroup(id=uuid)) client = mock.MagicMock() - client.list_security_groups.return_value = expected_vals + client.security_groups.return_value = iter(expected_vals) self.assertIsNone( neutron._verify_security_groups(sg_ids[:1], client)) - client.list_security_groups.assert_called_once_with( - fields='id', id=sg_ids[:1]) + client.security_groups.assert_called_once_with(id=sg_ids[:1]) def test_verify_sec_groups_more_than_configured(self): sg_ids = [] @@ -470,14 +394,12 @@ class TestNeutronNetworkActions(db_base.DbTestCase): sg_ids.append(uuidutils.generate_uuid()) client = mock.MagicMock() - expected_vals = {'security_groups': []} - client.list_security_groups.return_value = expected_vals + client.get.return_value = iter([]) self.assertRaises( exception.NetworkError, neutron._verify_security_groups, sg_ids, client) - client.list_security_groups.assert_called_once_with( - fields='id', id=sg_ids) + client.security_groups.assert_called_once_with(id=sg_ids) def test_verify_sec_groups_no_sg_from_neutron(self): sg_ids = [] @@ -485,13 +407,12 @@ class TestNeutronNetworkActions(db_base.DbTestCase): sg_ids.append(uuidutils.generate_uuid()) client = mock.MagicMock() - client.list_security_groups.return_value = {} + client.security_groups.return_value = iter([]) self.assertRaises( exception.NetworkError, neutron._verify_security_groups, sg_ids, client) - client.list_security_groups.assert_called_once_with( - fields='id', id=sg_ids) + client.security_groups.assert_called_once_with(id=sg_ids) def test_verify_sec_groups_exception_by_neutronclient(self): sg_ids = [] @@ -499,15 +420,14 @@ class TestNeutronNetworkActions(db_base.DbTestCase): sg_ids.append(uuidutils.generate_uuid()) client = mock.MagicMock() - client.list_security_groups.side_effect = \ - neutron_client_exc.NeutronClientException + client.security_groups.side_effect = ( + openstack_exc.OpenStackCloudException) self.assertRaisesRegex( exception.NetworkError, "Could not retrieve security groups", neutron._verify_security_groups, sg_ids, client) - client.list_security_groups.assert_called_once_with( - fields='id', id=sg_ids) + client.security_groups.assert_called_once_with(id=sg_ids) def test_add_ports_with_client_id_to_network(self): self._test_add_ports_to_network(is_client_id=True) @@ -520,32 +440,28 @@ class TestNeutronNetworkActions(db_base.DbTestCase): self.node.save() port = self.ports[0] expected_create_body = { - 'port': { - 'network_id': self.network_uuid, - 'admin_state_up': True, - 'binding:vnic_type': 'baremetal', - 'device_id': self.node.instance_uuid, - } + 'network_id': self.network_uuid, + 'admin_state_up': True, + 'binding:vnic_type': 'baremetal', + 'device_id': self.node.instance_uuid } expected_update_body = { - 'port': { - 'device_owner': 'baremetal:none', - 'binding:host_id': self.node.uuid, - 'mac_address': port.address, - 'binding:profile': { - 'local_link_information': [port.local_link_connection] - } + 'device_owner': 'baremetal:none', + 'binding:host_id': self.node.uuid, + 'mac_address': port.address, + 'binding:profile': { + 'local_link_information': [port.local_link_connection] } } vpi_mock.return_value = True # Ensure we can create ports - self.client_mock.create_port.return_value = {'port': self.neutron_port} - expected = {port.uuid: self.neutron_port['id']} + self.client_mock.create_port.return_value = self.neutron_port + expected = {port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.uuid) as task: ports = neutron.add_ports_to_network(task, self.network_uuid) self.assertEqual(expected, ports) self.client_mock.create_port.assert_called_once_with( - expected_create_body) + **expected_create_body) update_mock.assert_called_once_with(self.context, self.neutron_port['id'], expected_update_body) @@ -555,7 +471,7 @@ class TestNeutronNetworkActions(db_base.DbTestCase): def test_add_network_all_ports_fail(self, rollback_mock): # Check that if creating a port fails, the ports are cleaned up self.client_mock.create_port.side_effect = \ - neutron_client_exc.ConnectionFailed + openstack_exc.OpenStackCloudException with task_manager.acquire(self.context, self.node.uuid) as task: self.assertRaises( @@ -573,7 +489,7 @@ class TestNeutronNetworkActions(db_base.DbTestCase): extra={'vif_port_id': uuidutils.generate_uuid()} ) self.client_mock.create_port.side_effect = [ - {'port': self.neutron_port}, neutron_client_exc.ConnectionFailed] + self.neutron_port, openstack_exc.OpenStackCloudException] with task_manager.acquire(self.context, self.node.uuid) as task: neutron.add_ports_to_network(task, self.network_uuid) self.assertIn("Could not create neutron port for node's", @@ -649,49 +565,39 @@ class TestNeutronNetworkActions(db_base.DbTestCase): def test_remove_neutron_ports(self): with task_manager.acquire(self.context, self.node.uuid) as task: - self.client_mock.list_ports.return_value = { - 'ports': [self.neutron_port]} + self.client_mock.ports.return_value = iter([self.neutron_port]) neutron.remove_neutron_ports(task, {'param': 'value'}) - self.client_mock.list_ports.assert_called_once_with( - **{'param': 'value'}) - self.client_mock.delete_port.assert_called_once_with( - self.neutron_port['id']) + self.client_mock.ports.assert_called_once_with(**{'param': 'value'}) + self.client_mock.delete_port.assert_called_once_with(self.neutron_port) def test_remove_neutron_ports_list_fail(self): with task_manager.acquire(self.context, self.node.uuid) as task: - self.client_mock.list_ports.side_effect = \ - neutron_client_exc.ConnectionFailed + self.client_mock.ports.side_effect = ( + openstack_exc.OpenStackCloudException) self.assertRaisesRegex( exception.NetworkError, 'Could not get given network VIF', neutron.remove_neutron_ports, task, {'param': 'value'}) - self.client_mock.list_ports.assert_called_once_with( - **{'param': 'value'}) + self.client_mock.ports.assert_called_once_with(**{'param': 'value'}) def test_remove_neutron_ports_delete_fail(self): with task_manager.acquire(self.context, self.node.uuid) as task: self.client_mock.delete_port.side_effect = \ - neutron_client_exc.ConnectionFailed - self.client_mock.list_ports.return_value = { - 'ports': [self.neutron_port]} + openstack_exc.OpenStackCloudException + self.client_mock.ports.return_value = iter([self.neutron_port]) self.assertRaisesRegex( exception.NetworkError, 'Could not remove VIF', neutron.remove_neutron_ports, task, {'param': 'value'}) - self.client_mock.list_ports.assert_called_once_with( - **{'param': 'value'}) - self.client_mock.delete_port.assert_called_once_with( - self.neutron_port['id']) + self.client_mock.ports.assert_called_once_with(**{'param': 'value'}) + self.client_mock.delete_port.assert_called_once_with(self.neutron_port) def test_remove_neutron_ports_delete_race(self): with task_manager.acquire(self.context, self.node.uuid) as task: self.client_mock.delete_port.side_effect = \ - neutron_client_exc.PortNotFoundClient - self.client_mock.list_ports.return_value = { - 'ports': [self.neutron_port]} + openstack_exc.ResourceNotFound + self.client_mock.ports.return_value = iter([self.neutron_port]) neutron.remove_neutron_ports(task, {'param': 'value'}) - self.client_mock.list_ports.assert_called_once_with( - **{'param': 'value'}) - self.client_mock.delete_port.assert_called_once_with( - self.neutron_port['id']) + self.client_mock.ports.assert_called_once_with(**{'param': 'value'}) + self.client_mock.delete_port.assert_called_once_with(self.neutron_port) def test__uncidr_ipv4(self): network, netmask = neutron._uncidr('10.0.0.0/24') @@ -740,7 +646,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(port_show_file, 'rb') as fl: self.port_data = json.load(fl) - self.client_mock.show_port.return_value = self.port_data + self.client_mock.get_port.return_value = stubs.FakeNeutronPort( + **self.port_data['port']) network_show_file = os.path.join( os.path.dirname(__file__), 'json_samples', @@ -748,7 +655,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(network_show_file, 'rb') as fl: self.network_data = json.load(fl) - self.client_mock.show_network.return_value = self.network_data + self.client_mock.get_network.return_value = stubs.FakeNeutronNetwork( + **self.network_data['network']) subnet_show_file = os.path.join( os.path.dirname(__file__), 'json_samples', @@ -756,7 +664,8 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with open(subnet_show_file, 'rb') as fl: self.subnet_data = json.load(fl) - self.client_mock.show_subnet.return_value = self.subnet_data + self.client_mock.get_subnet.return_value = stubs.FakeNeutronSubnet( + **self.subnet_data['subnet']) def test_get_neutron_port_data_ipv6(self): self.load_ipv6_files() @@ -949,13 +858,15 @@ class TestNeutronNetworkActions(db_base.DbTestCase): self.assertFalse(log_mock.warning.called) def test_validate_agent_up(self): - self.client_mock.list_agents.return_value = { - 'agents': [{'alive': True}]} + self.client_mock.agents.return_value = iter([ + stubs.FakeNeutronAgent(alive=True) + ]) self.assertTrue(neutron._validate_agent(self.client_mock)) def test_validate_agent_down(self): - self.client_mock.list_agents.return_value = { - 'agents': [{'alive': False}]} + self.client_mock.agents.return_value = iter([ + stubs.FakeNeutronAgent(alive=False) + ]) self.assertFalse(neutron._validate_agent(self.client_mock)) def test_is_smartnic_port_true(self): @@ -1007,14 +918,15 @@ class TestNeutronNetworkActions(db_base.DbTestCase): @mock.patch.object(neutron, '_get_port_by_uuid') @mock.patch.object(time, 'sleep') def test_wait_for_port_status_up(self, sleep_mock, get_port_mock): - get_port_mock.return_value = {'status': 'ACTIVE'} + get_port_mock.return_value = stubs.FakeNeutronPort(status='ACTIVE') neutron.wait_for_port_status(self.client_mock, 'port_id', 'ACTIVE') sleep_mock.assert_not_called() @mock.patch.object(neutron, '_get_port_by_uuid') @mock.patch.object(time, 'sleep') def test_wait_for_port_status_down(self, sleep_mock, get_port_mock): - get_port_mock.side_effect = [{'status': 'DOWN'}, {'status': 'ACTIVE'}] + get_port_mock.side_effect = [stubs.FakeNeutronPort(status='DOWN'), + stubs.FakeNeutronPort(status='ACTIVE')] neutron.wait_for_port_status(self.client_mock, 'port_id', 'ACTIVE') sleep_mock.assert_called_once() @@ -1022,7 +934,7 @@ class TestNeutronNetworkActions(db_base.DbTestCase): @mock.patch.object(time, 'sleep') def test_wait_for_port_status_active_max_retry(self, sleep_mock, get_port_mock): - get_port_mock.return_value = {'status': 'DOWN'} + get_port_mock.return_value = stubs.FakeNeutronPort(status='DOWN') self.assertRaises(exception.NetworkError, neutron.wait_for_port_status, self.client_mock, 'port_id', 'ACTIVE') @@ -1031,7 +943,7 @@ class TestNeutronNetworkActions(db_base.DbTestCase): @mock.patch.object(time, 'sleep') def test_wait_for_port_status_down_max_retry(self, sleep_mock, get_port_mock): - get_port_mock.return_value = {'status': 'ACTIVE'} + get_port_mock.return_value = stubs.FakeNeutronPort(status='ACTIVE') self.assertRaises(exception.NetworkError, neutron.wait_for_port_status, self.client_mock, 'port_id', 'DOWN') @@ -1058,40 +970,35 @@ class TestNeutronNetworkActions(db_base.DbTestCase): port.is_smartnic = True port.save() - expected_create_body = { - 'port': { - 'network_id': self.network_uuid, - 'admin_state_up': True, - 'binding:vnic_type': 'smart-nic', - 'device_id': self.node.uuid, - } + expected_create_attrs = { + 'network_id': self.network_uuid, + 'admin_state_up': True, + 'binding:vnic_type': 'smart-nic', + 'device_id': self.node.uuid, } - expected_update_body = { - 'port': { - 'device_owner': 'baremetal:none', - 'binding:host_id': port.local_link_connection['hostname'], - 'mac_address': port.address, - 'binding:profile': { - 'local_link_information': [port.local_link_connection] - } - } + expected_update_attrs = { + 'device_owner': 'baremetal:none', + 'binding:host_id': port.local_link_connection['hostname'], + 'mac_address': port.address, + 'binding:profile': { + 'local_link_information': [port.local_link_connection] + }, } # Ensure we can create ports - self.client_mock.create_port.return_value = { - 'port': self.neutron_port} - expected = {port.uuid: self.neutron_port['id']} + self.client_mock.create_port.return_value = self.neutron_port + expected = {port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.uuid) as task: ports = neutron.add_ports_to_network(task, self.network_uuid) self.assertEqual(expected, ports) self.client_mock.create_port.assert_called_once_with( - expected_create_body) + **expected_create_attrs) update_mock.assert_called_once_with( - self.context, self.neutron_port['id'], expected_update_body) + self.context, self.neutron_port.id, expected_update_attrs) wait_agent_mock.assert_called_once_with( self.client_mock, 'hostname') wait_port_mock.assert_called_once_with( - self.client_mock, self.neutron_port['id'], 'ACTIVE') + self.client_mock, self.neutron_port.id, 'ACTIVE') @mock.patch.object(neutron, 'is_smartnic_port', autospec=True) @mock.patch.object(neutron, 'wait_for_host_agent', autospec=True) @@ -1100,13 +1007,12 @@ class TestNeutronNetworkActions(db_base.DbTestCase): with task_manager.acquire(self.context, self.node.uuid) as task: is_smartnic_mock.return_value = True self.neutron_port['binding:host_id'] = 'hostname' - self.client_mock.list_ports.return_value = { - 'ports': [self.neutron_port]} + self.client_mock.ports.return_value = iter([self.neutron_port]) neutron.remove_neutron_ports(task, {'param': 'value'}) - self.client_mock.list_ports.assert_called_once_with( + self.client_mock.ports.assert_called_once_with( **{'param': 'value'}) self.client_mock.delete_port.assert_called_once_with( - self.neutron_port['id']) + self.neutron_port) is_smartnic_mock.assert_called_once_with(self.neutron_port) wait_agent_mock.assert_called_once_with(self.client_mock, 'hostname') @@ -1120,67 +1026,50 @@ class TestValidateNetwork(base.TestCase): self.context = context.RequestContext() def test_by_uuid(self, client_mock): - net_mock = client_mock.return_value.list_networks - net_mock.return_value = { - 'networks': [ - {'id': self.uuid}, - ] - } + net_mock = client_mock.return_value.find_network + net_mock.return_value = stubs.FakeNeutronNetwork(id=self.uuid) self.assertEqual(self.uuid, neutron.validate_network( self.uuid, context=self.context)) - net_mock.assert_called_once_with(fields=['id'], - id=self.uuid) + net_mock.assert_called_once_with(self.uuid, ignore_missing=False) def test_by_name(self, client_mock): - net_mock = client_mock.return_value.list_networks - net_mock.return_value = { - 'networks': [ - {'id': self.uuid}, - ] - } + net_mock = client_mock.return_value.find_network + net_mock.return_value = stubs.FakeNeutronNetwork(id=self.uuid, + name='name') self.assertEqual(self.uuid, neutron.validate_network( 'name', context=self.context)) - net_mock.assert_called_once_with(fields=['id'], - name='name') + net_mock.assert_called_once_with('name', ignore_missing=False) def test_not_found(self, client_mock): - net_mock = client_mock.return_value.list_networks - net_mock.return_value = { - 'networks': [] - } + net_mock = client_mock.return_value.find_network + net_mock.side_effect = openstack_exc.ResourceNotFound() self.assertRaisesRegex(exception.InvalidParameterValue, 'was not found', neutron.validate_network, self.uuid, context=self.context) - net_mock.assert_called_once_with(fields=['id'], - id=self.uuid) + net_mock.assert_called_once_with(self.uuid, ignore_missing=False) def test_failure(self, client_mock): - net_mock = client_mock.return_value.list_networks - net_mock.side_effect = neutron_client_exc.NeutronClientException('foo') + net_mock = client_mock.return_value.find_network + net_mock.side_effect = openstack_exc.OpenStackCloudException('foo') self.assertRaisesRegex(exception.NetworkError, 'foo', neutron.validate_network, 'name', context=self.context) - net_mock.assert_called_once_with(fields=['id'], - name='name') + net_mock.assert_called_once_with('name', ignore_missing=False) def test_duplicate(self, client_mock): - net_mock = client_mock.return_value.list_networks - net_mock.return_value = { - 'networks': [{'id': self.uuid}, - {'id': 'uuid2'}] - } + net_mock = client_mock.return_value.find_network + net_mock.side_effect = openstack_exc.DuplicateResource() self.assertRaisesRegex(exception.InvalidParameterValue, 'More than one network', neutron.validate_network, 'name', context=self.context) - net_mock.assert_called_once_with(fields=['id'], - name='name') + net_mock.assert_called_once_with('name', ignore_missing=False) @mock.patch.object(neutron, 'get_client', autospec=True) @@ -1194,8 +1083,8 @@ class TestUpdatePortAddress(base.TestCase): def test_update_port_address(self, mock_unp, mock_client): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - expected = {'port': {'mac_address': address}} - mock_client.return_value.show_port.return_value = {} + expected = {'mac_address': address} + mock_client.return_value.get_port.return_value = {} neutron.update_port_address(port_id, address, context=self.context) mock_unp.assert_called_once_with(self.context, port_id, expected) @@ -1207,15 +1096,12 @@ class TestUpdatePortAddress(base.TestCase): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - mock_client.return_value.show_port.return_value = { - 'port': {'binding:host_id': 'host', - 'binding:profile': 'foo'}} + mock_client.return_value.get_port.return_value = { + 'binding:host_id': 'host', 'binding:profile': 'foo'} - calls = [mock.call(self.context, port_id, - {'port': {'mac_address': address}}), - mock.call(self.context, port_id, - {'port': {'binding:host_id': 'host', - 'binding:profile': 'foo'}})] + calls = [mock.call(self.context, port_id, {'mac_address': address}), + mock.call(self.context, port_id, {'binding:host_id': 'host', + 'binding:profile': 'foo'})] neutron.update_port_address(port_id, address, context=self.context) mock_unp.assert_called_once_with( @@ -1229,9 +1115,9 @@ class TestUpdatePortAddress(base.TestCase): mock_client): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - expected = {'port': {'mac_address': address}} - mock_client.return_value.show_port.return_value = { - 'port': {'binding:profile': 'foo'}} + expected = {'mac_address': address} + mock_client.return_value.get_port.return_value = { + 'binding:profile': 'foo'} neutron.update_port_address(port_id, address, context=self.context) self.assertFalse(mock_unp.called) @@ -1240,8 +1126,8 @@ class TestUpdatePortAddress(base.TestCase): def test_update_port_address_show_failed(self, mock_client): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - mock_client.return_value.show_port.side_effect = ( - neutron_client_exc.NeutronClientException()) + mock_client.return_value.get_port.side_effect = ( + openstack_exc.OpenStackCloudException()) self.assertRaises(exception.FailedToUpdateMacOnPort, neutron.update_port_address, @@ -1253,9 +1139,8 @@ class TestUpdatePortAddress(base.TestCase): mock_client): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - mock_client.return_value.show_port.return_value = { - 'port': {'binding:profile': 'foo', - 'binding:host_id': 'host'}} + mock_client.return_value.get_port.return_value = { + 'binding:profile': 'foo', 'binding:host_id': 'host'} mock_unp.side_effect = (exception.NetworkError('boom')) self.assertRaises(exception.FailedToUpdateMacOnPort, neutron.update_port_address, @@ -1272,9 +1157,9 @@ class TestUpdatePortAddress(base.TestCase): mock_client): address = 'fe:54:00:77:07:d9' port_id = 'fake-port-id' - mock_client.return_value.show_port.return_value = {} + mock_client.return_value.get_port.return_value = {} mock_update.side_effect = ( - neutron_client_exc.NeutronClientException()) + openstack_exc.OpenStackCloudException()) self.assertRaises(exception.FailedToUpdateMacOnPort, neutron.update_port_address, @@ -1290,21 +1175,12 @@ class TestUnbindPort(base.TestCase): def test_unbind_neutron_port_client_passed(self, mock_unp): port_id = 'fake-port-id' - body_unbind = { - 'port': { - 'binding:host_id': '', - 'binding:profile': {} - } - } - body_reset_mac = { - 'port': { - 'mac_address': None - } - } + attr_unbind = {'binding:host_id': '', 'binding:profile': {}} + attr_reset_mac = {'mac_address': None} client = mock.MagicMock() update_calls = [ - mock.call(self.context, port_id, body_unbind, client), - mock.call(self.context, port_id, body_reset_mac, client) + mock.call(self.context, port_id, attr_unbind, client), + mock.call(self.context, port_id, attr_reset_mac, client) ] neutron.unbind_neutron_port(port_id, client, context=self.context) self.assertEqual(2, mock_unp.call_count) @@ -1312,35 +1188,21 @@ class TestUnbindPort(base.TestCase): @mock.patch.object(neutron, 'LOG', autospec=True) def test_unbind_neutron_port_failure(self, mock_log, mock_unp): - mock_unp.side_effect = (neutron_client_exc.NeutronClientException()) - body = { - 'port': { - 'binding:host_id': '', - 'binding:profile': {} - } - } + mock_unp.side_effect = openstack_exc.OpenStackCloudException() + attr = {'binding:host_id': '', 'binding:profile': {}} port_id = 'fake-port-id' self.assertRaises(exception.NetworkError, neutron.unbind_neutron_port, port_id, context=self.context) - mock_unp.assert_called_once_with(self.context, port_id, body, None) + mock_unp.assert_called_once_with(self.context, port_id, attr, None) mock_log.exception.assert_called_once() def test_unbind_neutron_port(self, mock_unp): port_id = 'fake-port-id' - body_unbind = { - 'port': { - 'binding:host_id': '', - 'binding:profile': {} - } - } - body_reset_mac = { - 'port': { - 'mac_address': None - } - } + attr_unbind = {'binding:host_id': '', 'binding:profile': {}} + attr_reset_mac = {'mac_address': None} update_calls = [ - mock.call(self.context, port_id, body_unbind, None), - mock.call(self.context, port_id, body_reset_mac, None) + mock.call(self.context, port_id, attr_unbind, None), + mock.call(self.context, port_id, attr_reset_mac, None) ] neutron.unbind_neutron_port(port_id, context=self.context) mock_unp.assert_has_calls(update_calls) @@ -1349,15 +1211,10 @@ class TestUnbindPort(base.TestCase): def test_unbind_neutron_port_not_found(self, mock_log, mock_unp): port_id = 'fake-port-id' mock_unp.side_effect = ( - neutron_client_exc.PortNotFoundClient()) - body = { - 'port': { - 'binding:host_id': '', - 'binding:profile': {} - } - } + openstack_exc.ResourceNotFound()) + attr = {'binding:host_id': '', 'binding:profile': {}} neutron.unbind_neutron_port(port_id, context=self.context) - mock_unp.assert_called_once_with(self.context, port_id, body, None) + mock_unp.assert_called_once_with(self.context, port_id, attr, None) mock_log.info.assert_called_once_with('Port %s was not found while ' 'unbinding.', port_id) @@ -1370,69 +1227,53 @@ class TestGetNetworkByUUIDOrName(base.TestCase): def test__get_network_by_uuid_or_name_uuid(self): network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7' - networks = { - 'networks': [{ - 'field1': 'value1', - 'field2': 'value2', - }], - } - fields = ['field1', 'field2'] - self.client.list_networks.return_value = networks + network = stubs.FakeNeutronNetwork(id=network_uuid) + self.client.find_network.return_value = network result = neutron._get_network_by_uuid_or_name( - self.client, network_uuid, fields=fields) - self.client.list_networks.assert_called_once_with( - id=network_uuid, fields=fields) - self.assertEqual(networks['networks'][0], result) + self.client, network_uuid) + self.client.find_network.assert_called_once_with( + network_uuid, ignore_missing=False) + self.assertEqual(network, result) def test__get_network_by_uuid_or_name_name(self): network_name = 'test-net' - networks = { - 'networks': [{ - 'field1': 'value1', - 'field2': 'value2', - }], - } - fields = ['field1', 'field2'] - self.client.list_networks.return_value = networks + network = stubs.FakeNeutronNetwork(name=network_name) + self.client.find_network.return_value = network result = neutron._get_network_by_uuid_or_name( - self.client, network_name, fields=fields) - self.client.list_networks.assert_called_once_with( - name=network_name, fields=fields) - self.assertEqual(networks['networks'][0], result) + self.client, network_name) + self.client.find_network.assert_called_once_with( + network_name, ignore_missing=False) + self.assertEqual(network, result) def test__get_network_by_uuid_or_name_failure(self): network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7' - self.client.list_networks.side_effect = ( - neutron_client_exc.NeutronClientException()) + self.client.find_network.side_effect = ( + openstack_exc.OpenStackCloudException()) self.assertRaises(exception.NetworkError, neutron._get_network_by_uuid_or_name, self.client, network_uuid) - self.client.list_networks.assert_called_once_with(id=network_uuid) + self.client.find_network.assert_called_once_with( + network_uuid, ignore_missing=False) def test__get_network_by_uuid_or_name_missing(self): network_uuid = '9acb0256-2c1b-420a-b9d7-62bee90b6ed7' - networks = { - 'networks': [], - } - self.client.list_networks.return_value = networks + self.client.find_network.side_effect = ( + openstack_exc.ResourceNotFound()) self.assertRaises(exception.InvalidParameterValue, neutron._get_network_by_uuid_or_name, self.client, network_uuid) - self.client.list_networks.assert_called_once_with(id=network_uuid) + self.client.find_network.assert_called_once_with( + network_uuid, ignore_missing=False) def test__get_network_by_uuid_or_name_duplicate(self): network_name = 'test-net' - networks = { - 'networks': [ - {'id': '9acb0256-2c1b-420a-b9d7-62bee90b6ed7'}, - {'id': '9014b6a7-8291-4676-80b0-ab00988ce3c7'}, - ], - } - self.client.list_networks.return_value = networks + self.client.find_network.side_effect = ( + openstack_exc.DuplicateResource()) self.assertRaises(exception.InvalidParameterValue, neutron._get_network_by_uuid_or_name, self.client, network_name) - self.client.list_networks.assert_called_once_with(name=network_name) + self.client.find_network.assert_called_once_with( + network_name, ignore_missing=False) @mock.patch.object(neutron, '_get_network_by_uuid_or_name', autospec=True) @@ -1450,88 +1291,69 @@ class TestGetPhysnetsByPortUUID(base.TestCase): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' physnet = 'fake-physnet' - mock_gp.return_value = { - 'network_id': network_uuid, - } - mock_gn.return_value = { - 'provider:physical_network': physnet, - } - result = neutron.get_physnets_by_port_uuid(self.client, - port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) + mock_gn.return_value = stubs.FakeNeutronNetwork( + **{'segments': [ + {'provider:physical_network': physnet} + ]}) + result = neutron.get_physnets_by_port_uuid(self.client, port_uuid) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) self.assertEqual({physnet}, result) - def test_get_physnets_by_port_uuid_single_segment_no_physnet( + def test_get_physnets_by_port_uuid_no_segment_no_physnet( self, mock_gp, mock_gn): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' - mock_gp.return_value = { - 'network_id': network_uuid, - } - mock_gn.return_value = { - 'provider:physical_network': None, - } - result = neutron.get_physnets_by_port_uuid(self.client, - port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) + fake_network = stubs.FakeNeutronNetwork( + **{'provider:physical_network': None}) + mock_gn.return_value = fake_network + result = neutron.get_physnets_by_port_uuid(self.client, port_uuid) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) self.assertEqual(set(), result) + def test_get_physnets_by_port_uuid_no_segment( + self, mock_gp, mock_gn): + port_uuid = 'fake-port-uuid' + network_uuid = 'fake-network-uuid' + physnet = 'fake-physnet' + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) + fake_network = stubs.FakeNeutronNetwork( + **{'provider:physical_network': physnet}) + mock_gn.return_value = fake_network + result = neutron.get_physnets_by_port_uuid(self.client, port_uuid) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) + self.assertEqual({physnet}, result) + def test_get_physnets_by_port_uuid_multiple_segments(self, mock_gp, mock_gn): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' physnet1 = 'fake-physnet-1' physnet2 = 'fake-physnet-2' - mock_gp.return_value = { - 'network_id': network_uuid, - } - mock_gn.return_value = { - 'segments': [ - { - 'provider:physical_network': physnet1, - }, - { - 'provider:physical_network': physnet2, - }, - ], - } - result = neutron.get_physnets_by_port_uuid(self.client, - port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) + mock_gn.return_value = stubs.FakeNeutronNetwork( + **{'segments': [{'provider:physical_network': physnet1}, + {'provider:physical_network': physnet2}]}) + result = neutron.get_physnets_by_port_uuid(self.client, port_uuid) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) self.assertEqual({physnet1, physnet2}, result) def test_get_physnets_by_port_uuid_multiple_segments_no_physnet( self, mock_gp, mock_gn): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' - mock_gp.return_value = { - 'network_id': network_uuid, - } - mock_gn.return_value = { - 'segments': [ - { - 'provider:physical_network': None, - }, - { - 'provider:physical_network': None, - }, - ], - } - result = neutron.get_physnets_by_port_uuid(self.client, - port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) + mock_gn.return_value = stubs.FakeNeutronNetwork( + **{'segments': [{'provider:physical_network': None}, + {'provider:physical_network': None}]}) + result = neutron.get_physnets_by_port_uuid(self.client, port_uuid) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) self.assertEqual(set(), result) def test_get_physnets_by_port_uuid_port_missing(self, mock_gp, mock_gn): @@ -1540,8 +1362,7 @@ class TestGetPhysnetsByPortUUID(base.TestCase): self.assertRaises(exception.InvalidParameterValue, neutron.get_physnets_by_port_uuid, self.client, port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) + mock_gp.assert_called_once_with(self.client, port_uuid) self.assertFalse(mock_gn.called) def test_get_physnets_by_port_uuid_port_failure(self, mock_gp, mock_gn): @@ -1550,38 +1371,29 @@ class TestGetPhysnetsByPortUUID(base.TestCase): self.assertRaises(exception.NetworkError, neutron.get_physnets_by_port_uuid, self.client, port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) + mock_gp.assert_called_once_with(self.client, port_uuid) self.assertFalse(mock_gn.called) def test_get_physnets_by_port_uuid_network_missing( self, mock_gp, mock_gn): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' - mock_gp.return_value = { - 'network_id': network_uuid, - } + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) mock_gn.side_effect = exception.InvalidParameterValue('error') self.assertRaises(exception.InvalidParameterValue, neutron.get_physnets_by_port_uuid, self.client, port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) def test_get_physnets_by_port_uuid_network_failure( self, mock_gp, mock_gn): port_uuid = 'fake-port-uuid' network_uuid = 'fake-network-uuid' - mock_gp.return_value = { - 'network_id': network_uuid, - } + mock_gp.return_value = stubs.FakeNeutronPort(network_id=network_uuid) mock_gn.side_effect = exception.NetworkError self.assertRaises(exception.NetworkError, neutron.get_physnets_by_port_uuid, self.client, port_uuid) - mock_gp.assert_called_once_with(self.client, port_uuid, - fields=self.PORT_FIELDS) - mock_gn.assert_called_once_with(self.client, network_uuid, - fields=self.NETWORK_FIELDS) + mock_gp.assert_called_once_with(self.client, port_uuid) + mock_gn.assert_called_once_with(self.client, network_uuid) diff --git a/ironic/tests/unit/conf/test_auth.py b/ironic/tests/unit/conf/test_auth.py index 5508a77e12..eefe831a01 100644 --- a/ironic/tests/unit/conf/test_auth.py +++ b/ironic/tests/unit/conf/test_auth.py @@ -12,7 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. -from keystoneauth1 import loading as kaloading +from keystoneauth1 import loading as ks_loading from oslo_config import cfg from ironic.conf import auth as ironic_auth @@ -31,8 +31,8 @@ class AuthConfTestCase(base.TestCase): # NOTE(pas-ha) this is due to auth_plugin options # being dynamically registered on first load, # but we need to set the config before - plugin = kaloading.get_plugin_loader('password') - opts = kaloading.get_auth_plugin_conf_options(plugin) + plugin = ks_loading.get_plugin_loader('password') + opts = ks_loading.get_auth_plugin_conf_options(plugin) self.cfg_fixture.register_opts(opts, group=self.test_group) self.config(auth_url='http://127.0.0.1:9898', username='fake_user', diff --git a/ironic/tests/unit/dhcp/test_neutron.py b/ironic/tests/unit/dhcp/test_neutron.py index 23a807da6e..c0921874e9 100644 --- a/ironic/tests/unit/dhcp/test_neutron.py +++ b/ironic/tests/unit/dhcp/test_neutron.py @@ -16,7 +16,7 @@ from unittest import mock -from neutronclient.common import exceptions as neutron_client_exc +from openstack.connection import exceptions as openstack_exc from oslo_utils import uuidutils from ironic.common import dhcp_factory @@ -59,7 +59,7 @@ class TestNeutron(db_base.DbTestCase): {'opt_name': 'server-ip-address', 'opt_value': '1.1.1.1'}] port_id = 'fake-port-id' - expected = {'port': {'extra_dhcp_opts': opts}} + expected = {'extra_dhcp_opts': opts} port_data = { "id": port_id, "fixed_ips": [ @@ -68,7 +68,7 @@ class TestNeutron(db_base.DbTestCase): } ], } - client_mock.return_value.show_port.return_value = {'port': port_data} + client_mock.return_value.get_port.return_value = port_data api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: @@ -94,12 +94,13 @@ class TestNeutron(db_base.DbTestCase): 'ip_version': 6}] port_id = 'fake-port-id' expected = { - 'port': { - 'extra_dhcp_opts': [{ + 'extra_dhcp_opts': [ + { 'opt_name': 'bootfile-url', 'opt_value': 'tftp://::1/file.name', - 'ip_version': 6}] - } + 'ip_version': 6 + } + ] } port_data = { "id": port_id, @@ -109,7 +110,7 @@ class TestNeutron(db_base.DbTestCase): } ], } - client_mock.return_value.show_port.return_value = {'port': port_data} + client_mock.return_value.get_port.return_value = port_data api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: @@ -132,9 +133,8 @@ class TestNeutron(db_base.DbTestCase): } ], } - client_mock.return_value.show_port.return_value = {'port': port_data} - update_mock.side_effect = ( - neutron_client_exc.NeutronClientException()) + client_mock.return_value.get_port.return_value = port_data + update_mock.side_effect = openstack_exc.OpenStackCloudException() api = dhcp_factory.DHCPFactory() with task_manager.acquire(self.context, self.node.uuid) as task: @@ -262,10 +262,10 @@ class TestNeutron(db_base.DbTestCase): "device_id": 'bece68a3-2f8b-4e66-9092-244493d6aba7', } fake_client = mock.Mock() - fake_client.show_port.return_value = {'port': port_data} + fake_client.get_port.return_value = port_data result = api._get_fixed_ip_address(port_id, fake_client) self.assertEqual(expected, result) - fake_client.show_port.assert_called_once_with(port_id) + fake_client.get_port.assert_called_once_with(port_id) def test__get_fixed_ip_address_ipv6(self): port_id = 'fake-port-id' @@ -286,10 +286,10 @@ class TestNeutron(db_base.DbTestCase): "device_id": 'bece68a3-2f8b-4e66-9092-244493d6aba7', } fake_client = mock.Mock() - fake_client.show_port.return_value = {'port': port_data} + fake_client.get_port.return_value = port_data result = api._get_fixed_ip_address(port_id, fake_client) self.assertEqual(expected, result) - fake_client.show_port.assert_called_once_with(port_id) + fake_client.get_port.assert_called_once_with(port_id) def test__get_fixed_ip_address_invalid_ip(self): port_id = 'fake-port-id' @@ -309,22 +309,23 @@ class TestNeutron(db_base.DbTestCase): "device_id": 'bece68a3-2f8b-4e66-9092-244493d6aba7', } fake_client = mock.Mock() - fake_client.show_port.return_value = {'port': port_data} + fake_client.get_port.return_value = port_data self.assertRaises(exception.InvalidIPAddress, api._get_fixed_ip_address, port_id, fake_client) - fake_client.show_port.assert_called_once_with(port_id) + fake_client.get_port.assert_called_once_with(port_id) def test__get_fixed_ip_address_with_exception(self): port_id = 'fake-port-id' api = dhcp_factory.DHCPFactory().provider fake_client = mock.Mock() - fake_client.show_port.side_effect = ( - neutron_client_exc.NeutronClientException()) + fake_client.get_port.side_effect = ( + openstack_exc.OpenStackCloudException()) + self.assertRaises(exception.NetworkError, api._get_fixed_ip_address, port_id, fake_client) - fake_client.show_port.assert_called_once_with(port_id) + fake_client.get_port.assert_called_once_with(port_id) @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi._get_fixed_ip_address', autospec=True) diff --git a/ironic/tests/unit/drivers/modules/network/test_flat.py b/ironic/tests/unit/drivers/modules/network/test_flat.py index 66c75441cf..f76f2a4463 100644 --- a/ironic/tests/unit/drivers/modules/network/test_flat.py +++ b/ironic/tests/unit/drivers/modules/network/test_flat.py @@ -12,7 +12,7 @@ from unittest import mock -from neutronclient.common import exceptions as neutron_exceptions +from openstack.connection import exceptions as openstack_exc from oslo_config import cfg from oslo_utils import uuidutils @@ -178,9 +178,9 @@ class TestFlatInterface(db_base.DbTestCase): utils.create_test_port(self.context, node_id=self.node.id, address='52:54:00:cf:2d:33', extra=extra, uuid=uuidutils.generate_uuid()) - exp_body = {'port': {'binding:host_id': self.node.uuid, - 'binding:vnic_type': neutron.VNIC_BAREMETAL, - 'mac_address': '52:54:00:cf:2d:33'}} + exp_body = {'binding:host_id': self.node.uuid, + 'binding:vnic_type': neutron.VNIC_BAREMETAL, + 'mac_address': '52:54:00:cf:2d:33'} with task_manager.acquire(self.context, self.node.id) as task: self.interface._bind_flat_ports(task) update_mock.assert_called_once_with(self.context, 'foo', exp_body) @@ -194,12 +194,12 @@ class TestFlatInterface(db_base.DbTestCase): utils.create_test_port( self.context, node_id=self.node.id, address='52:54:00:cf:2d:33', extra={'vif_port_id': 'bar'}, uuid=uuidutils.generate_uuid()) - exp_body1 = {'port': {'binding:host_id': self.node.uuid, - 'binding:vnic_type': neutron.VNIC_BAREMETAL, - 'mac_address': '52:54:00:cf:2d:33'}} - exp_body2 = {'port': {'binding:host_id': self.node.uuid, - 'binding:vnic_type': neutron.VNIC_BAREMETAL, - 'mac_address': '52:54:00:cf:2d:31'}} + exp_body1 = {'binding:host_id': self.node.uuid, + 'binding:vnic_type': neutron.VNIC_BAREMETAL, + 'mac_address': '52:54:00:cf:2d:33'} + exp_body2 = {'binding:host_id': self.node.uuid, + 'binding:vnic_type': neutron.VNIC_BAREMETAL, + 'mac_address': '52:54:00:cf:2d:31'} with task_manager.acquire(self.context, self.node.id) as task: self.interface._bind_flat_ports(task) update_mock.assert_has_calls([ @@ -235,7 +235,7 @@ class TestFlatInterface(db_base.DbTestCase): @mock.patch.object(neutron, 'update_neutron_port', autospec=True) def test__bind_flat_ports_set_binding_host_id_raise(self, update_mock): - update_mock.side_effect = (neutron_exceptions.ConnectionFailed()) + update_mock.side_effect = openstack_exc.OpenStackCloudException() extra = {'vif_port_id': 'foo'} utils.create_test_port(self.context, node_id=self.node.id, address='52:54:00:cf:2d:33', extra=extra, diff --git a/ironic/tests/unit/drivers/modules/network/test_neutron.py b/ironic/tests/unit/drivers/modules/network/test_neutron.py index 4d8c5e7bee..bee21422cd 100644 --- a/ironic/tests/unit/drivers/modules/network/test_neutron.py +++ b/ironic/tests/unit/drivers/modules/network/test_neutron.py @@ -13,7 +13,7 @@ import copy from unittest import mock -from neutronclient.common import exceptions as neutron_exceptions +from openstack.connection import exceptions as openstack_exc from oslo_config import cfg from oslo_utils import uuidutils @@ -25,6 +25,7 @@ from ironic.drivers import base as drivers_base from ironic.drivers.modules.network import neutron from ironic.tests.unit.db import base as db_base from ironic.tests.unit.objects import utils +from ironic.tests.unit import stubs CONF = cfg.CONF CLIENT_ID1 = '20:00:55:04:01:fe:80:00:00:00:00:00:00:00:02:c9:02:00:23:13:92' @@ -53,8 +54,9 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): self.context, node_id=self.node.id, address='52:54:00:cf:2d:32', extra={'vif_port_id': uuidutils.generate_uuid()}) - self.neutron_port = {'id': '132f871f-eaec-4fed-9475-0d54465e0f00', - 'mac_address': '52:54:00:cf:2d:32'} + self.neutron_port = stubs.FakeNeutronPort( + id='132f871f-eaec-4fed-9475-0d54465e0f00', + mac_address='52:54:00:cf:2d:32') @mock.patch('%s.vif_list' % VIFMIXINPATH, autospec=True) def test_vif_list(self, mock_vif_list): @@ -171,7 +173,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): validate_mock): self.port.internal_info = {'provisioning_vif_port_id': 'vif-port-id'} self.port.save() - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.id) as task: self.interface.add_provisioning_network(task) rollback_mock.assert_called_once_with( @@ -183,7 +185,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): CONF.neutron.provisioning_network, 'provisioning network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['provisioning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -194,7 +196,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): rollback_mock, validate_mock): self.port.internal_info = {'provisioning_vif_port_id': 'vif-port-id'} self.port.save() - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} # Make sure that changing the network UUID works for provisioning_network_uuid in [ '3aea0de6-4b92-44da-9aa0-52d134c83fdf', @@ -215,7 +217,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): provisioning_network_uuid, 'provisioning network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['provisioning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -230,7 +232,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): self.config(provisioning_network_security_groups=sg_ids, group='neutron') - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.id) as task: self.interface.add_provisioning_network(task) rollback_mock.assert_called_once_with( @@ -240,7 +242,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): security_groups=( CONF.neutron.provisioning_network_security_groups)) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['provisioning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -290,7 +292,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_cleaning_network(self, add_ports_mock, rollback_mock, validate_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.id) as task: res = self.interface.add_cleaning_network(task) rollback_mock.assert_called_once_with( @@ -300,7 +302,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): CONF.neutron.cleaning_network, 'cleaning network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['cleaning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -309,7 +311,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_cleaning_network_from_node(self, add_ports_mock, rollback_mock, validate_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} # Make sure that changing the network UUID works for cleaning_network_uuid in ['3aea0de6-4b92-44da-9aa0-52d134c83fdf', '438be438-6aae-4fb1-bbcb-613ad7a38286']: @@ -326,7 +328,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): cleaning_network_uuid, 'cleaning network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['cleaning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -334,7 +336,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'rollback_ports', autospec=True) @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_cleaning_network_with_sg(self, add_ports_mock, rollback_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} sg_ids = [] for i in range(2): sg_ids.append(uuidutils.generate_uuid()) @@ -348,7 +350,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): task, CONF.neutron.cleaning_network) self.assertEqual(res, add_ports_mock.return_value) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['cleaning_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -485,7 +487,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'rollback_ports', autospec=True) @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_rescuing_network_with_sg(self, add_ports_mock, rollback_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} sg_ids = [] for i in range(2): sg_ids.append(uuidutils.generate_uuid()) @@ -499,7 +501,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): task, CONF.neutron.rescuing_network) self.assertEqual(add_ports_mock.return_value, res) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['rescuing_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -583,21 +585,20 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron, 'LOG', autospec=True) def test_configure_tenant_networks_multiple_ports_one_vif_id( self, log_mock, client_mock, update_mock, wait_agent_mock): - expected_body = { - 'port': { - 'binding:vnic_type': 'baremetal', - 'binding:host_id': self.node.uuid, - 'binding:profile': {'local_link_information': - [self.port.local_link_connection]}, - 'mac_address': '52:54:00:cf:2d:32' - } + expected_attrs = { + 'binding:vnic_type': 'baremetal', + 'binding:host_id': self.node.uuid, + 'binding:profile': { + 'local_link_information': [self.port.local_link_connection] + }, + 'mac_address': '52:54:00:cf:2d:32' } with task_manager.acquire(self.context, self.node.id) as task: self.interface.configure_tenant_networks(task) client_mock.assert_called_once_with(context=task.context) update_mock.assert_called_once_with(self.context, self.port.extra['vif_port_id'], - expected_body) + expected_attrs) @mock.patch.object(neutron_common, 'wait_for_host_agent', autospec=True) @mock.patch.object(neutron_common, 'update_neutron_port', autospec=True) @@ -605,8 +606,8 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): def test_configure_tenant_networks_update_fail(self, client_mock, update_mock, wait_agent_mock): - update_mock.side_effect = neutron_exceptions.ConnectionFailed( - reason='meow') + update_mock.side_effect = openstack_exc.OpenStackCloudException( + message='meow') with task_manager.acquire(self.context, self.node.id) as task: self.assertRaisesRegex( exception.NetworkError, 'Could not add', @@ -645,27 +646,23 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): port.extra = extra port.save() - expected_body = { - 'port': { - 'binding:vnic_type': 'baremetal', - 'binding:host_id': self.node.uuid, - } - } - port1_body = copy.deepcopy(expected_body) - port1_body['port']['binding:profile'] = { + expected_attrs = {'binding:vnic_type': 'baremetal', + 'binding:host_id': self.node.uuid} + port1_attrs = copy.deepcopy(expected_attrs) + port1_attrs['binding:profile'] = { 'local_link_information': [self.port.local_link_connection] } - port1_body['port']['mac_address'] = '52:54:00:cf:2d:32' - port2_body = copy.deepcopy(expected_body) - port2_body['port']['binding:profile'] = { + port1_attrs['mac_address'] = '52:54:00:cf:2d:32' + port2_attrs = copy.deepcopy(expected_attrs) + port2_attrs['binding:profile'] = { 'local_link_information': [second_port.local_link_connection] } - port2_body['port']['mac_address'] = '52:54:00:cf:2d:33' + port2_attrs['mac_address'] = '52:54:00:cf:2d:33' if is_client_id: - port1_body['port']['extra_dhcp_opts'] = ( - [{'opt_name': '61', 'opt_value': client_ids[0]}]) - port2_body['port']['extra_dhcp_opts'] = ( - [{'opt_name': '61', 'opt_value': client_ids[1]}]) + port1_attrs['extra_dhcp_opts'] = [{'opt_name': '61', + 'opt_value': client_ids[0]}] + port2_attrs['extra_dhcp_opts'] = [{'opt_name': '61', + 'opt_value': client_ids[1]}] with task_manager.acquire(self.context, self.node.id) as task: self.interface.configure_tenant_networks(task) client_mock.assert_called_once_with(context=task.context) @@ -676,8 +673,8 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): portid1 = self.port.extra['vif_port_id'] portid2 = second_port.extra['vif_port_id'] update_mock.assert_has_calls( - [mock.call(self.context, portid1, port1_body), - mock.call(self.context, portid2, port2_body)], + [mock.call(self.context, portid1, port1_attrs), + mock.call(self.context, portid2, port2_attrs)], any_order=True ) @@ -729,24 +726,20 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): ) local_group_info = {'a': 'b'} glgi_mock.return_value = local_group_info - expected_body = { - 'port': { - 'binding:vnic_type': 'baremetal', - 'binding:host_id': self.node.uuid, - } + expected_attrs = {'binding:vnic_type': 'baremetal', + 'binding:host_id': self.node.uuid} + call1_attrs = copy.deepcopy(expected_attrs) + call1_attrs['binding:profile'] = { + 'local_link_information': [self.port.local_link_connection] } - call1_body = copy.deepcopy(expected_body) - call1_body['port']['binding:profile'] = { - 'local_link_information': [self.port.local_link_connection], - } - call1_body['port']['mac_address'] = '52:54:00:cf:2d:32' - call2_body = copy.deepcopy(expected_body) - call2_body['port']['binding:profile'] = { + call1_attrs['mac_address'] = '52:54:00:cf:2d:32' + call2_attrs = copy.deepcopy(expected_attrs) + call2_attrs['binding:profile'] = { 'local_link_information': [port1.local_link_connection, port2.local_link_connection], 'local_group_information': local_group_info } - call2_body['port']['mac_address'] = 'ff:54:00:cf:2d:32' + call2_attrs['mac_address'] = 'ff:54:00:cf:2d:32' with task_manager.acquire(self.context, self.node.id) as task: # Override task.portgroups here, to have ability to check # that mocked get_local_group_information was called with @@ -757,9 +750,9 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): glgi_mock.assert_called_once_with(task, pg) update_mock.assert_has_calls( [mock.call(self.context, self.port.extra['vif_port_id'], - call1_body), + call1_attrs), mock.call(self.context, pg.extra['vif_port_id'], - call2_body)] + call2_attrs)] ) def test_need_power_on_true(self): @@ -778,7 +771,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_inspection_network(self, add_ports_mock, rollback_mock, validate_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} with task_manager.acquire(self.context, self.node.id) as task: res = self.interface.add_inspection_network(task) rollback_mock.assert_called_once_with( @@ -788,7 +781,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): CONF.neutron.inspection_network, 'inspection network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['inspection_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -797,7 +790,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_inspection_network_from_node(self, add_ports_mock, rollback_mock, validate_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} # Make sure that changing the network UUID works for inspection_network_uuid in [ '3aea0de6-4b92-44da-9aa0-52d134c83fdf', @@ -815,7 +808,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): inspection_network_uuid, 'inspection network', context=task.context) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['inspection_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', @@ -824,7 +817,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): @mock.patch.object(neutron_common, 'add_ports_to_network', autospec=True) def test_add_inspection_network_with_sg(self, add_ports_mock, rollback_mock): - add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']} + add_ports_mock.return_value = {self.port.uuid: self.neutron_port.id} sg_ids = [] for i in range(2): sg_ids.append(uuidutils.generate_uuid()) @@ -839,7 +832,7 @@ class NeutronInterfaceTestCase(db_base.DbTestCase): task, CONF.neutron.inspection_network) self.assertEqual(res, add_ports_mock.return_value) self.port.refresh() - self.assertEqual(self.neutron_port['id'], + self.assertEqual(self.neutron_port.id, self.port.internal_info['inspection_vif_port_id']) @mock.patch.object(neutron_common, 'validate_network', diff --git a/ironic/tests/unit/stubs.py b/ironic/tests/unit/stubs.py index eeb9483b45..f048b916aa 100644 --- a/ironic/tests/unit/stubs.py +++ b/ironic/tests/unit/stubs.py @@ -76,3 +76,185 @@ class FakeImage(dict): self[key] = value else: raise AttributeError(key) + + +class FakeNeutronPort(dict): + def __init__(self, **attrs): + PORT_ATTRS = ['admin_state_up', + 'allowed_address_pairs', + 'binding:host_id', + 'binding:profile', + 'binding:vif_details', + 'binding:vif_type', + 'binding:vnic_type', + 'data_plane_status', + 'description', + 'device_id', + 'device_owner', + 'dns_assignment', + 'dns_domain', + 'dns_name', + 'extra_dhcp_opts', + 'fixed_ips', + 'id', + 'mac_address', + 'name', 'network_id', + 'port_security_enabled', + 'security_group_ids', + 'status', + 'tenant_id', + 'qos_network_policy_id', + 'qos_policy_id', + 'tags', + 'uplink_status_propagation'] + + raw = dict.fromkeys(PORT_ATTRS) + raw.update(attrs) + super(FakeNeutronPort, self).__init__(raw) + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + def __setattr__(self, key, value): + if key in self: + self[key] = value + else: + raise AttributeError(key) + + +class FakeNeutronSubnet(dict): + def __init__(self, **attrs): + SUBNET_ATTRS = ['id', + 'name', + 'network_id', + 'cidr', + 'tenant_id', + 'enable_dhcp', + 'dns_nameservers', + 'allocation_pools', + 'host_routes', + 'ip_version', + 'gateway_ip', + 'ipv6_address_mode', + 'ipv6_ra_mode', + 'subnetpool_id'] + + raw = dict.fromkeys(SUBNET_ATTRS) + raw.update(attrs) + super(FakeNeutronSubnet, self).__init__(raw) + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + def __setattr__(self, key, value): + if key in self: + self[key] = value + else: + raise AttributeError(key) + + +class FakeNeutronNetwork(dict): + def __init__(self, **attrs): + NETWORK_ATTRS = ['id', + 'name', + 'status', + 'tenant_id', + 'admin_state_up', + 'segments', + 'shared', + 'subnets', + 'provider:network_type', + 'provider:physical_network', + 'provider:segmentation_id', + 'router:external', + 'availability_zones', + 'availability_zone_hints', + 'is_default'] + + raw = dict.fromkeys(NETWORK_ATTRS) + raw.update(attrs) + raw.update({ + 'provider_physical_network': attrs.get( + 'provider:physical_network', None)}) + super(FakeNeutronNetwork, self).__init__(raw) + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + def __setattr__(self, key, value): + if key in self: + self[key] = value + else: + raise AttributeError(key) + + +class FakeNeutronAgent(dict): + def __init__(self, **attrs): + AGENT_ATTRS = ['admin_state_up', + 'agents', + 'agent_type', + 'alive', + 'availability_zone', + 'binary', + 'configurations', + 'created_at', + 'description', + 'heartbeat_timestamp', + 'host', + 'id', + 'resources_synced', + 'started_at', + 'topic'] + + raw = dict.fromkeys(AGENT_ATTRS) + raw.update(attrs) + raw.update({'is_alive': attrs.get('alive', False)}) + super(FakeNeutronAgent, self).__init__(raw) + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + def __setattr__(self, key, value): + if key in self: + self[key] = value + else: + raise AttributeError(key) + + +class FakeNeutronSecurityGroup(dict): + def __init__(self, **attrs): + SECURITY_GROUP_ATTRS = ['id', + 'name', + 'description', + 'stateful', + 'project_id', + 'tenant_id', + 'security_group_rules'] + + raw = dict.fromkeys(SECURITY_GROUP_ATTRS) + raw.update(attrs) + super(FakeNeutronSecurityGroup, self).__init__(raw) + + def __getattr__(self, key): + try: + return self[key] + except KeyError: + raise AttributeError(key) + + def __setattr__(self, key, value): + if key in self: + self[key] = value + else: + raise AttributeError(key) diff --git a/lower-constraints.txt b/lower-constraints.txt index 182fdf50ca..9b9cb8b4f0 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -56,7 +56,7 @@ msgpack-python==0.5.6 munch==2.5.0 netaddr==0.7.19 netifaces==0.10.9 -openstacksdk==0.37.0 +openstacksdk==0.48.0 os-client-config==2.1.0 os-service-types==1.7.0 os-traits==0.4.0 @@ -106,7 +106,6 @@ python-editor==1.0.4 python-glanceclient==2.8.0 python-keystoneclient==4.0.0 python-mimeparse==1.6.0 -python-neutronclient==6.7.0 python-subunit==1.4.0 python-swiftclient==3.2.0 pytz==2013.6 diff --git a/releasenotes/notes/replace-neutronclient-with-openstacksdk-20c06d9d0316c2df.yaml b/releasenotes/notes/replace-neutronclient-with-openstacksdk-20c06d9d0316c2df.yaml new file mode 100644 index 0000000000..762ba42f27 --- /dev/null +++ b/releasenotes/notes/replace-neutronclient-with-openstacksdk-20c06d9d0316c2df.yaml @@ -0,0 +1,10 @@ +--- +deprecations: + - | + With the switch from neutronclient to openstacksdk the ``[neutron]/retries`` + option has been deprecated, use ``[neutron]/status_code_retries`` and + ``[neutron]/status_code_retry_delay`` instead. +other: + - | + Communication with neutron is now using openstacksdk, removing the + dependency on neutronclient. diff --git a/requirements.txt b/requirements.txt index 9f7593b3c0..0dfe0bbd74 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,6 @@ automaton>=1.9.0 # Apache-2.0 eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT WebOb>=1.7.1 # MIT python-cinderclient!=4.0.0,>=3.3.0 # Apache-2.0 -python-neutronclient>=6.7.0 # Apache-2.0 python-glanceclient>=2.8.0 # Apache-2.0 keystoneauth1>=4.2.0 # Apache-2.0 ironic-lib>=4.3.0 # Apache-2.0 @@ -43,4 +42,4 @@ jsonschema>=3.2.0 # MIT psutil>=3.2.2 # BSD futurist>=1.2.0 # Apache-2.0 tooz>=2.7.0 # Apache-2.0 -openstacksdk>=0.37.0 # Apache-2.0 +openstacksdk>=0.48.0 # Apache-2.0