diff --git a/vmware_nsx/common/exceptions.py b/vmware_nsx/common/exceptions.py index a12fae3838..7554ecdbd9 100644 --- a/vmware_nsx/common/exceptions.py +++ b/vmware_nsx/common/exceptions.py @@ -83,11 +83,6 @@ class ServiceOverQuota(n_exc.Conflict): message = _("Quota exceeded for NSX resource %(overs)s: %(err_msg)s") -class ServiceClusterUnavailable(NsxPluginException): - message = _("Service cluster: '%(cluster_id)s' is unavailable. Please, " - "check NSX setup and/or configuration") - - class PortConfigurationError(NsxPluginException): message = _("An error occurred while connecting LSN %(lsn_id)s " "and network %(net_id)s via port %(port_id)s") @@ -127,27 +122,6 @@ class NoRouterAvailable(n_exc.ResourceExhausted): "No tenant router is available for allocation.") -class ManagerError(NsxPluginException): - message = _("Unexpected error from backend manager (%(manager)s) " - "for %(operation)s %(details)s") - - def __init__(self, **kwargs): - kwargs['details'] = (': %s' % kwargs['details'] - if 'details' in kwargs - else '') - super(ManagerError, self).__init__(**kwargs) - self.msg = self.message % kwargs - - -class ResourceNotFound(ManagerError): - message = _("Resource could not be found on backend (%(manager)s) for " - "%(operation)s") - - -class StaleRevision(ManagerError): - pass - - class NsxL2GWConnectionMappingNotFound(n_exc.NotFound): message = _('Unable to find mapping for L2 gateway connection: %(conn)s') @@ -165,8 +139,7 @@ class InvalidIPAddress(n_exc.InvalidInput): class SecurityGroupMaximumCapacityReached(NsxPluginException): - message = _("Security Group %(sg_id)s has reached its maximum capacity, " - "no more ports can be associated with this security-group.") + pass class NsxResourceNotFound(n_exc.NotFound): diff --git a/vmware_nsx/nsxlib/v3/__init__.py b/vmware_nsx/nsxlib/v3/__init__.py index dbda36fd02..173eb14f79 100644 --- a/vmware_nsx/nsxlib/v3/__init__.py +++ b/vmware_nsx/nsxlib/v3/__init__.py @@ -1,4 +1,4 @@ -# Copyright 2015 OpenStack Foundation +# Copyright 2016 OpenStack Foundation # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,388 +13,408 @@ # License for the specific language governing permissions and limitations # under the License. -from oslo_config import cfg from oslo_log import log from vmware_nsx._i18n import _, _LW -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils from vmware_nsx.nsxlib.v3 import client +from vmware_nsx.nsxlib.v3 import cluster +from vmware_nsx.nsxlib.v3 import dfw_api +from vmware_nsx.nsxlib.v3 import exceptions +from vmware_nsx.nsxlib.v3 import security LOG = log.getLogger(__name__) - -def get_version(): - node = client.get_resource("node") - version = node.get('node_version') - return version +# Max amount of time to try a request +DEFAULT_MAX_ATTEMPTS = 3 -def get_edge_cluster(edge_cluster_uuid): - resource = "edge-clusters/%s" % edge_cluster_uuid - return client.get_resource(resource) +class NsxLib(dfw_api.DfwApi, security.Security): + MAX_ATTEMPTS = DEFAULT_MAX_ATTEMPTS -@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision) -def update_resource_with_retry(resource, payload): - revised_payload = client.get_resource(resource) - for key_name in payload.keys(): - revised_payload[key_name] = payload[key_name] - return client.update_resource(resource, revised_payload) + def __init__(self, + username=None, + password=None, + retries=None, + insecure=None, + ca_file=None, + concurrent_connections=None, + http_timeout=None, + http_read_timeout=None, + conn_idle_timeout=None, + http_provider=None, + max_attempts=DEFAULT_MAX_ATTEMPTS): + self.max_attempts = max_attempts + self.cluster = cluster.NSXClusteredAPI( + username=username, password=password, + retries=retries, insecure=insecure, + ca_file=ca_file, + concurrent_connections=concurrent_connections, + http_timeout=http_timeout, + http_read_timeout=http_read_timeout, + conn_idle_timeout=conn_idle_timeout, + http_provider=http_provider) -def delete_resource_by_values(resource, skip_not_found=True, **kwargs): - resources_get = client.get_resource(resource) - matched_num = 0 - for res in resources_get['results']: - if utils.dict_match(kwargs, res): - LOG.debug("Deleting %s from resource %s", res, resource) - delete_resource = resource + "/" + str(res['id']) - client.delete_resource(delete_resource) - matched_num = matched_num + 1 - if matched_num == 0: - if skip_not_found: - LOG.warning(_LW("No resource in %(res)s matched for values: " - "%(values)s"), {'res': resource, + self.client = client.NSX3Client(self.cluster) + super(NsxLib, self).__init__() + + def get_version(self): + node = self.client.get("node") + version = node.get('node_version') + return version + + def get_edge_cluster(self, edge_cluster_uuid): + resource = "edge-clusters/%s" % edge_cluster_uuid + return self.client.get(resource) + + @utils.retry_upon_exception_nsxv3(exceptions.StaleRevision) + def update_resource_with_retry(self, resource, payload): + revised_payload = self.client.get(resource) + for key_name in payload.keys(): + revised_payload[key_name] = payload[key_name] + return self.client.update(resource, revised_payload) + + def delete_resource_by_values(self, resource, + skip_not_found=True, **kwargs): + resources_get = self.client.get(resource) + matched_num = 0 + for res in resources_get['results']: + if utils.dict_match(kwargs, res): + LOG.debug("Deleting %s from resource %s", res, resource) + delete_resource = resource + "/" + str(res['id']) + self.client.delete(delete_resource) + matched_num = matched_num + 1 + if matched_num == 0: + if skip_not_found: + LOG.warning(_LW("No resource in %(res)s matched for values: " + "%(values)s"), {'res': resource, + 'values': kwargs}) + else: + err_msg = (_("No resource in %(res)s matched for values: " + "%(values)s") % {'res': resource, + 'values': kwargs}) + raise exceptions.ResourceNotFound( + manager=client._get_nsx_managers_from_conf(), + operation=err_msg) + elif matched_num > 1: + LOG.warning(_LW("%(num)s resources in %(res)s matched for values: " + "%(values)s"), {'num': matched_num, + 'res': resource, 'values': kwargs}) - else: - err_msg = (_("No resource in %(res)s matched for values: " - "%(values)s") % {'res': resource, - 'values': kwargs}) - raise nsx_exc.ResourceNotFound( - manager=client._get_nsx_managers_from_conf(), - operation=err_msg) - elif matched_num > 1: - LOG.warning(_LW("%(num)s resources in %(res)s matched for values: " - "%(values)s"), {'num': matched_num, - 'res': resource, - 'values': kwargs}) + def create_logical_switch(self, display_name, transport_zone_id, tags, + replication_mode=nsx_constants.MTEP, + admin_state=True, vlan_id=None): + # TODO(salv-orlando): Validate Replication mode and admin_state + # NOTE: These checks might be moved to the API client library if one + # that performs such checks in the client is available -def create_logical_switch(display_name, transport_zone_id, tags, - replication_mode=nsx_constants.MTEP, - admin_state=True, vlan_id=None): - # TODO(salv-orlando): Validate Replication mode and admin_state - # NOTE: These checks might be moved to the API client library if one that - # performs such checks in the client is available + resource = 'logical-switches' + body = {'transport_zone_id': transport_zone_id, + 'replication_mode': replication_mode, + 'display_name': display_name, + 'tags': tags} - resource = 'logical-switches' - body = {'transport_zone_id': transport_zone_id, - 'replication_mode': replication_mode, - 'display_name': display_name, - 'tags': tags} - - if admin_state: - body['admin_state'] = nsx_constants.ADMIN_STATE_UP - else: - body['admin_state'] = nsx_constants.ADMIN_STATE_DOWN - - if vlan_id: - body['vlan'] = vlan_id - - return client.create_resource(resource, body) - - -@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision, - max_attempts=cfg.CONF.nsx_v3.retries) -def delete_logical_switch(lswitch_id): - resource = 'logical-switches/%s?detach=true&cascade=true' % lswitch_id - client.delete_resource(resource) - - -def get_logical_switch(logical_switch_id): - resource = "logical-switches/%s" % logical_switch_id - return client.get_resource(resource) - - -@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision, - max_attempts=cfg.CONF.nsx_v3.retries) -def update_logical_switch(lswitch_id, name=None, admin_state=None, tags=None): - resource = "logical-switches/%s" % lswitch_id - lswitch = get_logical_switch(lswitch_id) - if name is not None: - lswitch['display_name'] = name - if admin_state is not None: if admin_state: - lswitch['admin_state'] = nsx_constants.ADMIN_STATE_UP + body['admin_state'] = nsx_constants.ADMIN_STATE_UP else: - lswitch['admin_state'] = nsx_constants.ADMIN_STATE_DOWN - if tags is not None: - lswitch['tags'] = tags - return client.update_resource(resource, lswitch) - - -def add_nat_rule(logical_router_id, action, translated_network, - source_net=None, dest_net=None, - enabled=True, rule_priority=None): - resource = 'logical-routers/%s/nat/rules' % logical_router_id - body = {'action': action, - 'enabled': enabled, - 'translated_network': translated_network} - if source_net: - body['match_source_network'] = source_net - if dest_net: - body['match_destination_network'] = dest_net - if rule_priority: - body['rule_priority'] = rule_priority - return client.create_resource(resource, body) - - -def add_static_route(logical_router_id, dest_cidr, nexthop): - resource = 'logical-routers/%s/routing/static-routes' % logical_router_id - body = {} - if dest_cidr: - body['network'] = dest_cidr - if nexthop: - body['next_hops'] = [{"ip_address": nexthop}] - return client.create_resource(resource, body) - - -def delete_static_route(logical_router_id, static_route_id): - resource = 'logical-routers/%s/routing/static-routes/%s' % ( - logical_router_id, static_route_id) - client.delete_resource(resource) - - -def delete_static_route_by_values(logical_router_id, - dest_cidr=None, nexthop=None): - resource = 'logical-routers/%s/routing/static-routes' % logical_router_id - kwargs = {} - if dest_cidr: - kwargs['network'] = dest_cidr - if nexthop: - kwargs['next_hops'] = [{"ip_address": nexthop}] - return delete_resource_by_values(resource, **kwargs) - - -def delete_nat_rule(logical_router_id, nat_rule_id): - resource = 'logical-routers/%s/nat/rules/%s' % (logical_router_id, - nat_rule_id) - client.delete_resource(resource) - - -def delete_nat_rule_by_values(logical_router_id, **kwargs): - resource = 'logical-routers/%s/nat/rules' % logical_router_id - return delete_resource_by_values(resource, **kwargs) - - -def update_logical_router_advertisement(logical_router_id, **kwargs): - resource = 'logical-routers/%s/routing/advertisement' % logical_router_id - return update_resource_with_retry(resource, kwargs) - - -def _build_qos_switching_profile_args(tags, name=None, description=None): - body = {"resource_type": "QosSwitchingProfile", - "tags": tags} - return _update_qos_switching_profile_args( - body, name=name, description=description) - - -def _update_qos_switching_profile_args(body, name=None, description=None): - if name: - body["display_name"] = name - if description: - body["description"] = description - return body - - -def _enable_shaping_in_args(body, burst_size=None, peak_bandwidth=None, - average_bandwidth=None): - for shaper in body["shaper_configuration"]: - # Neutron currently supports only shaping of Egress traffic - if shaper["resource_type"] == "EgressRateShaper": - shaper["enabled"] = True - if burst_size: - shaper["burst_size_bytes"] = burst_size - if peak_bandwidth: - shaper["peak_bandwidth_mbps"] = peak_bandwidth - if average_bandwidth: - shaper["average_bandwidth_mbps"] = average_bandwidth - break - - return body - - -def _disable_shaping_in_args(body): - for shaper in body["shaper_configuration"]: - # Neutron currently supports only shaping of Egress traffic - if shaper["resource_type"] == "EgressRateShaper": - shaper["enabled"] = False - shaper["burst_size_bytes"] = 0 - shaper["peak_bandwidth_mbps"] = 0 - shaper["average_bandwidth_mbps"] = 0 - break - - return body - - -def _update_dscp_in_args(body, qos_marking, dscp): - body["dscp"] = {} - body["dscp"]["mode"] = qos_marking.upper() - if dscp: - body["dscp"]["priority"] = dscp - - return body - - -def create_qos_switching_profile(tags, name=None, - description=None): - resource = 'switching-profiles' - body = _build_qos_switching_profile_args(tags, name, description) - return client.create_resource(resource, body) - - -def update_qos_switching_profile(profile_id, tags, name=None, - description=None): - resource = 'switching-profiles/%s' % profile_id - # get the current configuration - body = get_qos_switching_profile(profile_id) - # update the relevant fields - body = _update_qos_switching_profile_args(body, name, description) - return update_resource_with_retry(resource, body) - - -def update_qos_switching_profile_shaping(profile_id, shaping_enabled=False, - burst_size=None, peak_bandwidth=None, - average_bandwidth=None, - qos_marking=None, dscp=None): - resource = 'switching-profiles/%s' % profile_id - # get the current configuration - body = get_qos_switching_profile(profile_id) - # update the relevant fields - if shaping_enabled: - body = _enable_shaping_in_args(body, - burst_size=burst_size, - peak_bandwidth=peak_bandwidth, - average_bandwidth=average_bandwidth) - else: - body = _disable_shaping_in_args(body) - body = _update_dscp_in_args(body, qos_marking, dscp) - return update_resource_with_retry(resource, body) - - -def get_qos_switching_profile(profile_id): - resource = 'switching-profiles/%s' % profile_id - return client.get_resource(resource) - - -def delete_qos_switching_profile(profile_id): - resource = 'switching-profiles/%s' % profile_id - client.delete_resource(resource) - - -def create_bridge_endpoint(device_name, seg_id, tags): - """Create a bridge endpoint on the backend. - - Create a bridge endpoint resource on a bridge cluster for the L2 gateway - network connection. - :param device_name: device_name actually refers to the bridge cluster's - UUID. - :param seg_id: integer representing the VLAN segmentation ID. - :param tags: nsx backend specific tags. - """ - resource = 'bridge-endpoints' - body = {'bridge_cluster_id': device_name, - 'tags': tags, - 'vlan': seg_id} - return client.create_resource(resource, body) - - -def delete_bridge_endpoint(bridge_endpoint_id): - """Delete a bridge endpoint on the backend. - - :param bridge_endpoint_id: string representing the UUID of the bridge - endpoint to be deleted. - """ - resource = 'bridge-endpoints/%s' % bridge_endpoint_id - client.delete_resource(resource) - - -def _get_resource_by_name_or_id(name_or_id, resource): - all_results = client.get_resource(resource)['results'] - matched_results = [] - for rs in all_results: - if rs.get('id') == name_or_id: - # Matched by id - must be unique - return name_or_id - - if rs.get('display_name') == name_or_id: - # Matched by name - add to the list to verify it is unique - matched_results.append(rs) - - if len(matched_results) == 0: - err_msg = (_("Could not find %(resource)s %(name)s") % - {'name': name_or_id, 'resource': resource}) - raise nsx_exc.NsxPluginException(err_msg=err_msg) - elif len(matched_results) > 1: - err_msg = (_("Found multiple %(resource)s named %(name)s") % - {'name': name_or_id, 'resource': resource}) - raise nsx_exc.NsxPluginException(err_msg=err_msg) - - return matched_results[0].get('id') - - -def get_transport_zone_id_by_name_or_id(name_or_id): - """Get a transport zone by it's display name or uuid - - Return the transport zone data, or raise an exception if not found or - not unique - """ - - return _get_resource_by_name_or_id(name_or_id, 'transport-zones') - - -def get_logical_router_id_by_name_or_id(name_or_id): - """Get a logical router by it's display name or uuid - - Return the logical router data, or raise an exception if not found or - not unique - """ - - return _get_resource_by_name_or_id(name_or_id, 'logical-routers') - - -def get_bridge_cluster_id_by_name_or_id(name_or_id): - """Get a bridge cluster by it's display name or uuid - - Return the bridge cluster data, or raise an exception if not found or - not unique - """ - - return _get_resource_by_name_or_id(name_or_id, 'bridge-clusters') - - -def create_port_mirror_session(source_ports, dest_ports, direction, - description, name, tags): - """Create a PortMirror Session on the backend. - - :param source_ports: List of UUIDs of the ports whose traffic is to be - mirrored. - :param dest_ports: List of UUIDs of the ports where the mirrored traffic is - to be sent. - :param direction: String representing the direction of traffic to be - mirrored. [INGRESS, EGRESS, BIDIRECTIONAL] - :param description: String representing the description of the session. - :param name: String representing the name of the session. - :param tags: nsx backend specific tags. - """ - - resource = 'mirror-sessions' - body = {'direction': direction, - 'tags': tags, - 'display_name': name, - 'description': description, - 'mirror_sources': source_ports, - 'mirror_destination': dest_ports} - return client.create_resource(resource, body) - - -def delete_port_mirror_session(mirror_session_id): - """Delete a PortMirror session on the backend. - - :param mirror_session_id: string representing the UUID of the port mirror - session to be deleted. - """ - resource = 'mirror-sessions/%s' % mirror_session_id - client.delete_resource(resource) + body['admin_state'] = nsx_constants.ADMIN_STATE_DOWN + + if vlan_id: + body['vlan'] = vlan_id + + return self.client.create(resource, body) + + @utils.retry_upon_exception_nsxv3(exceptions.StaleRevision, + max_attempts=MAX_ATTEMPTS) + def delete_logical_switch(self, lswitch_id): + resource = 'logical-switches/%s?detach=true&cascade=true' % lswitch_id + self.client.delete(resource) + + def get_logical_switch(self, logical_switch_id): + resource = "logical-switches/%s" % logical_switch_id + return self.client.get(resource) + + @utils.retry_upon_exception_nsxv3(exceptions.StaleRevision, + max_attempts=MAX_ATTEMPTS) + def update_logical_switch(self, lswitch_id, name=None, admin_state=None, + tags=None): + resource = "logical-switches/%s" % lswitch_id + lswitch = self.get_logical_switch(lswitch_id) + if name is not None: + lswitch['display_name'] = name + if admin_state is not None: + if admin_state: + lswitch['admin_state'] = nsx_constants.ADMIN_STATE_UP + else: + lswitch['admin_state'] = nsx_constants.ADMIN_STATE_DOWN + if tags is not None: + lswitch['tags'] = tags + return self.client.update(resource, lswitch) + + def add_nat_rule(self, logical_router_id, action, translated_network, + source_net=None, dest_net=None, + enabled=True, rule_priority=None): + resource = 'logical-routers/%s/nat/rules' % logical_router_id + body = {'action': action, + 'enabled': enabled, + 'translated_network': translated_network} + if source_net: + body['match_source_network'] = source_net + if dest_net: + body['match_destination_network'] = dest_net + if rule_priority: + body['rule_priority'] = rule_priority + return self.client.create(resource, body) + + def add_static_route(self, logical_router_id, dest_cidr, nexthop): + resource = ('logical-routers/%s/routing/static-routes' % + logical_router_id) + body = {} + if dest_cidr: + body['network'] = dest_cidr + if nexthop: + body['next_hops'] = [{"ip_address": nexthop}] + return self.client.create(resource, body) + + def delete_static_route(self, logical_router_id, static_route_id): + resource = 'logical-routers/%s/routing/static-routes/%s' % ( + logical_router_id, static_route_id) + self.client.delete(resource) + + def delete_static_route_by_values(self, logical_router_id, + dest_cidr=None, nexthop=None): + resource = ('logical-routers/%s/routing/static-routes' % + logical_router_id) + kwargs = {} + if dest_cidr: + kwargs['network'] = dest_cidr + if nexthop: + kwargs['next_hops'] = [{"ip_address": nexthop}] + return self.delete_resource_by_values(resource, **kwargs) + + def delete_nat_rule(self, logical_router_id, nat_rule_id): + resource = 'logical-routers/%s/nat/rules/%s' % (logical_router_id, + nat_rule_id) + self.client.delete(resource) + + def delete_nat_rule_by_values(self, logical_router_id, **kwargs): + resource = 'logical-routers/%s/nat/rules' % logical_router_id + return self.delete_resource_by_values(resource, **kwargs) + + def update_logical_router_advertisement(self, logical_router_id, **kwargs): + resource = ('logical-routers/%s/routing/advertisement' % + logical_router_id) + return self.update_resource_with_retry(resource, kwargs) + + def _build_qos_switching_profile_args(self, tags, name=None, + description=None): + body = {"resource_type": "QosSwitchingProfile", + "tags": tags} + return self._update_qos_switching_profile_args( + body, name=name, description=description) + + def _update_qos_switching_profile_args(self, body, name=None, + description=None): + if name: + body["display_name"] = name + if description: + body["description"] = description + return body + + def _enable_shaping_in_args(self, body, burst_size=None, + peak_bandwidth=None, average_bandwidth=None): + for shaper in body["shaper_configuration"]: + # Neutron currently supports only shaping of Egress traffic + if shaper["resource_type"] == "EgressRateShaper": + shaper["enabled"] = True + if burst_size: + shaper["burst_size_bytes"] = burst_size + if peak_bandwidth: + shaper["peak_bandwidth_mbps"] = peak_bandwidth + if average_bandwidth: + shaper["average_bandwidth_mbps"] = average_bandwidth + break + + return body + + def _disable_shaping_in_args(self, body): + for shaper in body["shaper_configuration"]: + # Neutron currently supports only shaping of Egress traffic + if shaper["resource_type"] == "EgressRateShaper": + shaper["enabled"] = False + shaper["burst_size_bytes"] = 0 + shaper["peak_bandwidth_mbps"] = 0 + shaper["average_bandwidth_mbps"] = 0 + break + + return body + + def _update_dscp_in_args(self, body, qos_marking, dscp): + body["dscp"] = {} + body["dscp"]["mode"] = qos_marking.upper() + if dscp: + body["dscp"]["priority"] = dscp + + return body + + def create_qos_switching_profile(self, tags, name=None, + description=None): + resource = 'switching-profiles' + body = self._build_qos_switching_profile_args(tags, name, + description) + return self.client.create(resource, body) + + def update_qos_switching_profile(self, profile_id, tags, name=None, + description=None): + resource = 'switching-profiles/%s' % profile_id + # get the current configuration + body = self.get_qos_switching_profile(profile_id) + # update the relevant fields + body = self._update_qos_switching_profile_args(body, name, + description) + return self.update_resource_with_retry(resource, body) + + def update_qos_switching_profile_shaping(self, profile_id, + shaping_enabled=False, + burst_size=None, + peak_bandwidth=None, + average_bandwidth=None, + qos_marking=None, dscp=None): + resource = 'switching-profiles/%s' % profile_id + # get the current configuration + body = self.get_qos_switching_profile(profile_id) + # update the relevant fields + if shaping_enabled: + body = self._enable_shaping_in_args( + body, burst_size=burst_size, + peak_bandwidth=peak_bandwidth, + average_bandwidth=average_bandwidth) + else: + body = self._disable_shaping_in_args(body) + body = self._update_dscp_in_args(body, qos_marking, dscp) + return self.update_resource_with_retry(resource, body) + + def get_qos_switching_profile(self, profile_id): + resource = 'switching-profiles/%s' % profile_id + return self.client.get(resource) + + def delete_qos_switching_profile(self, profile_id): + resource = 'switching-profiles/%s' % profile_id + self.client.delete(resource) + + def create_bridge_endpoint(self, device_name, seg_id, tags): + """Create a bridge endpoint on the backend. + + Create a bridge endpoint resource on a bridge cluster for the L2 + gateway network connection. + :param device_name: device_name actually refers to the bridge cluster's + UUID. + :param seg_id: integer representing the VLAN segmentation ID. + :param tags: nsx backend specific tags. + """ + resource = 'bridge-endpoints' + body = {'bridge_cluster_id': device_name, + 'tags': tags, + 'vlan': seg_id} + return self.client.create(resource, body) + + def delete_bridge_endpoint(self, bridge_endpoint_id): + """Delete a bridge endpoint on the backend. + + :param bridge_endpoint_id: string representing the UUID of the bridge + endpoint to be deleted. + """ + resource = 'bridge-endpoints/%s' % bridge_endpoint_id + self.client.delete(resource) + + def _get_resource_by_name_or_id(self, name_or_id, resource): + all_results = self.client.get(resource)['results'] + matched_results = [] + for rs in all_results: + if rs.get('id') == name_or_id: + # Matched by id - must be unique + return name_or_id + + if rs.get('display_name') == name_or_id: + # Matched by name - add to the list to verify it is unique + matched_results.append(rs) + + if len(matched_results) == 0: + err_msg = (_("Could not find %(resource)s %(name)s") % + {'name': name_or_id, 'resource': resource}) + # XXX improve exception handling... + raise exceptions.ManagerError(details=err_msg) + elif len(matched_results) > 1: + err_msg = (_("Found multiple %(resource)s named %(name)s") % + {'name': name_or_id, 'resource': resource}) + # XXX improve exception handling... + raise exceptions.ManagerError(details=err_msg) + + return matched_results[0].get('id') + + def get_transport_zone_id_by_name_or_id(self, name_or_id): + """Get a transport zone by it's display name or uuid + + Return the transport zone data, or raise an exception if not found or + not unique + """ + + return self._get_resource_by_name_or_id(name_or_id, + 'transport-zones') + + def get_logical_router_id_by_name_or_id(self, name_or_id): + """Get a logical router by it's display name or uuid + + Return the logical router data, or raise an exception if not found or + not unique + """ + + return self._get_resource_by_name_or_id(name_or_id, + 'logical-routers') + + def get_bridge_cluster_id_by_name_or_id(self, name_or_id): + """Get a bridge cluster by it's display name or uuid + + Return the bridge cluster data, or raise an exception if not found or + not unique + """ + + return self._get_resource_by_name_or_id(name_or_id, + 'bridge-clusters') + + def create_port_mirror_session(self, source_ports, dest_ports, direction, + description, name, tags): + """Create a PortMirror Session on the backend. + + :param source_ports: List of UUIDs of the ports whose traffic is to be + mirrored. + :param dest_ports: List of UUIDs of the ports where the mirrored + traffic is to be sent. + :param direction: String representing the direction of traffic to be + mirrored. [INGRESS, EGRESS, BIDIRECTIONAL] + :param description: String representing the description of the session. + :param name: String representing the name of the session. + :param tags: nsx backend specific tags. + """ + + resource = 'mirror-sessions' + body = {'direction': direction, + 'tags': tags, + 'display_name': name, + 'description': description, + 'mirror_sources': source_ports, + 'mirror_destination': dest_ports} + return self.client.create(resource, body) + + def delete_port_mirror_session(self, mirror_session_id): + """Delete a PortMirror session on the backend. + + :param mirror_session_id: string representing the UUID of the port + mirror session to be deleted. + """ + resource = 'mirror-sessions/%s' % mirror_session_id + self.client.delete(resource) diff --git a/vmware_nsx/nsxlib/v3/client.py b/vmware_nsx/nsxlib/v3/client.py index 9f1eb47c34..9e07d9d84d 100644 --- a/vmware_nsx/nsxlib/v3/client.py +++ b/vmware_nsx/nsxlib/v3/client.py @@ -20,12 +20,12 @@ from oslo_config import cfg from oslo_log import log from oslo_serialization import jsonutils from vmware_nsx._i18n import _, _LW -from vmware_nsx.common import exceptions as nsx_exc +from vmware_nsx.nsxlib.v3 import exceptions LOG = log.getLogger(__name__) -ERRORS = {requests.codes.NOT_FOUND: nsx_exc.ResourceNotFound, - requests.codes.PRECONDITION_FAILED: nsx_exc.StaleRevision} +ERRORS = {requests.codes.NOT_FOUND: exceptions.ResourceNotFound, + requests.codes.PRECONDITION_FAILED: exceptions.StaleRevision} class RESTClient(object): @@ -63,8 +63,8 @@ class RESTClient(object): def update(self, uuid, body=None, headers=None): return self.url_put(uuid, body, headers=headers) - def create(self, body=None, headers=None): - return self.url_post('', body, headers=headers) + def create(self, resource='', body=None, headers=None): + return self.url_post(resource, body, headers=headers) def url_list(self, url, headers=None): return self.url_get(url, headers=headers) @@ -93,7 +93,7 @@ class RESTClient(object): 'body': result_msg}) manager_error = ERRORS.get( - result.status_code, nsx_exc.ManagerError) + result.status_code, exceptions.ManagerError) if isinstance(result_msg, dict): result_msg = result_msg.get('error_message', result_msg) raise manager_error( diff --git a/vmware_nsx/nsxlib/v3/cluster.py b/vmware_nsx/nsxlib/v3/cluster.py index 33d6560bcd..beea7f0bba 100644 --- a/vmware_nsx/nsxlib/v3/cluster.py +++ b/vmware_nsx/nsxlib/v3/cluster.py @@ -35,8 +35,9 @@ from oslo_service import loopingcall from requests import adapters from requests import exceptions as requests_exceptions from vmware_nsx._i18n import _, _LI, _LW -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.nsxlib.v3 import client as nsx_client +from vmware_nsx.nsxlib.v3 import exceptions + LOG = log.getLogger(__name__) @@ -117,7 +118,7 @@ class NSXRequestsHTTPProvider(AbstractHTTPProvider): msg = _("No transport zones found " "for '%s'") % endpoint.provider.url LOG.warning(msg) - raise nsx_exc.ResourceNotFound( + raise exceptions.ResourceNotFound( manager=endpoint.provider.url, operation=msg) def new_connection(self, cluster_api, provider): @@ -392,7 +393,7 @@ class ClusteredAPI(object): [str(ep) for ep in self._endpoints.values()]) # all endpoints are DOWN and will have their next # state updated as per _endpoint_keepalive() - raise nsx_exc.ServiceClusterUnavailable( + raise exceptions.ServiceClusterUnavailable( cluster_id=self.cluster_id) if endpoint.pool.free() == 0: diff --git a/vmware_nsx/nsxlib/v3/dfw_api.py b/vmware_nsx/nsxlib/v3/dfw_api.py index ebf20cbe93..bbcf20de70 100644 --- a/vmware_nsx/nsxlib/v3/dfw_api.py +++ b/vmware_nsx/nsxlib/v3/dfw_api.py @@ -19,10 +19,10 @@ NSX-V3 Distributed Firewall """ from oslo_log import log -from vmware_nsx._i18n import _, _LW -from vmware_nsx.common import exceptions as nsx_exc +from vmware_nsx._i18n import _LW from vmware_nsx.common import utils from vmware_nsx.nsxlib.v3 import client as nsxclient +from vmware_nsx.nsxlib.v3 import exceptions LOG = log.getLogger(__name__) @@ -72,223 +72,198 @@ IPV6 = 'IPV6' IPV4_IPV6 = 'IPV4_IPV6' -class NSGroupMemberNotFound(nsx_exc.NsxPluginException): - message = _("Could not find NSGroup %(nsgroup_id)s member %(member_id)s " - "for removal.") +class DfwApi(object): + def get_nsservice(self, resource_type, **properties): + service = {'resource_type': resource_type} + service.update(properties) + return {'service': service} -class NSGroupIsFull(nsx_exc.NsxPluginException): - message = _("NSGroup %(nsgroup_id)s contains has reached its maximum " - "capacity, unable to add additional members.") + def get_nsgroup_port_tag_expression(self, scope, tag): + return {'resource_type': NSGROUP_TAG_EXPRESSION, + 'target_type': LOGICAL_PORT, + 'scope': scope, + 'tag': tag} + def create_nsgroup(self, display_name, description, tags, + membership_criteria=None): + body = {'display_name': display_name, + 'description': description, + 'tags': tags, + 'members': []} + if membership_criteria: + body.update({'membership_criteria': [membership_criteria]}) + return self.client.create('ns-groups', body) -def get_nsservice(resource_type, **properties): - service = {'resource_type': resource_type} - service.update(properties) - return {'service': service} + def list_nsgroups(self): + return self.client.get( + 'ns-groups?populate_references=false').get('results', []) + @utils.retry_upon_exception_nsxv3(exceptions.StaleRevision) + def update_nsgroup(self, nsgroup_id, display_name=None, description=None, + membership_criteria=None, members=None): + nsgroup = self.read_nsgroup(nsgroup_id) + if display_name is not None: + nsgroup['display_name'] = display_name + if description is not None: + nsgroup['description'] = description + if members is not None: + nsgroup['members'] = members + if membership_criteria is not None: + nsgroup['membership_criteria'] = [membership_criteria] + return self.client.update( + 'ns-groups/%s' % nsgroup_id, nsgroup) -def get_nsgroup_port_tag_expression(scope, tag): - return {'resource_type': NSGROUP_TAG_EXPRESSION, - 'target_type': LOGICAL_PORT, - 'scope': scope, - 'tag': tag} + def get_nsgroup_member_expression(self, target_type, target_id): + return {'resource_type': NSGROUP_SIMPLE_EXPRESSION, + 'target_property': 'id', + 'target_type': target_type, + 'op': EQUALS, + 'value': target_id} + @utils.retry_upon_exception_nsxv3(exceptions.ManagerError) + def _update_nsgroup_with_members(self, nsgroup_id, members, action): + members_update = 'ns-groups/%s?action=%s' % (nsgroup_id, action) + return self.client.create(members_update, members) -def create_nsgroup(display_name, description, tags, membership_criteria=None): - body = {'display_name': display_name, - 'description': description, - 'tags': tags, - 'members': []} - if membership_criteria: - body.update({'membership_criteria': [membership_criteria]}) - return nsxclient.create_resource('ns-groups', body) + def add_nsgroup_members(self, nsgroup_id, target_type, target_ids): + members = [] + for target_id in target_ids: + member_expr = self.get_nsgroup_member_expression( + target_type, target_id) + members.append(member_expr) + members = {'members': members} + try: + return self._update_nsgroup_with_members( + nsgroup_id, members, ADD_MEMBERS) + except (exceptions.StaleRevision, exceptions.ResourceNotFound): + raise + except exceptions.ManagerError: + # REVISIT(roeyc): A ManagerError might have been raised for a + # different reason, e.g - NSGroup does not exists. + LOG.warning(_LW("Failed to add %(target_type)s resources " + "(%(target_ids))s to NSGroup %(nsgroup_id)s"), + {'target_type': target_type, + 'target_ids': target_ids, + 'nsgroup_id': nsgroup_id}) + raise exceptions.NSGroupIsFull(nsgroup_id=nsgroup_id) -def list_nsgroups(): - return nsxclient.get_resource( - 'ns-groups?populate_references=false').get('results', []) + def remove_nsgroup_member(self, nsgroup_id, target_type, + target_id, verify=False): + member_expr = self.get_nsgroup_member_expression( + target_type, target_id) + members = {'members': [member_expr]} + try: + return self._update_nsgroup_with_members( + nsgroup_id, members, REMOVE_MEMBERS) + except exceptions.ManagerError: + if verify: + raise exceptions.NSGroupMemberNotFound(member_id=target_id, + nsgroup_id=nsgroup_id) + def read_nsgroup(self, nsgroup_id): + return self.client.get( + 'ns-groups/%s?populate_references=true' % nsgroup_id) -@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision) -def update_nsgroup(nsgroup_id, display_name=None, description=None, - membership_criteria=None, members=None): - nsgroup = read_nsgroup(nsgroup_id) - if display_name is not None: - nsgroup['display_name'] = display_name - if description is not None: - nsgroup['description'] = description - if members is not None: - nsgroup['members'] = members - if membership_criteria is not None: - nsgroup['membership_criteria'] = [membership_criteria] - return nsxclient.update_resource('ns-groups/%s' % nsgroup_id, nsgroup) + def delete_nsgroup(self, nsgroup_id): + try: + return self.client.delete( + 'ns-groups/%s?force=true' % nsgroup_id) + # FIXME(roeyc): Should only except NotFound error. + except Exception: + LOG.debug("NSGroup %s does not exists for delete request.", + nsgroup_id) + def _build_section(self, display_name, description, applied_tos, tags): + return {'display_name': display_name, + 'description': description, + 'stateful': True, + 'section_type': LAYER3, + 'applied_tos': [self.get_nsgroup_reference(t_id) + for t_id in applied_tos], + 'tags': tags} -def get_nsgroup_member_expression(target_type, target_id): - return {'resource_type': NSGROUP_SIMPLE_EXPRESSION, - 'target_property': 'id', - 'target_type': target_type, - 'op': EQUALS, - 'value': target_id} + def create_empty_section(self, display_name, description, applied_tos, + tags, operation=INSERT_BOTTOM, + other_section=None): + resource = 'firewall/sections?operation=%s' % operation + body = self._build_section(display_name, description, + applied_tos, tags) + if other_section: + resource += '&id=%s' % other_section + return self.client.create(resource, body) + @utils.retry_upon_exception_nsxv3(exceptions.StaleRevision) + def update_section(self, section_id, display_name=None, description=None, + applied_tos=None, rules=None): + resource = 'firewall/sections/%s' % section_id + section = self.read_section(section_id) -@utils.retry_upon_exception_nsxv3(nsx_exc.ManagerError) -def _update_nsgroup_with_members(nsgroup_id, members, action): - members_update = 'ns-groups/%s?action=%s' % (nsgroup_id, action) - return nsxclient.create_resource(members_update, members) + if rules is not None: + resource += '?action=update_with_rules' + section.update({'rules': rules}) + if display_name is not None: + section['display_name'] = display_name + if description is not None: + section['description'] = description + if applied_tos is not None: + section['applied_tos'] = [self.get_nsgroup_reference(nsg_id) + for nsg_id in applied_tos] + if rules is not None: + return nsxclient.create_resource(resource, section) + elif any(p is not None for p in (display_name, description, + applied_tos)): + return self.client.update(resource, section) + def read_section(self, section_id): + resource = 'firewall/sections/%s' % section_id + return self.client.get(resource) -def add_nsgroup_members(nsgroup_id, target_type, target_ids): - members = [] - for target_id in target_ids: - member_expr = get_nsgroup_member_expression(target_type, target_id) - members.append(member_expr) - members = {'members': members} - try: - return _update_nsgroup_with_members(nsgroup_id, members, ADD_MEMBERS) - except (nsx_exc.StaleRevision, nsx_exc.ResourceNotFound): - raise - except nsx_exc.ManagerError: - # REVISIT(roeyc): A ManagerError might have been raised for a - # different reason, e.g - NSGroup does not exists. - LOG.warning(_LW("Failed to add %(target_type)s resources " - "(%(target_ids))s to NSGroup %(nsgroup_id)s"), - {'target_type': target_type, - 'target_ids': target_ids, - 'nsgroup_id': nsgroup_id}) - raise NSGroupIsFull(nsgroup_id=nsgroup_id) + def list_sections(self): + resource = 'firewall/sections' + return self.client.get(resource).get('results', []) + def delete_section(self, section_id): + resource = 'firewall/sections/%s?cascade=true' % section_id + return self.client.delete(resource) -def remove_nsgroup_member(nsgroup_id, target_type, target_id, verify=False): - member_expr = get_nsgroup_member_expression(target_type, target_id) - members = {'members': [member_expr]} - try: - return _update_nsgroup_with_members( - nsgroup_id, members, REMOVE_MEMBERS) - except nsx_exc.ManagerError: - if verify: - raise NSGroupMemberNotFound(member_id=target_id, - nsgroup_id=nsgroup_id) + def get_nsgroup_reference(self, nsgroup_id): + return {'target_id': nsgroup_id, + 'target_type': NSGROUP} + def get_ip_cidr_reference(self, ip_cidr_block, ip_protocol): + target_type = IPV4ADDRESS if ip_protocol == IPV4 else IPV6ADDRESS + return {'target_id': ip_cidr_block, + 'target_type': target_type} -def read_nsgroup(nsgroup_id): - return nsxclient.get_resource( - 'ns-groups/%s?populate_references=true' % nsgroup_id) + def get_firewall_rule_dict(self, display_name, source=None, + destination=None, + direction=IN_OUT, ip_protocol=IPV4_IPV6, + service=None, action=ALLOW, logged=False): + return {'display_name': display_name, + 'sources': [source] if source else [], + 'destinations': [destination] if destination else [], + 'direction': direction, + 'ip_protocol': ip_protocol, + 'services': [service] if service else [], + 'action': action, + 'logged': logged} + def add_rule_in_section(self, rule, section_id): + resource = 'firewall/sections/%s/rules' % section_id + params = '?operation=insert_bottom' + return self.client.create(resource + params, rule) -def delete_nsgroup(nsgroup_id): - try: - return nsxclient.delete_resource('ns-groups/%s?force=true' - % nsgroup_id) - #FIXME(roeyc): Should only except NotFound error. - except Exception: - LOG.debug("NSGroup %s does not exists for delete request.", - nsgroup_id) + def add_rules_in_section(self, rules, section_id): + resource = 'firewall/sections/%s/rules' % section_id + params = '?action=create_multiple&operation=insert_bottom' + return self.client.create(resource + params, {'rules': rules}) + def delete_rule(self, section_id, rule_id): + resource = 'firewall/sections/%s/rules/%s' % (section_id, rule_id) + return self.client.delete(resource) -def _build_section(display_name, description, applied_tos, tags): - return {'display_name': display_name, - 'description': description, - 'stateful': True, - 'section_type': LAYER3, - 'applied_tos': [get_nsgroup_reference(t_id) - for t_id in applied_tos], - 'tags': tags} - - -def create_empty_section(display_name, description, applied_tos, tags, - operation=INSERT_BOTTOM, other_section=None): - resource = 'firewall/sections?operation=%s' % operation - body = _build_section(display_name, description, applied_tos, tags) - if other_section: - resource += '&id=%s' % other_section - return nsxclient.create_resource(resource, body) - - -@utils.retry_upon_exception_nsxv3(nsx_exc.StaleRevision) -def update_section(section_id, display_name=None, description=None, - applied_tos=None, rules=None): - resource = 'firewall/sections/%s' % section_id - section = read_section(section_id) - - if rules is not None: - resource += '?action=update_with_rules' - section.update({'rules': rules}) - if display_name is not None: - section['display_name'] = display_name - if description is not None: - section['description'] = description - if applied_tos is not None: - section['applied_tos'] = [get_nsgroup_reference(nsg_id) - for nsg_id in applied_tos] - if rules is not None: - return nsxclient.create_resource(resource, section) - elif any(p is not None for p in (display_name, description, applied_tos)): - return nsxclient.update_resource(resource, section) - - -def read_section(section_id): - resource = 'firewall/sections/%s' % section_id - return nsxclient.get_resource(resource) - - -def list_sections(): - resource = 'firewall/sections' - return nsxclient.get_resource(resource).get('results', []) - - -def delete_section(section_id): - resource = 'firewall/sections/%s?cascade=true' % section_id - try: - return nsxclient.delete_resource(resource) - #FIXME(roeyc): Should only except NotFound error. - except Exception: - LOG.debug("Firewall section %s does not exists for delete request.", - section_id) - - -def get_nsgroup_reference(nsgroup_id): - return {'target_id': nsgroup_id, - 'target_type': NSGROUP} - - -def get_ip_cidr_reference(ip_cidr_block, ip_protocol): - target_type = IPV4ADDRESS if ip_protocol == IPV4 else IPV6ADDRESS - return {'target_id': ip_cidr_block, - 'target_type': target_type} - - -def get_firewall_rule_dict(display_name, source=None, destination=None, - direction=IN_OUT, ip_protocol=IPV4_IPV6, - service=None, action=ALLOW, logged=False): - return {'display_name': display_name, - 'sources': [source] if source else [], - 'destinations': [destination] if destination else [], - 'direction': direction, - 'ip_protocol': ip_protocol, - 'services': [service] if service else [], - 'action': action, - 'logged': logged} - - -def add_rule_in_section(rule, section_id): - resource = 'firewall/sections/%s/rules' % section_id - params = '?operation=insert_bottom' - return nsxclient.create_resource(resource + params, rule) - - -def add_rules_in_section(rules, section_id): - resource = 'firewall/sections/%s/rules' % section_id - params = '?action=create_multiple&operation=insert_bottom' - return nsxclient.create_resource(resource + params, {'rules': rules}) - - -def delete_rule(section_id, rule_id): - resource = 'firewall/sections/%s/rules/%s' % (section_id, rule_id) - return nsxclient.delete_resource(resource) - - -def get_section_rules(section_id): - resource = 'firewall/sections/%s/rules' % section_id - return nsxclient.get_resource(resource) + def get_section_rules(self, section_id): + resource = 'firewall/sections/%s/rules' % section_id + return self.client.get(resource) diff --git a/vmware_nsx/nsxlib/v3/exceptions.py b/vmware_nsx/nsxlib/v3/exceptions.py new file mode 100644 index 0000000000..8a86ea6915 --- /dev/null +++ b/vmware_nsx/nsxlib/v3/exceptions.py @@ -0,0 +1,97 @@ +# Copyright 2016 VMware, Inc. +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from oslo_utils import excutils +import six + +from vmware_nsx._i18n import _ + + +class NsxLibException(Exception): + """Base NsxLib Exception. + + To correctly use this class, inherit from it and define + a 'message' property. That message will get printf'd + with the keyword arguments provided to the constructor. + """ + message = _("An unknown exception occurred.") + + def __init__(self, **kwargs): + try: + super(NsxLibException, self).__init__(self.message % kwargs) + self.msg = self.message % kwargs + except Exception: + with excutils.save_and_reraise_exception() as ctxt: + if not self.use_fatal_exceptions(): + ctxt.reraise = False + # at least get the core message out if something happened + super(NsxLibException, self).__init__(self.message) + + if six.PY2: + def __unicode__(self): + return unicode(self.msg) + + def __str__(self): + return self.msg + + def use_fatal_exceptions(self): + return False + + +class ManagerError(NsxLibException): + message = _("Unexpected error from backend manager (%(manager)s) " + "for %(operation)s %(details)s") + + def __init__(self, **kwargs): + kwargs['details'] = (': %s' % kwargs['details'] + if 'details' in kwargs + else '') + super(ManagerError, self).__init__(**kwargs) + self.msg = self.message % kwargs + + +class ResourceNotFound(ManagerError): + message = _("Resource could not be found on backend (%(manager)s) for " + "%(operation)s") + + +class StaleRevision(ManagerError): + pass + + +class ServiceClusterUnavailable(ManagerError): + message = _("Service cluster: '%(cluster_id)s' is unavailable. Please, " + "check NSX setup and/or configuration") + + +class NSGroupMemberNotFound(ManagerError): + message = _("Could not find NSGroup %(nsgroup_id)s member %(member_id)s " + "for removal.") + + +class NSGroupIsFull(ManagerError): + message = _("NSGroup %(nsgroup_id)s contains has reached its maximum " + "capacity, unable to add additional members.") + + +class NumberOfNsgroupCriteriaTagsReached(ManagerError): + message = _("Port can be associated with at most %(max_num)s " + "security-groups.") + + +class SecurityGroupMaximumCapacityReached(ManagerError): + message = _("Security Group %(sg_id)s has reached its maximum capacity, " + "no more ports can be associated with this security-group.") diff --git a/vmware_nsx/nsxlib/v3/ns_group_manager.py b/vmware_nsx/nsxlib/v3/ns_group_manager.py new file mode 100644 index 0000000000..a9c11c0d0b --- /dev/null +++ b/vmware_nsx/nsxlib/v3/ns_group_manager.py @@ -0,0 +1,166 @@ +# Copyright 2015 OpenStack Foundation + +# All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +import uuid + +from oslo_config import cfg +from oslo_log import log + +from vmware_nsx._i18n import _, _LW +from vmware_nsx.common import utils +from vmware_nsx.nsxlib import v3 +from vmware_nsx.nsxlib.v3 import dfw_api as firewall +from vmware_nsx.nsxlib.v3 import exceptions + + +LOG = log.getLogger(__name__) + + +class NSGroupManager(object): + """ + This class assists with NSX integration for Neutron security-groups, + Each Neutron security-group is associated with NSX NSGroup object. + Some specific security policies are the same across all security-groups, + i.e - Default drop rule, DHCP. In order to bind these rules to all + NSGroups (security-groups), we create a nested NSGroup (which its members + are also of type NSGroups) to group the other NSGroups and associate it + with these rules. + In practice, one NSGroup (nested) can't contain all the other NSGroups, as + it has strict size limit. To overcome the limited space challange, we + create several nested groups instead of just one, and we evenly distribute + NSGroups (security-groups) between them. + By using an hashing function on the NSGroup uuid we determine in which + group it should be added, and when deleting an NSGroup (security-group) we + use the same procedure to find which nested group it was added. + """ + + NESTED_GROUP_NAME = 'OS Nested Group' + NESTED_GROUP_DESCRIPTION = ('OpenStack NSGroup. Do not delete.') + + def __init__(self, size): + # XXX intergrate this in a better way.. + self.nsx = v3.NsxLib( + username=cfg.CONF.nsx_v3.nsx_api_user, + password=cfg.CONF.nsx_v3.nsx_api_password, + retries=cfg.CONF.nsx_v3.http_retries, + insecure=cfg.CONF.nsx_v3.insecure, + ca_file=cfg.CONF.nsx_v3.ca_file, + concurrent_connections=cfg.CONF.nsx_v3.concurrent_connections, + http_timeout=cfg.CONF.nsx_v3.http_timeout, + http_read_timeout=cfg.CONF.nsx_v3.http_read_timeout, + conn_idle_timeout=cfg.CONF.nsx_v3.conn_idle_timeout, + http_provider=None, + max_attempts=cfg.CONF.nsx_v3.retries) + + self._nested_groups = self._init_nested_groups(size) + self._size = len(self._nested_groups) + + @property + def size(self): + return self._size + + @property + def nested_groups(self): + return self._nested_groups + + def _init_nested_groups(self, requested_size): + # Construct the groups dict - + # {0: ,.., n-1: } + size = requested_size + nested_groups = { + self._get_nested_group_index_from_name(nsgroup): nsgroup['id'] + for nsgroup in self.nsx.list_nsgroups() + if utils.is_internal_resource(nsgroup)} + + if nested_groups: + size = max(requested_size, max(nested_groups) + 1) + if size > requested_size: + LOG.warning(_LW("Lowering the value of " + "nsx_v3:number_of_nested_groups isn't " + "supported, '%s' nested-groups will be used."), + size) + + absent_groups = set(range(size)) - set(nested_groups.keys()) + if absent_groups: + LOG.warning( + _LW("Found %(num_present)s Nested Groups, " + "creating %(num_absent)s more."), + {'num_present': len(nested_groups), + 'num_absent': len(absent_groups)}) + for i in absent_groups: + cont = self._create_nested_group(i) + nested_groups[i] = cont['id'] + + return nested_groups + + def _get_nested_group_index_from_name(self, nested_group): + # The name format is "Nested Group " + return int(nested_group['display_name'].split()[-1]) - 1 + + def _create_nested_group(self, index): + name_prefix = NSGroupManager.NESTED_GROUP_NAME + name = '%s %s' % (name_prefix, index + 1) + description = NSGroupManager.NESTED_GROUP_DESCRIPTION + tags = utils.build_v3_api_version_tag() + return self.nsx.create_nsgroup(name, description, tags) + + def _hash_uuid(self, internal_id): + return hash(uuid.UUID(internal_id)) + + def _suggest_nested_group(self, internal_id): + # Suggests a nested group to use, can be iterated to find alternative + # group in case that previous suggestions did not help. + + index = self._hash_uuid(internal_id) % self.size + yield self.nested_groups[index] + + for i in range(1, self.size): + index = (index + 1) % self.size + yield self.nested_groups[index] + + def add_nsgroup(self, nsgroup_id): + for group in self._suggest_nested_group(nsgroup_id): + try: + LOG.debug("Adding NSGroup %s to nested group %s", + nsgroup_id, group) + self.nsx.add_nsgroup_members(group, + firewall.NSGROUP, + [nsgroup_id]) + break + except exceptions.NSGroupIsFull: + LOG.debug("Nested group %(group_id)s is full, trying the " + "next group..", {'group_id': group}) + else: + raise exceptions.ManagerError( + details=_("Reached the maximum supported amount of " + "security groups.")) + + def remove_nsgroup(self, nsgroup_id): + for group in self._suggest_nested_group(nsgroup_id): + try: + self.nsx.remove_nsgroup_member( + group, firewall.NSGROUP, nsgroup_id, verify=True) + break + except exceptions.NSGroupMemberNotFound: + LOG.warning(_LW("NSGroup %(nsgroup)s was expected to be found " + "in group %(group_id)s, but wasn't. " + "Looking in the next group.."), + {'nsgroup': nsgroup_id, 'group_id': group}) + continue + else: + LOG.warning(_LW("NSGroup %s was marked for removal, but its " + "reference is missing."), nsgroup_id) diff --git a/vmware_nsx/nsxlib/v3/resources.py b/vmware_nsx/nsxlib/v3/resources.py index 74e7a6c18a..270e483d72 100644 --- a/vmware_nsx/nsxlib/v3/resources.py +++ b/vmware_nsx/nsxlib/v3/resources.py @@ -20,10 +20,10 @@ import six from oslo_config import cfg from vmware_nsx._i18n import _ -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils from vmware_nsx.nsxlib.v3 import client +from vmware_nsx.nsxlib.v3 import exceptions SwitchingProfileTypeId = collections.namedtuple( @@ -281,13 +281,13 @@ class LogicalPort(AbstractRESTResource): return self._client.create(body=body) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def delete(self, lport_id): return self._client.url_delete('%s?detach=true' % lport_id) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def update(self, lport_id, vif_uuid, name=None, admin_state=None, @@ -338,7 +338,7 @@ class LogicalRouter(AbstractRESTResource): return self._client.url_delete(lrouter_id) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def update(self, lrouter_id, *args, **kwargs): lrouter = self.get(lrouter_id) @@ -382,10 +382,10 @@ class LogicalRouterPort(AbstractRESTResource): if edge_cluster_member_index: body['edge_cluster_member_index'] = edge_cluster_member_index - return self._client.create(body) + return self._client.create(body=body) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def update(self, logical_port_id, **kwargs): logical_router_port = self.get(logical_port_id) @@ -405,15 +405,15 @@ class LogicalRouterPort(AbstractRESTResource): router_ports = self._client.url_get(resource) result_count = int(router_ports.get('result_count', "0")) if result_count >= 2: - raise nsx_exc.NsxPluginException( - err_msg=_("Can't support more than one logical router ports " + raise exceptions.ManagerError( + details=_("Can't support more than one logical router ports " "on same logical switch %s ") % logical_switch_id) elif result_count == 1: return router_ports['results'][0] else: err_msg = (_("Logical router link port not found on logical " "switch %s") % logical_switch_id) - raise nsx_exc.ResourceNotFound( + raise exceptions.ResourceNotFound( manager=client._get_nsx_managers_from_conf(), operation=err_msg) @@ -435,7 +435,7 @@ class LogicalRouterPort(AbstractRESTResource): for port in logical_router_ports: if port['resource_type'] == nsx_constants.LROUTERPORT_LINKONTIER1: return port - raise nsx_exc.ResourceNotFound( + raise exceptions.ResourceNotFound( manager=client._get_nsx_managers_from_conf(), operation="get router link port") @@ -501,7 +501,7 @@ class LogicalDhcpServer(AbstractRESTResource): return self._client.create(body=body) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def update(self, uuid, dhcp_profile_id=None, server_ip=None, name=None, dns_servers=None, domain_name=None, gateway_ip=None, @@ -529,7 +529,7 @@ class LogicalDhcpServer(AbstractRESTResource): return self._client.url_get(url) @utils.retry_upon_exception_nsxv3( - nsx_exc.StaleRevision, + exceptions.StaleRevision, max_attempts=cfg.CONF.nsx_v3.retries) def update_binding(self, server_uuid, binding_uuid, **kwargs): body = self.get_binding(server_uuid, binding_uuid) diff --git a/vmware_nsx/nsxlib/v3/router.py b/vmware_nsx/nsxlib/v3/router.py index 346f450909..cbfdc65205 100644 --- a/vmware_nsx/nsxlib/v3/router.py +++ b/vmware_nsx/nsxlib/v3/router.py @@ -22,10 +22,9 @@ from neutron_lib import exceptions as n_exc from oslo_log import log from vmware_nsx._i18n import _, _LW -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils -from vmware_nsx.nsxlib import v3 as nsxlib +from vmware_nsx.nsxlib.v3 import exceptions LOG = log.getLogger(__name__) @@ -41,15 +40,16 @@ GW_NAT_PRI = 1000 class RouterLib(object): - def __init__(self, router_client, router_port_client): + def __init__(self, router_client, router_port_client, nsxlib): self._router_client = router_client self._router_port_client = router_port_client + self.nsxlib = nsxlib def validate_tier0(self, tier0_groups_dict, tier0_uuid): err_msg = None try: lrouter = self._router_client.get(tier0_uuid) - except nsx_exc.ResourceNotFound: + except exceptions.ResourceNotFound: err_msg = (_("Tier0 router %s not found at the backend. Either a " "valid UUID must be specified or a default tier0 " "router UUID must be configured in nsx.ini") % @@ -60,7 +60,7 @@ class RouterLib(object): err_msg = _("Failed to get edge cluster uuid from tier0 " "router %s at the backend") % lrouter else: - edge_cluster = nsxlib.get_edge_cluster(edge_cluster_uuid) + edge_cluster = self.nsxlib.get_edge_cluster(edge_cluster_uuid) member_index_list = [member['member_index'] for member in edge_cluster['members']] if len(member_index_list) < MIN_EDGE_NODE_NUM: @@ -101,7 +101,7 @@ class RouterLib(object): try: tier1_link_port = ( self._router_port_client.get_tier1_link_port(tier1_uuid)) - except nsx_exc.ResourceNotFound: + except exceptions.ResourceNotFound: LOG.warning(_LW("Logical router link port for tier1 router: %s " "not found at the backend"), tier1_uuid) return @@ -113,20 +113,20 @@ class RouterLib(object): def update_advertisement(self, logical_router_id, advertise_route_nat, advertise_route_connected, enabled=True): - return nsxlib.update_logical_router_advertisement( + return self.nsxlib.update_logical_router_advertisement( logical_router_id, advertise_nat_routes=advertise_route_nat, advertise_nsx_connected_routes=advertise_route_connected, enabled=enabled) def delete_gw_snat_rule(self, logical_router_id, gw_ip): - return nsxlib.delete_nat_rule_by_values(logical_router_id, - translated_network=gw_ip) + return self.nsxlib.delete_nat_rule_by_values(logical_router_id, + translated_network=gw_ip) def add_gw_snat_rule(self, logical_router_id, gw_ip): - return nsxlib.add_nat_rule(logical_router_id, action="SNAT", - translated_network=gw_ip, - rule_priority=GW_NAT_PRI) + return self.nsxlib.add_nat_rule(logical_router_id, action="SNAT", + translated_network=gw_ip, + rule_priority=GW_NAT_PRI) def update_router_edge_cluster(self, nsx_router_id, edge_cluster_uuid): return self._router_client.update(nsx_router_id, @@ -140,7 +140,7 @@ class RouterLib(object): address_groups): try: port = self._router_port_client.get_by_lswitch_id(ls_id) - except nsx_exc.ResourceNotFound: + except exceptions.ResourceNotFound: return self._router_port_client.create( logical_router_id, display_name, @@ -153,30 +153,31 @@ class RouterLib(object): port['id'], subnets=address_groups) def add_fip_nat_rules(self, logical_router_id, ext_ip, int_ip): - nsxlib.add_nat_rule(logical_router_id, action="SNAT", - translated_network=ext_ip, - source_net=int_ip, - rule_priority=FIP_NAT_PRI) - nsxlib.add_nat_rule(logical_router_id, action="DNAT", - translated_network=int_ip, - dest_net=ext_ip, - rule_priority=FIP_NAT_PRI) + self.nsxlib.add_nat_rule(logical_router_id, action="SNAT", + translated_network=ext_ip, + source_net=int_ip, + rule_priority=FIP_NAT_PRI) + self.nsxlib.add_nat_rule(logical_router_id, action="DNAT", + translated_network=int_ip, + dest_net=ext_ip, + rule_priority=FIP_NAT_PRI) def delete_fip_nat_rules(self, logical_router_id, ext_ip, int_ip): - nsxlib.delete_nat_rule_by_values(logical_router_id, - action="SNAT", - translated_network=ext_ip, - match_source_network=int_ip) - nsxlib.delete_nat_rule_by_values(logical_router_id, - action="DNAT", - translated_network=int_ip, - match_destination_network=ext_ip) + self.nsxlib.delete_nat_rule_by_values(logical_router_id, + action="SNAT", + translated_network=ext_ip, + match_source_network=int_ip) + self.nsxlib.delete_nat_rule_by_values(logical_router_id, + action="DNAT", + translated_network=int_ip, + match_destination_network=ext_ip) def add_static_routes(self, nsx_router_id, route): - return nsxlib.add_static_route(nsx_router_id, route['destination'], - route['nexthop']) + return self.nsxlib.add_static_route(nsx_router_id, + route['destination'], + route['nexthop']) def delete_static_routes(self, nsx_router_id, route): - return nsxlib.delete_static_route_by_values( + return self.nsxlib.delete_static_route_by_values( nsx_router_id, dest_cidr=route['destination'], nexthop=route['nexthop']) diff --git a/vmware_nsx/nsxlib/v3/security.py b/vmware_nsx/nsxlib/v3/security.py index 1d0e0e452a..848f1c8793 100644 --- a/vmware_nsx/nsxlib/v3/security.py +++ b/vmware_nsx/nsxlib/v3/security.py @@ -18,20 +18,18 @@ NSX-V3 Plugin security integration module """ -import uuid - from neutron_lib import constants from oslo_config import cfg from oslo_log import log from oslo_utils import excutils -from vmware_nsx._i18n import _, _LW, _LE -from vmware_nsx.common import exceptions as nsx_exc +from vmware_nsx._i18n import _LE from vmware_nsx.common import utils from vmware_nsx.db import nsx_models from vmware_nsx.extensions import secgroup_rule_local_ip_prefix from vmware_nsx.extensions import securitygrouplogging as sg_logging from vmware_nsx.nsxlib.v3 import dfw_api as firewall +from vmware_nsx.nsxlib.v3 import exceptions LOG = log.getLogger(__name__) @@ -43,402 +41,269 @@ PORT_SG_SCOPE = 'os-security-group' MAX_NSGROUPS_CRITERIA_TAGS = 10 -def _get_l4_protocol_name(protocol_number): - if protocol_number is None: - return - protocol_number = constants.IP_PROTOCOL_MAP.get(protocol_number, - protocol_number) - protocol_number = int(protocol_number) - if protocol_number == 6: - return firewall.TCP - elif protocol_number == 17: - return firewall.UDP - elif protocol_number == 1: - return firewall.ICMPV4 - else: - return protocol_number +# XXX this method should be refactored to pull the common stuff out to +# a security_group utils file. +class Security(object): - -def _get_direction(sg_rule): - return firewall.IN if sg_rule['direction'] == 'ingress' else firewall.OUT - - -def _decide_service(sg_rule): - l4_protocol = _get_l4_protocol_name(sg_rule['protocol']) - direction = _get_direction(sg_rule) - - if l4_protocol in [firewall.TCP, firewall.UDP]: - # If port_range_min is not specified then we assume all ports are - # matched, relying on neutron to perform validation. - source_ports = [] - if sg_rule['port_range_min'] is None: - destination_ports = [] - elif sg_rule['port_range_min'] != sg_rule['port_range_max']: - # NSX API requires a non-empty range (e.g - '22-23') - destination_ports = ['%(port_range_min)s-%(port_range_max)s' - % sg_rule] + def _get_l4_protocol_name(self, protocol_number): + if protocol_number is None: + return + protocol_number = constants.IP_PROTOCOL_MAP.get(protocol_number, + protocol_number) + protocol_number = int(protocol_number) + if protocol_number == 6: + return firewall.TCP + elif protocol_number == 17: + return firewall.UDP + elif protocol_number == 1: + return firewall.ICMPV4 else: - destination_ports = ['%(port_range_min)s' % sg_rule] + return protocol_number - if direction == firewall.OUT: - source_ports, destination_ports = destination_ports, [] + def _get_direction(self, sg_rule): + return ( + firewall.IN if sg_rule['direction'] == 'ingress' else firewall.OUT + ) - return firewall.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, + def _decide_service(self, sg_rule): + l4_protocol = self._get_l4_protocol_name(sg_rule['protocol']) + direction = self._get_direction(sg_rule) + + if l4_protocol in [firewall.TCP, firewall.UDP]: + # If port_range_min is not specified then we assume all ports are + # matched, relying on neutron to perform validation. + source_ports = [] + if sg_rule['port_range_min'] is None: + destination_ports = [] + elif sg_rule['port_range_min'] != sg_rule['port_range_max']: + # NSX API requires a non-empty range (e.g - '22-23') + destination_ports = ['%(port_range_min)s-%(port_range_max)s' + % sg_rule] + else: + destination_ports = ['%(port_range_min)s' % sg_rule] + + if direction == firewall.OUT: + source_ports, destination_ports = destination_ports, [] + + return self.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, l4_protocol=l4_protocol, source_ports=source_ports, destination_ports=destination_ports) - elif l4_protocol == firewall.ICMPV4: - return firewall.get_nsservice(firewall.ICMP_TYPE_NSSERVICE, + elif l4_protocol == firewall.ICMPV4: + return self.get_nsservice(firewall.ICMP_TYPE_NSSERVICE, protocol=l4_protocol, icmp_type=sg_rule['port_range_min'], icmp_code=sg_rule['port_range_max']) - elif l4_protocol is not None: - return firewall.get_nsservice(firewall.IP_PROTOCOL_NSSERVICE, + elif l4_protocol is not None: + return self.get_nsservice(firewall.IP_PROTOCOL_NSSERVICE, protocol_number=l4_protocol) + def _get_fw_rule_from_sg_rule(self, sg_rule, nsgroup_id, rmt_nsgroup_id, + logged, action): + # IPV4 or IPV6 + ip_protocol = sg_rule['ethertype'].upper() + direction = self._get_direction(sg_rule) -def _get_fw_rule_from_sg_rule(sg_rule, nsgroup_id, rmt_nsgroup_id, - logged, action): - # IPV4 or IPV6 - ip_protocol = sg_rule['ethertype'].upper() - direction = _get_direction(sg_rule) + if sg_rule.get(secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX): + local_ip_prefix = self.get_ip_cidr_reference( + sg_rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX], + ip_protocol) + else: + local_ip_prefix = None - if sg_rule.get(secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX): - local_ip_prefix = firewall.get_ip_cidr_reference( - sg_rule[secgroup_rule_local_ip_prefix.LOCAL_IP_PREFIX], - ip_protocol) - else: - local_ip_prefix = None + source = None + local_group = self.get_nsgroup_reference(nsgroup_id) + if sg_rule['remote_ip_prefix'] is not None: + source = self.get_ip_cidr_reference( + sg_rule['remote_ip_prefix'], ip_protocol) + destination = local_ip_prefix or local_group + else: + if rmt_nsgroup_id: + source = self.get_nsgroup_reference(rmt_nsgroup_id) + destination = local_ip_prefix or local_group + if direction == firewall.OUT: + source, destination = destination, source - source = None - local_group = firewall.get_nsgroup_reference(nsgroup_id) - if sg_rule['remote_ip_prefix'] is not None: - source = firewall.get_ip_cidr_reference(sg_rule['remote_ip_prefix'], - ip_protocol) - destination = local_ip_prefix or local_group - else: - if rmt_nsgroup_id: - source = firewall.get_nsgroup_reference(rmt_nsgroup_id) - destination = local_ip_prefix or local_group - if direction == firewall.OUT: - source, destination = destination, source + service = self._decide_service(sg_rule) + name = sg_rule['id'] - service = _decide_service(sg_rule) - name = sg_rule['id'] - - return firewall.get_firewall_rule_dict(name, source, + return self.get_firewall_rule_dict(name, source, destination, direction, ip_protocol, service, action, logged) + def create_firewall_rules(self, context, section_id, nsgroup_id, + logging_enabled, action, security_group_rules): -def create_firewall_rules(context, section_id, nsgroup_id, logging_enabled, - action, security_group_rules): + # 1. translate rules + # 2. insert in section + # 3. save mappings - # 1. translate rules - # 2. insert in section - # 3. save mappings + firewall_rules = [] + for sg_rule in security_group_rules: + remote_nsgroup_id = self._get_remote_nsg_mapping( + context, sg_rule, nsgroup_id) - firewall_rules = [] - for sg_rule in security_group_rules: - remote_nsgroup_id = _get_remote_nsg_mapping( - context, sg_rule, nsgroup_id) + fw_rule = self._get_fw_rule_from_sg_rule( + sg_rule, nsgroup_id, remote_nsgroup_id, + logging_enabled, action) - fw_rule = _get_fw_rule_from_sg_rule( - sg_rule, nsgroup_id, remote_nsgroup_id, logging_enabled, action) + firewall_rules.append(fw_rule) - firewall_rules.append(fw_rule) + return self.add_rules_in_section(firewall_rules, section_id) - return firewall.add_rules_in_section(firewall_rules, section_id) + def _process_firewall_section_rules_logging_for_update(self, section_id, + logging_enabled): + rules = self.get_section_rules(section_id).get('results', []) + update_rules = False + for rule in rules: + if rule['logged'] != logging_enabled: + rule['logged'] = logging_enabled + update_rules = True + return rules if update_rules else None + def set_firewall_rule_logging_for_section(self, section_id, logging): + rules = self._process_firewall_section_rules_logging_for_update( + section_id, logging) + self.update_section(section_id, rules=rules) -def _process_firewall_section_rules_logging_for_update(section_id, - logging_enabled): - rules = firewall.get_section_rules(section_id).get('results', []) - update_rules = False - for rule in rules: - if rule['logged'] != logging_enabled: - rule['logged'] = logging_enabled - update_rules = True - return rules if update_rules else None + def update_security_group_on_backend(self, context, security_group): + nsgroup_id, section_id = self.get_sg_mappings(context.session, + security_group['id']) + name = self.get_nsgroup_name(security_group) + description = security_group['description'] + logging = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic or + security_group[sg_logging.LOGGING]) + rules = self._process_firewall_section_rules_logging_for_update( + section_id, logging) + self.update_nsgroup(nsgroup_id, name, description) + self.update_section(section_id, name, description, rules=rules) + def get_nsgroup_name(self, security_group): + # NOTE(roeyc): We add the security-group id to the NSGroup name, + # for usability purposes. + return '%(name)s - %(id)s' % security_group -def set_firewall_rule_logging_for_section(section_id, logging): - rules = _process_firewall_section_rules_logging_for_update(section_id, - logging) - firewall.update_section(section_id, rules=rules) + def save_sg_rule_mappings(self, session, firewall_rules): + # REVISIT(roeyc): This method should take care db access only. + rules = [(rule['display_name'], rule['id']) for rule in firewall_rules] + with session.begin(subtransactions=True): + for neutron_id, nsx_id in rules: + mapping = nsx_models.NeutronNsxRuleMapping( + neutron_id=neutron_id, nsx_id=nsx_id) + session.add(mapping) + return mapping + # XXX db calls should not be here... + def save_sg_mappings(self, session, sg_id, nsgroup_id, section_id): + with session.begin(subtransactions=True): + session.add( + nsx_models.NeutronNsxFirewallSectionMapping(neutron_id=sg_id, + nsx_id=section_id)) + session.add( + nsx_models.NeutronNsxSecurityGroupMapping(neutron_id=sg_id, + nsx_id=nsgroup_id)) -def update_security_group_on_backend(context, security_group): - nsgroup_id, section_id = get_sg_mappings(context.session, - security_group['id']) - name = get_nsgroup_name(security_group) - description = security_group['description'] - logging = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic or - security_group[sg_logging.LOGGING]) - rules = _process_firewall_section_rules_logging_for_update(section_id, - logging) - firewall.update_nsgroup(nsgroup_id, name, description) - firewall.update_section(section_id, name, description, rules=rules) + # XXX db calls should not be here... + def get_sg_rule_mapping(self, session, rule_id): + rule_mapping = session.query( + nsx_models.NeutronNsxRuleMapping).filter_by( + neutron_id=rule_id).one() + return rule_mapping.nsx_id + # XXX db calls should not be here... + def get_sg_mappings(self, session, sg_id): + nsgroup_mapping = session.query( + nsx_models.NeutronNsxSecurityGroupMapping + ).filter_by(neutron_id=sg_id).one() + section_mapping = session.query( + nsx_models.NeutronNsxFirewallSectionMapping + ).filter_by(neutron_id=sg_id).one() + return nsgroup_mapping.nsx_id, section_mapping.nsx_id -def get_nsgroup_name(security_group): - # NOTE(roeyc): We add the security-group id to the NSGroup name, - # for usability purposes. - return '%(name)s - %(id)s' % security_group + def _get_remote_nsg_mapping(self, context, sg_rule, nsgroup_id): + remote_nsgroup_id = None + remote_group_id = sg_rule.get('remote_group_id') + # skip unnecessary db access when possible + if remote_group_id == sg_rule['security_group_id']: + remote_nsgroup_id = nsgroup_id + elif remote_group_id: + remote_nsgroup_id, s = self.get_sg_mappings(context.session, + remote_group_id) + return remote_nsgroup_id + def get_lport_tags_for_security_groups(self, secgroups): + if len(secgroups) > MAX_NSGROUPS_CRITERIA_TAGS: + raise exceptions.NumberOfNsgroupCriteriaTagsReached( + max_num=MAX_NSGROUPS_CRITERIA_TAGS) + tags = [] + for sg in secgroups: + tags = utils.add_v3_tag(tags, PORT_SG_SCOPE, sg) + if not tags: + # This port shouldn't be associated with any security-group + tags = [{'scope': PORT_SG_SCOPE, 'tag': None}] + return tags -def save_sg_rule_mappings(session, firewall_rules): - # REVISIT(roeyc): This method should take care db access only. - rules = [(rule['display_name'], rule['id']) for rule in firewall_rules] - with session.begin(subtransactions=True): - for neutron_id, nsx_id in rules: - mapping = nsx_models.NeutronNsxRuleMapping( - neutron_id=neutron_id, nsx_id=nsx_id) - session.add(mapping) - return mapping + def update_lport_with_security_groups(self, context, lport_id, + original, updated): + added = set(updated) - set(original) + removed = set(original) - set(updated) + for sg_id in added: + nsgroup_id, s = self.get_sg_mappings(context.session, sg_id) + try: + self.add_nsgroup_members( + nsgroup_id, firewall.LOGICAL_PORT, [lport_id]) + except exceptions.NSGroupIsFull: + for sg_id in added: + nsgroup_id, s = self.get_sg_mappings( + context.session, sg_id) + # NOTE(roeyc): If the port was not added to the nsgroup + # yet, then this request will silently fail. + self.remove_nsgroup_member( + nsgroup_id, firewall.LOGICAL_PORT, lport_id) + raise exceptions.SecurityGroupMaximumCapacityReached( + sg_id=sg_id) + except exceptions.ResourceNotFound: + with excutils.save_and_reraise_exception(): + LOG.error(_LE("NSGroup %s doesn't exists"), nsgroup_id) + for sg_id in removed: + nsgroup_id, s = self.get_sg_mappings(context.session, sg_id) + self.remove_nsgroup_member( + nsgroup_id, firewall.LOGICAL_PORT, lport_id) + def _init_default_section(self, name, description, nested_groups): + fw_sections = self.list_sections() + for section in fw_sections: + if section['display_name'] == name: + break + else: + tags = utils.build_v3_api_version_tag() + section = self.create_empty_section( + name, description, nested_groups, tags) -def save_sg_mappings(session, sg_id, nsgroup_id, section_id): - with session.begin(subtransactions=True): - session.add( - nsx_models.NeutronNsxFirewallSectionMapping(neutron_id=sg_id, - nsx_id=section_id)) - session.add( - nsx_models.NeutronNsxSecurityGroupMapping(neutron_id=sg_id, - nsx_id=nsgroup_id)) - - -def get_sg_rule_mapping(session, rule_id): - rule_mapping = session.query(nsx_models.NeutronNsxRuleMapping).filter_by( - neutron_id=rule_id).one() - return rule_mapping.nsx_id - - -def get_sg_mappings(session, sg_id): - nsgroup_mapping = session.query(nsx_models.NeutronNsxSecurityGroupMapping - ).filter_by(neutron_id=sg_id).one() - section_mapping = session.query(nsx_models.NeutronNsxFirewallSectionMapping - ).filter_by(neutron_id=sg_id).one() - return nsgroup_mapping.nsx_id, section_mapping.nsx_id - - -def _get_remote_nsg_mapping(context, sg_rule, nsgroup_id): - remote_nsgroup_id = None - remote_group_id = sg_rule.get('remote_group_id') - # skip unnecessary db access when possible - if remote_group_id == sg_rule['security_group_id']: - remote_nsgroup_id = nsgroup_id - elif remote_group_id: - remote_nsgroup_id, s = get_sg_mappings(context.session, - remote_group_id) - return remote_nsgroup_id - - -def get_lport_tags_for_security_groups(secgroups): - if len(secgroups) > MAX_NSGROUPS_CRITERIA_TAGS: - raise nsx_exc.NumberOfNsgroupCriteriaTagsReached( - max_num=MAX_NSGROUPS_CRITERIA_TAGS) - tags = [] - for sg in secgroups: - tags = utils.add_v3_tag(tags, PORT_SG_SCOPE, sg) - if not tags: - # This port shouldn't be associated with any security-group - tags = [{'scope': PORT_SG_SCOPE, 'tag': None}] - return tags - - -def update_lport_with_security_groups(context, lport_id, original, updated): - added = set(updated) - set(original) - removed = set(original) - set(updated) - for sg_id in added: - nsgroup_id, s = get_sg_mappings(context.session, sg_id) - try: - firewall.add_nsgroup_members( - nsgroup_id, firewall.LOGICAL_PORT, [lport_id]) - except firewall.NSGroupIsFull: - for sg_id in added: - nsgroup_id, s = get_sg_mappings(context.session, sg_id) - # NOTE(roeyc): If the port was not added to the nsgroup yet, - # then this request will silently fail. - firewall.remove_nsgroup_member( - nsgroup_id, firewall.LOGICAL_PORT, lport_id) - raise nsx_exc.SecurityGroupMaximumCapacityReached(sg_id=sg_id) - except nsx_exc.ResourceNotFound: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("NSGroup %s doesn't exists"), nsgroup_id) - for sg_id in removed: - nsgroup_id, s = get_sg_mappings(context.session, sg_id) - firewall.remove_nsgroup_member( - nsgroup_id, firewall.LOGICAL_PORT, lport_id) - - -def init_nsgroup_manager_and_default_section_rules(): - section_description = ("This section is handled by OpenStack to contain " - "default rules on security-groups.") - - nsgroup_manager = NSGroupManager(cfg.CONF.nsx_v3.number_of_nested_groups) - section_id = _init_default_section( - DEFAULT_SECTION, section_description, - nsgroup_manager.nested_groups.values()) - return nsgroup_manager, section_id - - -def _init_default_section(name, description, nested_groups): - fw_sections = firewall.list_sections() - for section in fw_sections: - if section['display_name'] == name: - break - else: - tags = utils.build_v3_api_version_tag() - section = firewall.create_empty_section( - name, description, nested_groups, tags) - - block_rule = firewall.get_firewall_rule_dict( - 'Block All', action=firewall.DROP, - logged=cfg.CONF.nsx_v3.log_security_groups_blocked_traffic) - # TODO(roeyc): Add additional rules to allow IPV6 NDP. - dhcp_client = firewall.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, + block_rule = self.get_firewall_rule_dict( + 'Block All', action=firewall.DROP, + logged=cfg.CONF.nsx_v3.log_security_groups_blocked_traffic) + # TODO(roeyc): Add additional rules to allow IPV6 NDP. + dhcp_client = self.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, l4_protocol=firewall.UDP, source_ports=[67], destination_ports=[68]) - dhcp_client_rule_in = firewall.get_firewall_rule_dict( - 'DHCP Reply', direction=firewall.IN, service=dhcp_client) + dhcp_client_rule_in = self.get_firewall_rule_dict( + 'DHCP Reply', direction=firewall.IN, service=dhcp_client) - dhcp_server = ( - firewall.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, + dhcp_server = ( + self.get_nsservice(firewall.L4_PORT_SET_NSSERVICE, l4_protocol=firewall.UDP, source_ports=[68], destination_ports=[67])) - dhcp_client_rule_out = firewall.get_firewall_rule_dict( - 'DHCP Request', direction=firewall.OUT, service=dhcp_server) + dhcp_client_rule_out = self.get_firewall_rule_dict( + 'DHCP Request', direction=firewall.OUT, service=dhcp_server) - firewall.update_section(section['id'], + self.update_section(section['id'], name, section['description'], applied_tos=nested_groups, rules=[dhcp_client_rule_out, dhcp_client_rule_in, block_rule]) - return section['id'] - - -class NSGroupManager(object): - """ - This class assists with NSX integration for Neutron security-groups, - Each Neutron security-group is associated with NSX NSGroup object. - Some specific security policies are the same across all security-groups, - i.e - Default drop rule, DHCP. In order to bind these rules to all - NSGroups (security-groups), we create a nested NSGroup (which its members - are also of type NSGroups) to group the other NSGroups and associate it - with these rules. - In practice, one NSGroup (nested) can't contain all the other NSGroups, as - it has strict size limit. To overcome the limited space challange, we - create several nested groups instead of just one, and we evenly distribute - NSGroups (security-groups) between them. - By using an hashing function on the NSGroup uuid we determine in which - group it should be added, and when deleting an NSGroup (security-group) we - use the same procedure to find which nested group it was added. - """ - - NESTED_GROUP_NAME = 'OS Nested Group' - NESTED_GROUP_DESCRIPTION = ('OpenStack NSGroup. Do not delete.') - - def __init__(self, size): - self._nested_groups = self._init_nested_groups(size) - self._size = len(self._nested_groups) - - @property - def size(self): - return self._size - - @property - def nested_groups(self): - return self._nested_groups - - def _init_nested_groups(self, requested_size): - # Construct the groups dict - - # {0: ,.., n-1: } - size = requested_size - nested_groups = { - self._get_nested_group_index_from_name(nsgroup): nsgroup['id'] - for nsgroup in firewall.list_nsgroups() - if utils.is_internal_resource(nsgroup)} - - if nested_groups: - size = max(requested_size, max(nested_groups) + 1) - if size > requested_size: - LOG.warning(_LW("Lowering the value of " - "nsx_v3:number_of_nested_groups isn't " - "supported, '%s' nested-groups will be used."), - size) - - absent_groups = set(range(size)) - set(nested_groups.keys()) - if absent_groups: - LOG.warning( - _LW("Found %(num_present)s Nested Groups, " - "creating %(num_absent)s more."), - {'num_present': len(nested_groups), - 'num_absent': len(absent_groups)}) - for i in absent_groups: - cont = self._create_nested_group(i) - nested_groups[i] = cont['id'] - - return nested_groups - - def _get_nested_group_index_from_name(self, nested_group): - # The name format is "Nested Group " - return int(nested_group['display_name'].split()[-1]) - 1 - - def _create_nested_group(self, index): - name_prefix = NSGroupManager.NESTED_GROUP_NAME - name = '%s %s' % (name_prefix, index + 1) - description = NSGroupManager.NESTED_GROUP_DESCRIPTION - tags = utils.build_v3_api_version_tag() - return firewall.create_nsgroup(name, description, tags) - - def _hash_uuid(self, internal_id): - return hash(uuid.UUID(internal_id)) - - def _suggest_nested_group(self, internal_id): - # Suggests a nested group to use, can be iterated to find alternative - # group in case that previous suggestions did not help. - - index = self._hash_uuid(internal_id) % self.size - yield self.nested_groups[index] - - for i in range(1, self.size): - index = (index + 1) % self.size - yield self.nested_groups[index] - - def add_nsgroup(self, nsgroup_id): - for group in self._suggest_nested_group(nsgroup_id): - try: - LOG.debug("Adding NSGroup %s to nested group %s", - nsgroup_id, group) - firewall.add_nsgroup_members(group, - firewall.NSGROUP, - [nsgroup_id]) - break - except firewall.NSGroupIsFull: - LOG.debug("Nested group %(group_id)s is full, trying the " - "next group..", {'group_id': group}) - else: - raise nsx_exc.NsxPluginException( - err_msg=_("Reached the maximum supported amount of " - "security groups.")) - - def remove_nsgroup(self, nsgroup_id): - for group in self._suggest_nested_group(nsgroup_id): - try: - firewall.remove_nsgroup_member( - group, firewall.NSGROUP, nsgroup_id, verify=True) - break - except firewall.NSGroupMemberNotFound: - LOG.warning(_LW("NSGroup %(nsgroup)s was expected to be found " - "in group %(group_id)s, but wasn't. " - "Looking in the next group.."), - {'nsgroup': nsgroup_id, 'group_id': group}) - continue - else: - LOG.warning(_LW("NSGroup %s was marked for removal, but its " - "reference is missing."), nsgroup_id) + return section['id'] diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 567af613a6..f26b478754 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -84,10 +84,10 @@ from vmware_nsx.extensions import maclearning as mac_ext from vmware_nsx.extensions import providersecuritygroup as provider_sg from vmware_nsx.extensions import securitygrouplogging as sg_logging from vmware_nsx.nsxlib import v3 as nsxlib -from vmware_nsx.nsxlib.v3 import client as nsx_client -from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster from vmware_nsx.nsxlib.v3 import dfw_api as firewall +from vmware_nsx.nsxlib.v3 import exceptions as nsx_lib_exc from vmware_nsx.nsxlib.v3 import native_dhcp +from vmware_nsx.nsxlib.v3 import ns_group_manager from vmware_nsx.nsxlib.v3 import resources as nsx_resources from vmware_nsx.nsxlib.v3 import router from vmware_nsx.nsxlib.v3 import security @@ -162,11 +162,23 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def __init__(self): super(NsxV3Plugin, self).__init__() LOG.info(_LI("Starting NsxV3Plugin")) - self._nsx_version = nsxlib.get_version() + + self.nsxlib = nsxlib.NsxLib( + username=cfg.CONF.nsx_v3.nsx_api_user, + password=cfg.CONF.nsx_v3.nsx_api_password, + retries=cfg.CONF.nsx_v3.http_retries, + insecure=cfg.CONF.nsx_v3.insecure, + ca_file=cfg.CONF.nsx_v3.ca_file, + concurrent_connections=cfg.CONF.nsx_v3.concurrent_connections, + http_timeout=cfg.CONF.nsx_v3.http_timeout, + http_read_timeout=cfg.CONF.nsx_v3.http_read_timeout, + conn_idle_timeout=cfg.CONF.nsx_v3.conn_idle_timeout, + http_provider=None, + max_attempts=cfg.CONF.nsx_v3.retries) + + self._nsx_version = self.nsxlib.get_version() LOG.info(_LI("NSX Version: %s"), self._nsx_version) - self._api_cluster = nsx_cluster.NSXClusteredAPI() - self._nsx_client = nsx_client.NSX3Client(self._api_cluster) - nsx_client._set_default_api_cluster(self._api_cluster) + self._nsx_client = self.nsxlib.client self.cfg_group = 'nsx_v3' # group name for nsx_v3 section in nsx.ini self.tier0_groups_dict = {} @@ -180,11 +192,31 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._router_port_client = nsx_resources.LogicalRouterPort( self._nsx_client) self._routerlib = router.RouterLib(self._router_client, - self._router_port_client) + self._router_port_client, + self.nsxlib) - LOG.debug("Initializing NSX v3 port spoofguard switching profile") self._switching_profiles = nsx_resources.SwitchingProfile( self._nsx_client) + + # init profiles on nsx backend + (self._psec_profile, self._no_psec_profile_id, self._dhcp_profile, + self._mac_learning_profile) = self._init_nsx_profiles() + + # Bind QoS notifications + callbacks_registry.subscribe(qos_utils.handle_qos_notification, + callbacks_resources.QOS_POLICY) + self.start_rpc_listeners_called = False + + self._unsubscribe_callback_events() + if cfg.CONF.api_replay_mode: + self.supported_extension_aliases.append('api-replay') + + # translate configured transport zones/rotuers names to uuid + self._translate_configured_names_2_uuids() + + def _init_nsx_profiles(self): + LOG.debug("Initializing NSX v3 port spoofguard switching profile") + # XXX improve logic to avoid requiring setting this to none. self._psec_profile = None self._psec_profile = self._init_port_security_profile() if not self._psec_profile: @@ -197,24 +229,21 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._switching_profiles.find_by_display_name( NSX_V3_NO_PSEC_PROFILE_NAME)[0])[0] - # Bind QoS notifications - callbacks_registry.subscribe(qos_utils.handle_qos_notification, - callbacks_resources.QOS_POLICY) - self.start_rpc_listeners_called = False - LOG.debug("Initializing NSX v3 DHCP switching profile") - self._dhcp_profile = None try: + # XXX improve logic to avoid requiring setting this to none. + self._dhcp_profile = None self._dhcp_profile = self._init_dhcp_switching_profile() except Exception: msg = _("Unable to initialize NSX v3 DHCP " "switching profile: %s") % NSX_V3_DHCP_PROFILE_NAME raise nsx_exc.NsxPluginException(msg) - self._mac_learning_profile = None if utils.is_nsx_version_1_1_0(self._nsx_version): LOG.debug("Initializing NSX v3 Mac Learning switching profile") try: + # XXX improve logic to avoid requiring setting this to none. + self._mac_learning_profile = None self._mac_learning_profile = self._init_mac_learning_profile() # Only expose the extension if it is supported self.supported_extension_aliases.append('mac-learning') @@ -223,33 +252,28 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "profile: %(name)s. Reason: %(reason)s"), {'name': NSX_V3_MAC_LEARNING_PROFILE_NAME, 'reason': e}) - - self._unsubscribe_callback_events() - if cfg.CONF.api_replay_mode: - self.supported_extension_aliases.append('api-replay') - - # translate configured transport zones/rotuers names to uuid - self._translate_configured_names_2_uuids() + return (self._psec_profile, self._no_psec_profile_id, + self._dhcp_profile, self._mac_learning_profile) def _translate_configured_names_2_uuids(self): # default VLAN transport zone name / uuid self._default_vlan_tz_uuid = None if cfg.CONF.nsx_v3.default_vlan_tz: - tz_id = nsxlib.get_transport_zone_id_by_name_or_id( + tz_id = self.nsxlib.get_transport_zone_id_by_name_or_id( cfg.CONF.nsx_v3.default_vlan_tz) self._default_vlan_tz_uuid = tz_id # default overlay transport zone name / uuid self._default_overlay_tz_uuid = None if cfg.CONF.nsx_v3.default_overlay_tz: - tz_id = nsxlib.get_transport_zone_id_by_name_or_id( + tz_id = self.nsxlib.get_transport_zone_id_by_name_or_id( cfg.CONF.nsx_v3.default_overlay_tz) self._default_overlay_tz_uuid = tz_id # default tier0 router self._default_tier0_router = None if cfg.CONF.nsx_v3.default_tier0_router: - rtr_id = nsxlib.get_logical_router_id_by_name_or_id( + rtr_id = self.nsxlib.get_logical_router_id_by_name_or_id( cfg.CONF.nsx_v3.default_tier0_router) self._default_tier0_router = rtr_id @@ -367,12 +391,12 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, sg_logging.LOGGING]) for sg in [sg for sg in secgroups if sg[sg_logging.LOGGING] is False]: - _, section_id = security.get_sg_mappings(context.session, - sg['id']) + _, section_id = self.nsxlib.get_sg_mappings(context.session, + sg['id']) try: - security.set_firewall_rule_logging_for_section( + self.nsxlib.set_firewall_rule_logging_for_section( section_id, logging=log_all_rules) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to update firewall rule logging " "for rule in section %s"), section_id) @@ -381,7 +405,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def _init_nsgroup_manager_and_default_section_rules(self): with locking.LockManager.get_lock('nsxv3_nsgroup_manager_init'): - return security.init_nsgroup_manager_and_default_section_rules() + nsgroup_manager = ns_group_manager.NSGroupManager( + cfg.CONF.nsx_v3.number_of_nested_groups) + section_description = ("This section is handled by OpenStack to " + "contain default rules on security-groups.") + section_id = self.nsxlib._init_default_section( + security.DEFAULT_SECTION, section_description, + nsgroup_manager.nested_groups.values()) + return nsgroup_manager, section_id def _init_dhcp_metadata(self): if cfg.CONF.nsx_v3.native_dhcp_metadata: @@ -403,7 +434,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, cfg.CONF.nsx_v3.dhcp_profile_uuid) self._dhcp_server = nsx_resources.LogicalDhcpServer( self._nsx_client) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to retrieve DHCP Profile %s, " "native DHCP service is not supported"), @@ -415,7 +446,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, try: nsx_resources.MetaDataProxy(self._nsx_client).get( cfg.CONF.nsx_v3.metadata_proxy_uuid) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to retrieve Metadata Proxy %s, " "native metadata service is not supported"), @@ -579,9 +610,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, 'tags': tags, 'admin_state': admin_state, 'vlan_id': vlan_id}) - nsx_result = nsxlib.create_logical_switch(net_name, physical_net, tags, - admin_state=admin_state, - vlan_id=vlan_id) + nsx_result = self.nsxlib.create_logical_switch( + net_name, physical_net, tags, + admin_state=admin_state, + vlan_id=vlan_id) return (is_provider_net, net_type, @@ -696,7 +728,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, LOG.exception(_LE('Failed to create network %s'), created_net['id']) if net_type != utils.NetworkTypes.L3_EXT: - nsxlib.delete_logical_switch(created_net['id']) + self.nsxlib.delete_logical_switch(created_net['id']) # this extra lookup is necessary to get the # latest db model for the extension functions @@ -778,7 +810,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # TODO(salv-orlando): Handle backend failure, possibly without # requiring us to un-delete the DB object. For instance, ignore # failures occurring if logical switch is not found - nsxlib.delete_logical_switch(nsx_net_id) + self.nsxlib.delete_logical_switch(nsx_net_id) else: # TODO(berlin): delete subnets public announce on the network pass @@ -818,7 +850,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, try: # get the nsx switch id from the DB mapping nsx_id = self._get_network_nsx_id(context, id) - nsxlib.update_logical_switch( + self.nsxlib.update_logical_switch( nsx_id, name=utils.get_name_and_uuid(net_data['name'] or 'network', id), @@ -826,7 +858,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # Backend does not update the admin state of the ports on # the switch when the switch's admin state changes. Do not # update the admin state of the ports in neutron either. - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: LOG.exception(_LE("Unable to update NSX backend, rolling " "back changes on neutron")) with excutils.save_and_reraise_exception(): @@ -896,7 +928,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, LOG.debug("Created DHCP logical port %(port)s for " "network %(network)s", {'port': nsx_port['id'], 'network': network['id']}) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to create logical DHCP server for " "network %s"), network['id']) @@ -943,7 +975,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "%(network)s", {'server': dhcp_service['nsx_service_id'], 'network': network_id}) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to delete logical DHCP server %(server)s" "for network %(network)s"), @@ -1049,7 +1081,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, try: self._dhcp_server.update( dhcp_service['nsx_service_id'], **kwargs) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error( _LE("Unable to update logical DHCP server " @@ -1173,7 +1205,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def _get_qos_profile_id(self, context, policy_id): switch_profile_id = nsx_db.get_switch_profile_by_qos_policy( context.session, policy_id) - qos_profile = nsxlib.get_qos_switching_profile(switch_profile_id) + qos_profile = self.nsxlib.get_qos_switching_profile(switch_profile_id) if qos_profile: profile_ids = self._switching_profiles.build_switch_profile_ids( self._switching_profiles, qos_profile) @@ -1207,7 +1239,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # If port has no security-groups then we don't need to add any # security criteria tag. if port_data[ext_sg.SECURITYGROUPS]: - tags += security.get_lport_tags_for_security_groups( + tags += self.nsxlib.get_lport_tags_for_security_groups( port_data[ext_sg.SECURITYGROUPS] + port_data[provider_sg.PROVIDER_SECURITYGROUPS]) @@ -1264,7 +1296,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, attachment_type=attachment_type, parent_vif_id=parent_name, parent_tag=tag, switch_profile_ids=profiles) - except nsx_exc.ManagerError as inst: + except nsx_lib_exc.ManagerError as inst: # we may fail if the QoS is not supported for this port # (for example - transport zone with KVM) LOG.exception(_LE("Unable to create port on the backend: %s"), @@ -1390,7 +1422,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, {'mac': port['mac_address'], 'ip': ip, 'port': port['id'], 'server': dhcp_service_id}) return binding - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to create static binding (mac: %(mac)s, " "ip: %(ip)s) for port %(port)s on logical DHCP " @@ -1422,7 +1454,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "logical DHCP server %(server)s", {'port': binding['port_id'], 'server': binding['nsx_service_id']}) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to delete static binding for port " "%(port)s) on logical DHCP server %(server)s"), @@ -1474,7 +1506,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "%(server)s", {'ip': new_ip, 'server': dhcp_service['nsx_service_id']}) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to update IP %(ip)s for logical " "DHCP server %(server)s"), @@ -1539,7 +1571,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "for port %(port)s on logical DHCP server %(server)s", {'mac': mac, 'ip': ip, 'port': binding['port_id'], 'server': binding['nsx_service_id']}) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to update static binding (mac: %(mac)s, " "ip: %(ip)s) for port %(port)s on logical DHCP " @@ -1607,10 +1639,10 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, if not utils.is_nsx_version_1_1_0(self._nsx_version): try: - security.update_lport_with_security_groups( + self.nsxlib.update_lport_with_security_groups( context, lport['id'], [], sgids or []) - except Exception: - with excutils.save_and_reraise_exception(): + except Exception as e: + with excutils.save_and_reraise_exception(reraise=False): LOG.debug("Couldn't associate port %s with " "one or more security-groups, reverting " "logical-port creation (%s).", @@ -1618,6 +1650,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._cleanup_port( context, neutron_db['id'], lport['id']) + # NOTE(arosen): this is to translate between nsxlib + # exceptions and the plugin exceptions. This should be + # later refactored. + if (e.__class__ is + nsx_lib_exc.SecurityGroupMaximumCapacityReached): + raise nsx_exc.SecurityGroupMaximumCapacityReached( + err_msg=e.msg) + else: + raise e try: net_id = port_data[pbin.VIF_DETAILS]['nsx-logical-switch-id'] nsx_db.add_neutron_nsx_port_mapping( @@ -1674,7 +1715,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, context.session, port_id) self._port_client.delete(nsx_port_id) if not utils.is_nsx_version_1_1_0(self._nsx_version): - security.update_lport_with_security_groups( + self.nsxlib.update_lport_with_security_groups( context, nsx_port_id, port.get(ext_sg.SECURITYGROUPS, []), []) self.disassociate_floatingips(context, port_id) @@ -1794,11 +1835,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, name = self._get_port_name(context, updated_port) if utils.is_nsx_version_1_1_0(self._nsx_version): - tags_update += security.get_lport_tags_for_security_groups( + tags_update += self.nsxlib.get_lport_tags_for_security_groups( updated_port.get(ext_sg.SECURITYGROUPS, []) + updated_port.get(provider_sg.PROVIDER_SECURITYGROUPS, [])) else: - security.update_lport_with_security_groups( + self.nsxlib.update_lport_with_security_groups( context, lport_id, original_port.get(ext_sg.SECURITYGROUPS, []) + original_port.get(provider_sg.PROVIDER_SECURITYGROUPS, []), @@ -1836,7 +1877,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, tags_update=tags_update, parent_vif_id=parent_vif_id, parent_tag=tag) - except nsx_exc.ManagerError as inst: + except nsx_lib_exc.ManagerError as inst: # we may fail if the QoS is not supported for this port # (for example - transport zone with KVM) LOG.exception(_LE("Unable to update port on the backend: %s"), @@ -1933,13 +1974,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, original_port, updated_port, address_bindings, switch_profile_ids) - except (nsx_exc.ManagerError, - nsx_exc.SecurityGroupMaximumCapacityReached): + except (nsx_lib_exc.ManagerError, + nsx_lib_exc.SecurityGroupMaximumCapacityReached) as e: # In case if there is a failure on NSX-v3 backend, rollback the # previous update operation on neutron side. LOG.exception(_LE("Unable to update NSX backend, rolling back " "changes on neutron")) - with excutils.save_and_reraise_exception(): + with excutils.save_and_reraise_exception(reraise=False): with context.session.begin(subtransactions=True): super(NsxV3Plugin, self).update_port( context, id, {'port': original_port}) @@ -1960,6 +2001,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self.update_security_group_on_port( context, id, {'port': original_port}, updated_port, original_port) + # NOTE(arosen): this is to translate between nsxlib + # exceptions and the plugin exceptions. This should be + # later refactored. + if (e.__class__ is + nsx_lib_exc.SecurityGroupMaximumCapacityReached): + raise nsx_exc.SecurityGroupMaximumCapacityReached( + err_msg=e.msg) + else: + raise e # Update DHCP bindings. if cfg.CONF.nsx_v3.native_dhcp_metadata: @@ -2164,7 +2214,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, if gw_info != const.ATTR_NOT_SPECIFIED: try: self._update_router_gw_info(context, router['id'], gw_info) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Failed to set gateway info for router " "being created: %s - removing router"), @@ -2193,12 +2243,12 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # passed (and indeed the resource was removed from the Neutron DB try: self._router_client.delete(nsx_router_id) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: # If the logical router was not found on the backend do not worry # about it. The conditions has already been logged, so there is no # need to do further logging pass - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: # if there is a failure in deleting the router do not fail the # operation, especially since the router object has already been # removed from the neutron DB. Take corrective steps to ensure the @@ -2287,14 +2337,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, router_name, port['id'], tag='port') self._port_client.update(nsx_port_id, None, name=name) return self._update_router_wrapper(context, router_id, router) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: with context.session.begin(subtransactions=True): router_db = self._get_router(context, router_id) router_db['status'] = const.NET_STATUS_ERROR raise nsx_exc.NsxPluginException( err_msg=(_("logical router %s not found at the backend") % router_id)) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): router_db = self._get_router(context, router_id) curr_status = router_db['status'] @@ -2427,7 +2477,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # its DHCP port), by creating it if needed. nsx_rpc.handle_router_metadata_access(self, context, router_id, interface=info) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): self.remove_router_interface( context, router_id, interface_info) @@ -2493,7 +2543,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, subnets=address_groups) else: self._router_port_client.delete_by_lswitch_id(nsx_net_id) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: LOG.error(_LE("router port on router %(router_id)s for net " "%(net_id)s not found at the backend"), {'router_id': router_id, @@ -2537,7 +2587,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._routerlib.add_fip_nat_rules( nsx_router_id, new_fip['floating_ip_address'], new_fip['fixed_ip_address']) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): self.delete_floatingip(context, new_fip['id']) return new_fip @@ -2552,7 +2602,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._routerlib.delete_fip_nat_rules( nsx_router_id, fip['floating_ip_address'], fip['fixed_ip_address']) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: LOG.warning(_LW("Backend NAT rules for fip: %(fip_id)s " "(ext_ip: %(ext_ip)s int_ip: %(int_ip)s) " "not found"), @@ -2580,7 +2630,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._routerlib.delete_fip_nat_rules( old_nsx_router_id, old_fip['floating_ip_address'], old_fip['fixed_ip_address']) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: LOG.warning(_LW("Backend NAT rules for fip: %(fip_id)s " "(ext_ip: %(ext_ip)s int_ip: %(int_ip)s) " "not found"), @@ -2599,7 +2649,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._routerlib.add_fip_nat_rules( nsx_router_id, new_fip['floating_ip_address'], new_fip['fixed_ip_address']) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): super(NsxV3Plugin, self).update_floatingip( context, fip_id, {'floatingip': {'port_id': old_port_id}}) @@ -2623,7 +2673,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._routerlib.delete_fip_nat_rules( nsx_router_id, fip_db.floating_ip_address, fip_db.fixed_ip_address) - except nsx_exc.ResourceNotFound: + except nsx_lib_exc.ResourceNotFound: LOG.warning(_LW("Backend NAT rules for fip: %(fip_id)s " "(ext_ip: %(ext_ip)s int_ip: %(int_ip)s) " "not found"), @@ -2672,9 +2722,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, # security-group rules are located in a dedicated firewall section. firewall_section = ( - firewall.create_empty_section( - nsgroup['display_name'], nsgroup['description'], - [nsgroup['id']], nsgroup['tags'], + self.nsxlib.create_empty_section( + nsgroup.get('display_name'), nsgroup.get('description'), + [nsgroup.get('id')], nsgroup.get('tags'), operation=operation, other_section=self.default_section)) return firewall_section @@ -2683,16 +2733,16 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, tags = utils.build_v3_tags_payload( secgroup, resource_type='os-neutron-secgr-id', project_name=secgroup['tenant_id']) - name = security.get_nsgroup_name(secgroup) + name = self.nsxlib.get_nsgroup_name(secgroup) if utils.is_nsx_version_1_1_0(self._nsx_version): tag_expression = ( - firewall.get_nsgroup_port_tag_expression( + self.nsxlib.get_nsgroup_port_tag_expression( security.PORT_SG_SCOPE, secgroup['id'])) else: tag_expression = None - ns_group = firewall.create_nsgroup( + ns_group = self.nsxlib.create_nsgroup( name, secgroup['description'], tags, tag_expression) # security-group rules are located in a dedicated firewall section. firewall_section = self._create_fw_section_for_secgroup( @@ -2726,21 +2776,21 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, super(NsxV3Plugin, self).create_security_group( context, security_group, default_sg)) - security.save_sg_mappings(context.session, - secgroup_db['id'], - ns_group['id'], - firewall_section['id']) + self.nsxlib.save_sg_mappings(context.session, + secgroup_db['id'], + ns_group['id'], + firewall_section['id']) self._process_security_group_properties_create(context, secgroup_db, secgroup, default_sg) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Unable to create security-group on the " "backend.")) if ns_group: - firewall.delete_nsgroup(ns_group['id']) + self.nsxlib.delete_nsgroup(ns_group['id']) except Exception: with excutils.save_and_reraise_exception(): section_id = firewall_section.get('id') @@ -2750,9 +2800,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, "section %s, ns-group %s.", section_id, nsgroup_id) if nsgroup_id: - firewall.delete_nsgroup(nsgroup_id) + self.nsxlib.delete_nsgroup(nsgroup_id) if section_id: - firewall.delete_section(section_id) + self.nsxlib.delete_section(section_id) try: sg_rules = secgroup_db['security_group_rules'] # skip if there are no rules in group. i.e provider case @@ -2763,13 +2813,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, action = (firewall.DROP if secgroup.get(provider_sg.PROVIDER) else firewall.ALLOW) - rules = security.create_firewall_rules( + rules = self.nsxlib.create_firewall_rules( context, firewall_section['id'], ns_group['id'], logging, action, sg_rules) - security.save_sg_rule_mappings(context.session, - rules['rules']) + self.nsxlib.save_sg_rule_mappings(context.session, + rules['rules']) self.nsgroup_manager.add_nsgroup(ns_group['id']) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to create backend firewall rules " "for security-group %(name)s (%(id)s), " @@ -2779,8 +2829,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, context = context.elevated() super(NsxV3Plugin, self).delete_security_group( context, secgroup_db['id']) - firewall.delete_nsgroup(ns_group['id']) - firewall.delete_section(firewall_section['id']) + self.nsxlib.delete_nsgroup(ns_group['id']) + self.nsxlib.delete_section(firewall_section['id']) return secgroup_db @@ -2794,8 +2844,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, self._process_security_group_properties_update( context, secgroup_res, security_group['security_group']) try: - security.update_security_group_on_backend(context, secgroup_res) - except nsx_exc.ManagerError: + self.nsxlib.update_security_group_on_backend(context, secgroup_res) + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.exception(_LE("Failed to update security-group %(name)s " "(%(id)s), rolling back changes in " @@ -2806,10 +2856,11 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, return secgroup_res def delete_security_group(self, context, id): - nsgroup_id, section_id = security.get_sg_mappings(context.session, id) + nsgroup_id, section_id = self.nsxlib.get_sg_mappings( + context.session, id) super(NsxV3Plugin, self).delete_security_group(context, id) - firewall.delete_section(section_id) - firewall.delete_nsgroup(nsgroup_id) + self.nsxlib.delete_section(section_id) + self.nsxlib.delete_nsgroup(nsgroup_id) self.nsgroup_manager.remove_nsgroup(nsgroup_id) def create_security_group_rule(self, context, security_group_rule): @@ -2849,26 +2900,26 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, action = firewall.DROP sg_id = rules_db[0]['security_group_id'] - nsgroup_id, section_id = security.get_sg_mappings(context.session, - sg_id) + nsgroup_id, section_id = self.nsxlib.get_sg_mappings(context.session, + sg_id) logging_enabled = (cfg.CONF.nsx_v3.log_security_groups_allowed_traffic or self._is_security_group_logged(context, sg_id)) try: - rules = security.create_firewall_rules( + rules = self.nsxlib.create_firewall_rules( context, section_id, nsgroup_id, logging_enabled, action, rules_db) - except nsx_exc.ManagerError: + except nsx_lib_exc.ManagerError: with excutils.save_and_reraise_exception(): for rule in rules_db: super(NsxV3Plugin, self).delete_security_group_rule( context, rule['id']) - security.save_sg_rule_mappings(context.session, rules['rules']) + self.nsxlib.save_sg_rule_mappings(context.session, rules['rules']) return rules_db def delete_security_group_rule(self, context, id): rule_db = self._get_security_group_rule(context, id) sg_id = rule_db['security_group_id'] - _, section_id = security.get_sg_mappings(context.session, sg_id) - fw_rule_id = security.get_sg_rule_mapping(context.session, id) - firewall.delete_rule(section_id, fw_rule_id) + _, section_id = self.nsxlib.get_sg_mappings(context.session, sg_id) + fw_rule_id = self.nsxlib.get_sg_rule_mapping(context.session, id) + self.nsxlib.delete_rule(section_id, fw_rule_id) super(NsxV3Plugin, self).delete_security_group_rule(context, id) diff --git a/vmware_nsx/services/l2gateway/nsx_v3/driver.py b/vmware_nsx/services/l2gateway/nsx_v3/driver.py index d41be8f62b..8fb233d044 100644 --- a/vmware_nsx/services/l2gateway/nsx_v3/driver.py +++ b/vmware_nsx/services/l2gateway/nsx_v3/driver.py @@ -34,11 +34,10 @@ from neutron_lib import constants from neutron_lib import exceptions as n_exc from vmware_nsx._i18n import _, _LE, _LI -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as nsx_utils from vmware_nsx.db import db as nsx_db -from vmware_nsx.nsxlib import v3 as nsxlib +from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc LOG = logging.getLogger(__name__) @@ -83,8 +82,9 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): return admin_ctx = context.get_admin_context() - def_l2gw_uuid = nsxlib.get_bridge_cluster_id_by_name_or_id( - def_l2gw_name) + def_l2gw_uuid = ( + self._core_plugin.nsxlib.get_bridge_cluster_id_by_name_or_id( + def_l2gw_name)) # Optimistically create the default L2 gateway in neutron DB device = {'device_name': def_l2gw_uuid, @@ -221,11 +221,11 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): tags = nsx_utils.build_v3_tags_payload( gw_connection, resource_type='os-neutron-l2gw-id', project_name=context.tenant_name) - bridge_endpoint = nsxlib.create_bridge_endpoint( + bridge_endpoint = self._core_plugin.nsxlib.create_bridge_endpoint( device_name=device_name, seg_id=seg_id, tags=tags) - except nsx_exc.ManagerError as e: + except nsxlib_exc.ManagerError as e: LOG.exception(_LE("Unable to create bridge endpoint, rolling back " "changes on neutron. Exception is %s"), e) raise l2gw_exc.L2GatewayServiceDriverError( @@ -252,11 +252,12 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): fixed_ip['subnet_id'], fixed_ip['ip_address']) LOG.debug("IP addresses deallocated on port %s", port['id']) - except (nsx_exc.ManagerError, + except (nsxlib_exc.ManagerError, n_exc.NeutronException): LOG.exception(_LE("Unable to create L2 gateway port, " "rolling back changes on neutron")) - nsxlib.delete_bridge_endpoint(bridge_endpoint['id']) + self._core_plugin.nsxlib.delete_bridge_endpoint( + bridge_endpoint['id']) raise l2gw_exc.L2GatewayServiceDriverError( method='create_l2_gateway_connection_postcommit') try: @@ -270,7 +271,8 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): with excutils.save_and_reraise_exception(): LOG.exception(_LE("Unable to add L2 gateway connection " "mappings, rolling back changes on neutron")) - nsxlib.delete_bridge_endpoint(bridge_endpoint['id']) + self._core_plugin.nsxlib.delete_bridge_endpoint( + bridge_endpoint['id']) super(NsxV3Driver, self).delete_l2_gateway_connection( context, @@ -294,8 +296,8 @@ class NsxV3Driver(l2gateway_db.L2GatewayMixin): port_id=conn_mapping.get('port_id'), l2gw_port_check=False) try: - nsxlib.delete_bridge_endpoint(bridge_endpoint_id) - except nsx_exc.ManagerError as e: + self._core_plugin.nsxlib.delete_bridge_endpoint(bridge_endpoint_id) + except nsxlib_exc.ManagerError as e: LOG.exception(_LE("Unable to delete bridge endpoint %(id)s on the " "backend due to exc: %(exc)s"), {'id': bridge_endpoint_id, 'exc': e}) diff --git a/vmware_nsx/services/neutron_taas/nsx_v3/driver.py b/vmware_nsx/services/neutron_taas/nsx_v3/driver.py index a82ab31b82..52d2d12f71 100644 --- a/vmware_nsx/services/neutron_taas/nsx_v3/driver.py +++ b/vmware_nsx/services/neutron_taas/nsx_v3/driver.py @@ -30,6 +30,7 @@ from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import utils as nsx_utils from vmware_nsx.db import db as nsx_db from vmware_nsx.nsxlib import v3 as nsxlib +from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc from vmware_nsx.nsxlib.v3 import resources as nsx_resources LOG = logging.getLogger(__name__) @@ -219,7 +220,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, direction=direction, destinations=destinations, tags=tags)) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to create port mirror switch profile " "for tap flow %s on NSX backend, rolling back " @@ -245,7 +246,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, self._update_port_at_backend(context=context, port_id=src_port_id, switching_profile=port_mirror_profile, delete_profile=False) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to update source port %(port)s with " "switching profile %(profile) for tap flow " @@ -272,14 +273,14 @@ class NsxV3Driver(base_driver.TaasBaseDriver, context._plugin_context.session, dest_port_id) # Create port mirror session on the backend try: - pm_session = nsxlib.create_port_mirror_session( + pm_session = nsxlib.NsxLib().create_port_mirror_session( source_ports=nsx_src_ports, dest_ports=nsx_dest_ports, direction=direction, description=tf.get('description'), name=tf.get('name'), tags=tags) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to create port mirror session %s " "on NSX backend, rolling back " @@ -298,7 +299,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, LOG.error(_LE("Unable to create port mirror session db " "mappings for tap flow %s. Rolling back " "changes in Neutron."), tf['id']) - nsxlib.delete_port_mirror_session(pm_session['id']) + nsxlib.NsxLib().delete_port_mirror_session(pm_session['id']) def delete_tap_flow_precommit(self, context): pass @@ -343,7 +344,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, self._update_port_at_backend(context=context, port_id=src_port_id, switching_profile=port_mirror_profile, delete_profile=True) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: LOG.error(_LE("Unable to update source port %(port)s " "to delete port mirror profile %(pm)s on NSX " "backend."), @@ -352,7 +353,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, try: # Delete port mirroring switching profile self._nsx_plugin._switching_profiles.delete(uuid=pm_profile_id) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: LOG.error(_LE("Unable to delete port mirror switching profile " "%s on NSX backend."), pm_profile_id) @@ -360,7 +361,7 @@ class NsxV3Driver(base_driver.TaasBaseDriver, # Delete port mirroring session on the backend try: nsxlib.delete_port_mirror_session(pm_session_id) - except nsx_exc.ManagerError: + except nsxlib_exc.ManagerError: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to delete port mirror session %s " "on NSX backend."), pm_session_id) diff --git a/vmware_nsx/services/qos/nsx_v3/utils.py b/vmware_nsx/services/qos/nsx_v3/utils.py index e64e429d30..5e53b4790c 100644 --- a/vmware_nsx/services/qos/nsx_v3/utils.py +++ b/vmware_nsx/services/qos/nsx_v3/utils.py @@ -16,6 +16,7 @@ from neutron.api.rpc.callbacks import events as callbacks_events from neutron import context as n_context +from neutron import manager from neutron.objects.qos import policy as qos_policy from neutron.services.qos import qos_consts from neutron_lib.api import validators @@ -26,7 +27,6 @@ from vmware_nsx._i18n import _, _LW from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db -from vmware_nsx.nsxlib import v3 as nsxlib LOG = logging.getLogger(__name__) MAX_KBPS_MIN_VALUE = 1024 @@ -75,6 +75,10 @@ class QosNotificationsHandler(object): def __init__(self): super(QosNotificationsHandler, self).__init__() + @property + def _core_plugin(self): + return manager.NeutronManager.get_plugin() + def _get_tags(self, context, policy): policy_dict = {'id': policy.id, 'tenant_id': policy.tenant_id} return utils.build_v3_tags_payload( @@ -84,7 +88,7 @@ class QosNotificationsHandler(object): def create_policy(self, context, policy): policy_id = policy.id tags = self._get_tags(context, policy) - result = nsxlib.create_qos_switching_profile( + result = self._core_plugin.nsxlib.create_qos_switching_profile( tags=tags, name=policy.name, description=policy.description) if not result or not validators.is_attr_set(result.get('id')): @@ -100,13 +104,13 @@ class QosNotificationsHandler(object): def delete_policy(self, context, policy_id): profile_id = nsx_db.get_switch_profile_by_qos_policy( context.session, policy_id) - nsxlib.delete_qos_switching_profile(profile_id) + self._core_plugin.nsxlib.delete_qos_switching_profile(profile_id) def update_policy(self, context, policy_id, policy): profile_id = nsx_db.get_switch_profile_by_qos_policy( context.session, policy_id) tags = self._get_tags(context, policy) - nsxlib.update_qos_switching_profile( + self._core_plugin.nsxlib.update_qos_switching_profile( profile_id, tags=tags, name=policy.name, @@ -176,8 +180,7 @@ class QosNotificationsHandler(object): average_bw) = self._get_bw_values_from_rule(bw_rule) qos_marking, dscp = self._get_dscp_values_from_rule(dscp_rule) - - nsxlib.update_qos_switching_profile_shaping( + self._core_plugin.nsxlib.update_qos_switching_profile_shaping( profile_id, shaping_enabled=shaping_enabled, burst_size=burst_size, diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/dhcp_binding.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/dhcp_binding.py index 337aace34b..9e69466d55 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/dhcp_binding.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/dhcp_binding.py @@ -21,9 +21,6 @@ from oslo_config import cfg from vmware_nsx._i18n import _LI from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as comm_utils -from vmware_nsx.nsxlib import v3 as nsxlib -from vmware_nsx.nsxlib.v3 import client -from vmware_nsx.nsxlib.v3 import cluster from vmware_nsx.nsxlib.v3 import native_dhcp from vmware_nsx.nsxlib.v3 import resources from vmware_nsx.shell.admin.plugins.common import constants @@ -51,15 +48,13 @@ def list_dhcp_bindings(resource, event, trigger, **kwargs): def nsx_update_dhcp_bindings(resource, event, trigger, **kwargs): """Resync DHCP bindings for NSXv3 CrossHairs.""" - nsx_version = nsxlib.get_version() + nsx_version = utils.get_connected_nsxlib().get_version() if not comm_utils.is_nsx_version_1_1_0(nsx_version): LOG.info(_LI("This utility is not available for NSX version %s"), nsx_version) return - cluster_api = cluster.NSXClusteredAPI() - nsx_client = client.NSX3Client(cluster_api) - client._set_default_api_cluster(cluster_api) + nsx_client = utils.get_nsxv3_client() port_resource = resources.LogicalPort(nsx_client) dhcp_server_resource = resources.LogicalDhcpServer(nsx_client) diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/metadata_proxy.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/metadata_proxy.py index 747e5dc2f4..68f46c283d 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/metadata_proxy.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/metadata_proxy.py @@ -20,8 +20,6 @@ from oslo_config import cfg from vmware_nsx._i18n import _LI, _LE from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils as nsx_utils -from vmware_nsx.nsxlib.v3 import client -from vmware_nsx.nsxlib.v3 import cluster from vmware_nsx.nsxlib.v3 import resources from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import utils as admin_utils @@ -36,9 +34,7 @@ neutron_client = utils.NeutronDbClient() def nsx_update_metadata_proxy(resource, event, trigger, **kwargs): """Update Metadata proxy for NSXv3 CrossHairs.""" - cluster_api = cluster.NSXClusteredAPI() - nsx_client = client.NSX3Client(cluster_api) - client._set_default_api_cluster(cluster_api) + nsx_client = utils.get_nsxv3_client() port_resource = resources.LogicalPort(nsx_client) for network in neutron_client.get_networks(): diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/networks.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/networks.py index 731d6f285b..1eab554a9e 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/networks.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/networks.py @@ -18,7 +18,6 @@ import logging from vmware_nsx._i18n import _LI from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.db import db as nsx_db -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import formatters from vmware_nsx.shell.admin.plugins.common import utils as admin_utils @@ -55,7 +54,7 @@ def list_missing_networks(resource, event, trigger, **kwargs): pass else: try: - nsxlib.get_logical_switch(nsx_id) + admin_utils.get_connected_nsxlib().get_logical_switch(nsx_id) except nsx_exc.ResourceNotFound: networks.append({'name': net['name'], 'neutron_id': neutron_id, diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py index 36e96e3146..ace44d281b 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/ports.py @@ -21,14 +21,13 @@ from vmware_nsx._i18n import _LI, _LW from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.db import db as nsx_db from vmware_nsx.db import nsx_models -from vmware_nsx.nsxlib.v3 import client -from vmware_nsx.nsxlib.v3 import cluster from vmware_nsx.nsxlib.v3 import resources from vmware_nsx.plugins.nsx_v3 import plugin from vmware_nsx.services.qos.common import utils as qos_utils from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import formatters from vmware_nsx.shell.admin.plugins.common import utils as admin_utils +from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils as v3_utils from vmware_nsx.shell import resources as shell from neutron.callbacks import registry @@ -60,8 +59,7 @@ def get_port_nsx_id(session, neutron_id): def get_port_and_profile_clients(): - _api_cluster = cluster.NSXClusteredAPI() - _nsx_client = client.NSX3Client(_api_cluster) + _nsx_client = v3_utils.get_nsxv3_client() return (resources.LogicalPort(_nsx_client), resources.SwitchingProfile(_nsx_client)) diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/routers.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/routers.py index d46f40a3bc..59a3e2d6fd 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/routers.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/routers.py @@ -18,12 +18,11 @@ import logging from vmware_nsx._i18n import _LI from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.db import db as nsx_db -from vmware_nsx.nsxlib.v3 import client as nsx_client -from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster from vmware_nsx.nsxlib.v3 import resources as nsx_resources from vmware_nsx.shell.admin.plugins.common import constants from vmware_nsx.shell.admin.plugins.common import formatters from vmware_nsx.shell.admin.plugins.common import utils as admin_utils +from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils from vmware_nsx.shell import resources as shell from neutron.callbacks import registry @@ -40,8 +39,7 @@ class RoutersPlugin(db_base_plugin_v2.NeutronDbPluginV2, def get_router_client(): - _api_cluster = nsx_cluster.NSXClusteredAPI() - _nsx_client = nsx_client.NSX3Client(_api_cluster) + _nsx_client = utils.get_nsxv3_client() return nsx_resources.LogicalRouter(_nsx_client) diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/securitygroups.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/securitygroups.py index 8aef7a860b..fe0a215cd9 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/securitygroups.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/securitygroups.py @@ -31,7 +31,6 @@ from vmware_nsx.shell.admin.plugins.nsxv3.resources import ports from vmware_nsx.shell.admin.plugins.nsxv3.resources import utils as v3_utils from vmware_nsx.shell import resources as shell from vmware_nsx._i18n import _LE, _LW -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.nsxlib.v3 import dfw_api as firewall from vmware_nsx.nsxlib.v3 import security @@ -107,6 +106,7 @@ class NeutronSecurityGroupApi(securitygroups_db.SecurityGroupDbMixin, neutron_sg = NeutronSecurityGroupApi() neutron_db = v3_utils.NeutronDbClient() +nsxlib = v3_utils.get_connected_nsxlib() def _log_info(resource, data, attrs=['display_name', 'id']): @@ -126,7 +126,7 @@ def list_security_groups_mappings(resource, event, trigger, **kwargs): @admin_utils.list_handler(constants.FIREWALL_SECTIONS) @admin_utils.output_header def nsx_list_dfw_sections(resource, event, trigger, **kwargs): - fw_sections = firewall.list_sections() + fw_sections = nsxlib.list_sections() _log_info(constants.FIREWALL_SECTIONS, fw_sections) return bool(fw_sections) @@ -134,13 +134,13 @@ def nsx_list_dfw_sections(resource, event, trigger, **kwargs): @admin_utils.list_handler(constants.FIREWALL_NSX_GROUPS) @admin_utils.output_header def nsx_list_security_groups(resource, event, trigger, **kwargs): - nsx_secgroups = firewall.list_nsgroups() + nsx_secgroups = nsxlib.list_nsgroups() _log_info(constants.FIREWALL_NSX_GROUPS, nsx_secgroups) return bool(nsx_secgroups) def _find_missing_security_groups(): - nsx_secgroups = firewall.list_nsgroups() + nsx_secgroups = nsxlib.list_nsgroups() sg_mappings = neutron_sg.get_security_groups_mappings() missing_secgroups = {} for sg_db in sg_mappings: @@ -169,7 +169,7 @@ def list_missing_security_groups(resource, event, trigger, **kwargs): def _find_missing_sections(): - fw_sections = firewall.list_sections() + fw_sections = nsxlib.list_sections() sg_mappings = neutron_sg.get_security_groups_mappings() missing_sections = {} for sg_db in sg_mappings: @@ -204,13 +204,13 @@ def fix_security_groups(resource, event, trigger, **kwargs): for sg_id, sg in inconsistent_secgroups.items(): secgroup = plugin.get_security_group(context_, sg_id) - firewall.delete_section(sg['section-id']) - firewall.delete_nsgroup(sg['nsx-securitygroup-id']) + nsxlib.delete_section(sg['section-id']) + nsxlib.delete_nsgroup(sg['nsx-securitygroup-id']) neutron_sg.delete_security_group_section_mapping(sg_id) neutron_sg.delete_security_group_backend_mapping(sg_id) nsgroup, fw_section = ( plugin._create_security_group_backend_resources(secgroup)) - security.save_sg_mappings( + nsxlib.save_sg_mappings( context_.session, sg_id, nsgroup['id'], fw_section['id']) # If version > 1.1 then we use dynamic criteria tags, and the port # should already have them. @@ -219,7 +219,7 @@ def fix_security_groups(resource, event, trigger, **kwargs): for port_id in neutron_db.get_ports_in_security_group(sg_id): lport_id = neutron_db.get_logical_port_id(port_id) members.append(lport_id) - firewall.add_nsgroup_members( + nsxlib.add_nsgroup_members( nsgroup['id'], firewall.LOGICAL_PORT, members) for rule in secgroup['security_group_rules']: @@ -231,11 +231,11 @@ def fix_security_groups(resource, event, trigger, **kwargs): action = (firewall.DROP if secgroup.get(provider_sg.PROVIDER) else firewall.ALLOW) - rules = security.create_firewall_rules( + rules = nsxlib.create_firewall_rules( context_, fw_section['id'], nsgroup['id'], secgroup.get(sg_logging.LOGGING, False), action, secgroup['security_group_rules']) - security.save_sg_rule_mappings(context_.session, rules['rules']) + nsxlib.save_sg_rule_mappings(context_.session, rules['rules']) # Add nsgroup to a nested group plugin.nsgroup_manager.add_nsgroup(nsgroup['id']) @@ -250,7 +250,7 @@ def _update_ports_dynamic_criteria_tags(): _, lport_id = neutron_db.get_lswitch_and_lport_id(port['id']) lport = port_client.get(lport_id) - criteria_tags = security.get_lport_tags_for_security_groups(secgroups) + criteria_tags = nsxlib.get_lport_tags_for_security_groups(secgroups) lport['tags'] = utils.update_v3_tags( lport.get('tags', []), criteria_tags) port_client._client.update(lport_id, body=lport) @@ -260,14 +260,14 @@ def _update_security_group_dynamic_criteria(): secgroups = neutron_sg.get_security_groups() for sg in secgroups: nsgroup_id = neutron_sg.get_nsgroup_id(sg['id']) - membership_criteria = firewall.get_nsgroup_port_tag_expression( + membership_criteria = nsxlib.get_nsgroup_port_tag_expression( security.PORT_SG_SCOPE, sg['id']) try: # We want to add the dynamic criteria and remove all direct members # they will be added by the manager using the new criteria. - firewall.update_nsgroup(nsgroup_id, - membership_criteria=membership_criteria, - members=[]) + nsxlib.update_nsgroup(nsgroup_id, + membership_criteria=membership_criteria, + members=[]) except Exception as e: LOG.warning(_LW("Failed to update membership criteria for nsgroup " "%(nsgroup_id)s, request to backend returned " diff --git a/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py b/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py index 3cf52cb6be..0eec62f825 100644 --- a/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py +++ b/vmware_nsx/shell/admin/plugins/nsxv3/resources/utils.py @@ -12,13 +12,35 @@ # License for the specific language governing permissions and limitations # under the License. + from neutron import context from neutron.db import db_base_plugin_v2 +from oslo_config import cfg from vmware_nsx.db import db as nsx_db +from vmware_nsx.nsxlib import v3 from vmware_nsx.plugins.nsx_v3 import plugin +def get_nsxv3_client(): + return get_connected_nsxlib().client + + +def get_connected_nsxlib(): + return v3.NsxLib( + username=cfg.CONF.nsx_v3.nsx_api_user, + password=cfg.CONF.nsx_v3.nsx_api_password, + retries=cfg.CONF.nsx_v3.http_retries, + insecure=cfg.CONF.nsx_v3.insecure, + ca_file=cfg.CONF.nsx_v3.ca_file, + concurrent_connections=cfg.CONF.nsx_v3.concurrent_connections, + http_timeout=cfg.CONF.nsx_v3.http_timeout, + http_read_timeout=cfg.CONF.nsx_v3.http_read_timeout, + conn_idle_timeout=cfg.CONF.nsx_v3.conn_idle_timeout, + http_provider=None, + max_attempts=cfg.CONF.nsx_v3.retries) + + class NeutronDbClient(db_base_plugin_v2.NeutronDbPluginV2): def __init__(self): super(NeutronDbClient, self).__init__() diff --git a/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py b/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py index 49be2fe9cd..1ba66c757a 100644 --- a/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py +++ b/vmware_nsx/tests/unit/extensions/test_provider_security_groups.py @@ -244,8 +244,8 @@ class ProviderSecurityGroupExtTestCase( self.assertEqual([sg_id], port['port']['security_groups']) -class TestNSXv3ProviderSecurityGrp(test_nsxv3_plugin.NsxV3PluginTestCaseMixin, - ProviderSecurityGroupExtTestCase): +class TestNSXv3ProviderSecurityGrp(ProviderSecurityGroupExtTestCase, + test_nsxv3_plugin.NsxV3PluginTestCaseMixin): pass diff --git a/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py b/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py index 736182eae5..e0441f8b61 100644 --- a/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py +++ b/vmware_nsx/tests/unit/extensions/test_secgroup_rule_local_ip_prefix.py @@ -27,7 +27,6 @@ from neutron_lib import constants as const from vmware_nsx.db import extended_security_group_rule as ext_rule_db from vmware_nsx.extensions import secgroup_rule_local_ip_prefix as ext_loip -from vmware_nsx.nsxlib.v3 import dfw_api as v3_fw from vmware_nsx.plugins.nsx_v.vshield import securitygroup_utils from vmware_nsx.tests.unit.nsx_v import test_plugin as test_nsxv_plugin from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3_plugin @@ -124,15 +123,30 @@ class TestNsxVExtendedSGRule(test_nsxv_plugin.NsxVSecurityGroupsTestCase, class TestNSXv3ExtendedSGRule(test_nsxv3_plugin.NsxV3PluginTestCaseMixin, LocalIPPrefixExtTestCase): def test_create_rule_with_local_ip_prefix(self): - local_ip_prefix = '239.255.0.0/16' - dest = v3_fw.get_ip_cidr_reference(local_ip_prefix, v3_fw.IPV4) + sg_rules = [ + {'tenant_id': mock.ANY, + 'id': mock.ANY, + 'port_range_min': None, + 'local_ip_prefix': '239.255.0.0/16', + 'ethertype': 'IPv4', + 'protocol': u'udp', 'remote_ip_prefix': '10.0.0.0/24', + 'port_range_max': None, + 'security_group_id': mock.ANY, + 'remote_group_id': None, 'direction': u'ingress', + 'description': ''}] - with mock.patch.object(v3_fw, 'get_firewall_rule_dict', - side_effect=v3_fw.get_firewall_rule_dict) as mock_rule: + with mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_firewall_rules", + side_effect=test_nsxv3_plugin._mock_create_firewall_rules, + ) as mock_rule: super(TestNSXv3ExtendedSGRule, self).test_create_rule_with_local_ip_prefix() - mock_rule.assert_called_with(mock.ANY, mock.ANY, dest, mock.ANY, - v3_fw.IPV4, mock.ANY, v3_fw.ALLOW, - mock.ANY) + mock_rule.assert_called_with( + mock.ANY, # content + mock.ANY, # firewall_section_id + mock.ANY, # ns_group_id + False, # logging + 'ALLOW', # action + sg_rules) # sg_rules diff --git a/vmware_nsx/tests/unit/extensions/test_securitygroup.py b/vmware_nsx/tests/unit/extensions/test_securitygroup.py index 4d91522842..b455c21782 100644 --- a/vmware_nsx/tests/unit/extensions/test_securitygroup.py +++ b/vmware_nsx/tests/unit/extensions/test_securitygroup.py @@ -18,7 +18,8 @@ from neutron.extensions import securitygroup as ext_sg from neutron.tests.unit.extensions import test_securitygroup as test_ext_sg from vmware_nsx.nsxlib.v3 import dfw_api as firewall -from vmware_nsx.nsxlib.v3 import security +from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc +from vmware_nsx.nsxlib.v3 import ns_group_manager from vmware_nsx.plugins.nsx_v3 import plugin as nsx_plugin from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsxv3 from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase @@ -44,11 +45,13 @@ def _mock_create_and_list_nsgroups(test_method): return nsgroup def wrap(*args, **kwargs): - with mock.patch.object(nsx_plugin.security.firewall, - 'create_nsgroup') as create_nsgroup_mock: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.create_nsgroup' + ) as create_nsgroup_mock: create_nsgroup_mock.side_effect = _create_nsgroup_mock - with mock.patch.object(nsx_plugin.security.firewall, - 'list_nsgroups') as list_nsgroups_mock: + with mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.list_nsgroups" + ) as list_nsgroups_mock: list_nsgroups_mock.side_effect = lambda: nsgroups test_method(*args, **kwargs) return wrap @@ -71,8 +74,8 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, self._patchers.append(mock_nsx_version) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_create_port_with_multiple_security_groups(self, add_member_mock, remove_member_mock): @@ -86,8 +89,8 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, add_member_mock.assert_has_calls(calls, any_order=True) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_update_port_with_multiple_security_groups(self, add_member_mock, remove_member_mock): @@ -103,8 +106,8 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, NSG_IDS[0], firewall.LOGICAL_PORT, mock.ANY) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_update_port_remove_security_group_empty_list(self, add_member_mock, remove_member_mock): @@ -117,12 +120,12 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, NSG_IDS[1], firewall.LOGICAL_PORT, mock.ANY) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_create_port_with_full_security_group(self, add_member_mock): def _add_member_mock(nsgroup, target_type, target_id): if nsgroup in NSG_IDS: - raise firewall.NSGroupIsFull(nsgroup_id=nsgroup) + raise nsxlib_exc.NSGroupIsFull(nsgroup_id=nsgroup) add_member_mock.side_effect = _add_member_mock with self.network() as net: @@ -135,14 +138,14 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, res_body['NeutronError']['type']) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_update_port_with_full_security_group(self, add_member_mock, remove_member_mock): def _add_member_mock(nsgroup, target_type, target_id): if nsgroup == NSG_IDS[2]: - raise firewall.NSGroupIsFull(nsgroup_id=nsgroup) + raise nsxlib_exc.NSGroupIsFull(nsgroup_id=nsgroup) add_member_mock.side_effect = _add_member_mock with self.port() as port: @@ -174,13 +177,13 @@ class TestSecurityGroupsNoDynamicCriteria(test_nsxv3.NsxV3PluginTestCaseMixin, class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): """ This test suite is responsible for unittesting of class - vmware_nsx.nsxlib.v3.security.NSGroupManager. + vmware_nsx.nsxlib.v3.ns_group_manager.NSGroupManager. """ @_mock_create_and_list_nsgroups def test_first_initialization(self): size = 5 - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) nested_groups = cont_manager.nested_groups self.assertEqual({i: NSG_IDS[i] for i in range(size)}, nested_groups) @@ -194,17 +197,17 @@ class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): size = 2 # Creates 2 nested groups. - security.NSGroupManager(size) + ns_group_manager.NSGroupManager(size) size = 5 # Creates another 3 nested groups. - nested_groups = security.NSGroupManager(size).nested_groups + nested_groups = ns_group_manager.NSGroupManager(size).nested_groups self.assertEqual({i: NSG_IDS[i] for i in range(size)}, nested_groups) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_add_and_remove_nsgroups(self, add_member_mock, remove_member_mock): @@ -212,7 +215,7 @@ class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): # according to its id and the number of nested groups. size = 5 - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) nsgroup_id = 'nsgroup_id' with mock.patch.object(cont_manager, '_hash_uuid', return_value=7): @@ -227,25 +230,26 @@ class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): NSG_IDS[2], firewall.NSGROUP, nsgroup_id, verify=True) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def test_when_nested_group_is_full(self, add_member_mock, remove_member_mock): def _add_member_mock(nsgroup, target_type, target_id): if nsgroup == NSG_IDS[2]: - raise firewall.NSGroupIsFull(nsgroup_id=nsgroup) + raise nsxlib_exc.NSGroupIsFull(nsgroup_id=nsgroup) def _remove_member_mock(nsgroup, target_type, target_id, verify=False): if nsgroup == NSG_IDS[2]: - raise firewall.NSGroupMemberNotFound() + raise nsxlib_exc.NSGroupMemberNotFound(nsgroup_id=nsgroup, + member_id=target_id) add_member_mock.side_effect = _add_member_mock remove_member_mock.side_effect = _remove_member_mock size = 5 - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) nsgroup_id = 'nsgroup_id' with mock.patch.object(cont_manager, '_hash_uuid', return_value=7): @@ -270,20 +274,20 @@ class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): remove_member_mock.assert_has_calls(calls) @_mock_create_and_list_nsgroups - @mock.patch.object(firewall, 'remove_nsgroup_member') - @mock.patch.object(firewall, 'add_nsgroup_members') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.remove_nsgroup_member') + @mock.patch('vmware_nsx.nsxlib.v3.NsxLib.add_nsgroup_members') def initialize_with_absent_nested_groups(self, add_member_mock, remove_member_mock): size = 3 - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) # list_nsgroups will return nested group 1 and 3, but not group 2. with mock.patch.object(firewall, 'list_nsgroups_mock') as list_nsgroups_mock: list_nsgroups_mock = lambda: list_nsgroups_mock()[::2] # invoking the initialization process again, it should process # groups 1 and 3 and create group 2. - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) self.assertEqual({1: NSG_IDS[0], 2: NSG_IDS[3], 3: NSG_IDS[2]}, @@ -292,7 +296,7 @@ class TestNSGroupManager(nsxlib_testcase.NsxLibTestCase): @_mock_create_and_list_nsgroups def test_suggest_nested_group(self): size = 5 - cont_manager = security.NSGroupManager(size) + cont_manager = ns_group_manager.NSGroupManager(size) # We expect that the first suggested index is 2 expected_suggested_groups = NSG_IDS[2:5] + NSG_IDS[:2] suggest_group = lambda: cont_manager._suggest_nested_group('fake-id') diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 4c8330be92..77f43b20ba 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -42,7 +42,6 @@ from neutron import version from neutron_lib import constants from neutron_lib import exceptions as n_exc from oslo_config import cfg -from oslo_serialization import jsonutils from oslo_utils import uuidutils from vmware_nsx.common import exceptions as nsx_exc @@ -50,8 +49,6 @@ from vmware_nsx.common import nsx_constants from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db from vmware_nsx.extensions import advancedserviceproviders as as_providers -from vmware_nsx.nsxlib.v3 import client as nsx_client -from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster from vmware_nsx.nsxlib.v3 import resources as nsx_resources from vmware_nsx.plugins.nsx_v3 import plugin as nsx_plugin from vmware_nsx.tests import unit as vmware @@ -63,6 +60,84 @@ from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase PLUGIN_NAME = 'vmware_nsx.plugin.NsxV3Plugin' +def _mock_create_firewall_rules(*args): + # NOTE(arosen): the code in the neutron plugin expects the + # neutron rule id as the display_name. + rules = args[5] + return { + 'rules': [ + {'display_name': rule['id'], 'id': uuidutils.generate_uuid()} + for rule in rules + ]} + + +def _mock_nsx_backend_calls(): + mock.patch("vmware_nsx.nsxlib.v3.client.NSX3Client").start() + + class FakeProfile(object): + profile_id = uuidutils.generate_uuid() + profile_type = 'FakeProfile' + + def _init_nsx_profiles(): + return ( + FakeProfile(), # _psec_profile + FakeProfile(), # _no_psec_profile_id + FakeProfile(), # _dhcp_profile + FakeProfile(), # _mac_learning_profile + ) + + def _return_id_key(*args, **kwargs): + return {'id': uuidutils.generate_uuid()} + + def _return_id(*args, **kwargs): + return uuidutils.generate_uuid() + + mock.patch( + "vmware_nsx.plugins.nsx_v3.plugin.NsxV3Plugin._init_nsx_profiles", + side_effect=_init_nsx_profiles).start() + + mock.patch( + "vmware_nsx.plugins.nsx_v3.plugin.NsxV3Plugin" + "._get_port_security_profile_id", return_value=FakeProfile() + ).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.router.RouterLib.validate_tier0").start() + + mock.patch( + "vmware_nsx.nsxlib.v3.resources.SwitchingProfile." + "create_port_mirror_profile", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.get_bridge_cluster_id_by_name_or_id", + return_value=uuidutils.generate_uuid()).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_bridge_endpoint", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_logical_switch", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.resources.LogicalPort.create", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.resources.LogicalRouter.create", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.resources.LogicalDhcpServer.create", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.resources.LogicalDhcpServer.create_binding", + side_effect=_return_id_key).start() + + class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase, nsxlib_testcase.NsxClientTestCase): @@ -72,83 +147,8 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase, self._patchers = [] - self.mock_api = nsx_v3_mocks.MockRequestSessionApi() + _mock_nsx_backend_calls() nsxlib_testcase.NsxClientTestCase.setup_conf_overrides() - self.cluster = nsx_cluster.NSXClusteredAPI( - http_provider=nsxlib_testcase.MemoryMockAPIProvider(self.mock_api)) - - def _patch_object(*args, **kwargs): - patcher = mock.patch.object(*args, **kwargs) - patcher.start() - self._patchers.append(patcher) - - def _new_cluster(*args, **kwargs): - return self.cluster - - self.mocked_rest_fns( - nsx_plugin.security.firewall, 'nsxclient', - mock_cluster=self.cluster) - self.mocked_rest_fns( - nsx_plugin.router.nsxlib, 'client', mock_cluster=self.cluster) - - mock_client_module = mock.Mock() - mock_cluster_module = mock.Mock() - mocked_client = self.new_mocked_client( - nsx_client.NSX3Client, mock_cluster=self.cluster) - mock_cluster_module.NSXClusteredAPI.return_value = self.cluster - mock_client_module.NSX3Client.return_value = mocked_client - _patch_object(nsx_plugin, 'nsx_client', new=mock_client_module) - _patch_object(nsx_plugin, 'nsx_cluster', new=mock_cluster_module) - self._mock_client_module = mock_client_module - self._mock_cluster_module = mock_cluster_module - - # Mock the nsx v3 version - mock_nsxlib_get_version = mock.patch( - "vmware_nsx.nsxlib.v3.get_version", - return_value='1.1.0') - mock_nsxlib_get_version.start() - - # populate pre-existing mock resources - cluster_id = uuidutils.generate_uuid() - self.mock_api.post( - 'api/v1/logical-routers', - data=jsonutils.dumps({ - 'display_name': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID, - 'router_type': "TIER0", - 'id': nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID, - 'edge_cluster_id': cluster_id}), - headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS) - - self.mock_api.post( - 'api/v1/edge-clusters', - data=jsonutils.dumps({ - 'id': cluster_id, - 'members': [ - {'member_index': 0}, - {'member_index': 1} - ]}), - headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS) - - self.mock_api.post( - 'api/v1/switching-profiles', - data=jsonutils.dumps({ - 'id': uuidutils.generate_uuid(), - 'display_name': nsx_plugin.NSX_V3_NO_PSEC_PROFILE_NAME - }), headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS) - - self.mock_api.post( - 'api/v1/transport-zones', - data=jsonutils.dumps({ - 'id': uuidutils.generate_uuid(), - 'display_name': nsxlib_testcase.NSX_TZ_NAME - }), headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS) - - self.mock_api.post( - 'api/v1/bridge-clusters', - data=jsonutils.dumps({ - 'id': uuidutils.generate_uuid(), - 'display_name': nsx_v3_mocks.NSX_BRIDGE_CLUSTER_NAME - }), headers=nsx_client.JSONRESTClient._DEFAULT_HEADERS) super(NsxV3PluginTestCaseMixin, self).setUp(plugin=plugin, ext_mgr=ext_mgr) @@ -646,8 +646,8 @@ class TestL3NatTestCase(L3NatTest, self.skipTest('not supported') -class ExtGwModeTestCase(L3NatTest, - test_ext_gw_mode.ExtGwModeIntTestCase): +class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase, + L3NatTest): def test_router_gateway_set_fail_after_port_create(self): self.skipTest("TBD") @@ -869,8 +869,10 @@ class NsxNativeDhcpTestCase(NsxV3PluginTestCaseMixin): def _verify_dhcp_binding(self, subnet, port_data, update_data, assert_data): # Verify if DHCP binding is updated. - with mock.patch.object(nsx_resources.LogicalDhcpServer, - 'update_binding') as update_dhcp_binding: + + with mock.patch( + 'vmware_nsx.nsxlib.v3.resources.LogicalDhcpServer.update_binding' + ) as update_dhcp_binding: device_owner = constants.DEVICE_OWNER_COMPUTE_PREFIX + 'None' device_id = uuidutils.generate_uuid() with self.port(subnet=subnet, device_owner=device_owner, diff --git a/vmware_nsx/tests/unit/nsxlib/v3/nsxlib_testcase.py b/vmware_nsx/tests/unit/nsxlib/v3/nsxlib_testcase.py index a7be9c43a3..e6dc158462 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/nsxlib_testcase.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/nsxlib_testcase.py @@ -18,8 +18,10 @@ import mock import unittest from oslo_config import cfg +from oslo_utils import uuidutils from requests import exceptions as requests_exceptions +from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.nsxlib.v3 import client as nsx_client from vmware_nsx.nsxlib.v3 import cluster as nsx_cluster @@ -39,6 +41,54 @@ BRIDGE_FNS = ['create_resource', 'delete_resource', 'update_resource', 'get_resource'] +def _mock_nsxlib(): + def _return_id_key(*args, **kwargs): + return {'id': uuidutils.generate_uuid()} + + # FIXME(arosen): this is duplicated in test_plugin + def _mock_create_firewall_rules(*args): + # NOTE(arosen): the code in the neutron plugin expects the + # neutron rule id as the display_name. + rules = args[5] + return { + 'rules': [ + {'display_name': rule['id'], 'id': uuidutils.generate_uuid()} + for rule in rules + ]} + + mock.patch( + "vmware_nsx.nsxlib.v3.cluster.NSXRequestsHTTPProvider" + ".validate_connection").start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_nsgroup", + side_effect=_return_id_key + ).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_empty_section", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib._init_default_section", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.list_nsgroups").start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.create_firewall_rules", + side_effect=_mock_create_firewall_rules).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.get_transport_zone_id_by_name_or_id", + side_effect=_return_id_key).start() + + mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib.get_version", + return_value='1.1.0').start() + + class NsxLibTestCase(unittest.TestCase): @classmethod @@ -57,12 +107,27 @@ class NsxLibTestCase(unittest.TestCase): cfg.CONF.set_override('http_timeout', NSX_HTTP_TIMEOUT, 'nsx_v3') cfg.CONF.set_override('http_read_timeout', NSX_HTTP_READ_TIMEOUT, 'nsx_v3') - cfg.CONF.set_override('network_scheduler_driver', + cfg.CONF.set_override( + 'network_scheduler_driver', 'neutron.scheduler.dhcp_agent_scheduler.AZAwareWeightScheduler') def setUp(self, *args, **kwargs): super(NsxLibTestCase, self).setUp() NsxClientTestCase.setup_conf_overrides() + _mock_nsxlib() + + self.nsxlib = nsxlib.NsxLib( + username=cfg.CONF.nsx_v3.nsx_api_user, + password=cfg.CONF.nsx_v3.nsx_api_password, + retries=cfg.CONF.nsx_v3.http_retries, + insecure=cfg.CONF.nsx_v3.insecure, + ca_file=cfg.CONF.nsx_v3.ca_file, + concurrent_connections=cfg.CONF.nsx_v3.concurrent_connections, + http_timeout=cfg.CONF.nsx_v3.http_timeout, + http_read_timeout=cfg.CONF.nsx_v3.http_read_timeout, + conn_idle_timeout=cfg.CONF.nsx_v3.conn_idle_timeout, + http_provider=None, + max_attempts=cfg.CONF.nsx_v3.retries) # print diffs when assert comparisons fail self.maxDiff = None @@ -254,9 +319,10 @@ class NsxClientTestCase(NsxLibTestCase): return client def mocked_rest_fns(self, module, attr, mock_validate=True, - mock_cluster=None): - client = nsx_client.NSX3Client( - mock_cluster or self.mock_nsx_clustered_api()) + mock_cluster=None, client=None): + if client is None: + client = nsx_client.NSX3Client( + mock_cluster or self.mock_nsx_clustered_api()) mocked_fns = NsxClientTestCase.MockBridge(client) mocked_fns.JSONRESTClient = nsx_client.JSONRESTClient diff --git a/vmware_nsx/tests/unit/nsxlib/v3/test_client.py b/vmware_nsx/tests/unit/nsxlib/v3/test_client.py index 9ecd0afe78..7caae76a7e 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/test_client.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/test_client.py @@ -18,8 +18,8 @@ import copy from oslo_log import log from oslo_serialization import jsonutils -from vmware_nsx.common import exceptions as exep from vmware_nsx.nsxlib.v3 import client +from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc from vmware_nsx.tests.unit.nsx_v3 import mocks from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase @@ -160,7 +160,7 @@ class NsxV3RESTClientTestCase(nsxlib_testcase.NsxClientTestCase): def test_client_create(self): api = self.new_mocked_client(client.RESTClient, url_prefix='api/v1/ports') - api.create(jsonutils.dumps({'resource-name': 'port1'})) + api.create(body=jsonutils.dumps({'resource-name': 'port1'})) assert_call( 'post', api, @@ -235,7 +235,7 @@ class NsxV3RESTClientTestCase(nsxlib_testcase.NsxClientTestCase): for code in client.RESTClient._VERB_RESP_CODES.get(verb): _verb_response_code(verb, code) self.assertRaises( - exep.ManagerError, + nsxlib_exc.ManagerError, _verb_response_code, verb, 500) diff --git a/vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py b/vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py index 7984ad422c..c99a33af4c 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/test_cluster.py @@ -15,13 +15,14 @@ # import mock import six.moves.urllib.parse as urlparse +import unittest from oslo_config import cfg from oslo_serialization import jsonutils from requests import exceptions as requests_exceptions -from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.nsxlib.v3 import client from vmware_nsx.nsxlib.v3 import cluster +from vmware_nsx.nsxlib.v3 import exceptions as nsxlib_exc from vmware_nsx.tests.unit.nsx_v3 import mocks from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase @@ -34,7 +35,7 @@ def _validate_conn_down(*args, **kwargs): raise requests_exceptions.ConnectionError() -class RequestsHTTPProviderTestCase(nsxlib_testcase.NsxClientTestCase): +class RequestsHTTPProviderTestCase(unittest.TestCase): def test_new_connection(self): mock_api = mock.Mock() @@ -56,11 +57,12 @@ class RequestsHTTPProviderTestCase(nsxlib_testcase.NsxClientTestCase): self.assertEqual(session.timeout, 99) def test_validate_connection(self): + self.skipTest("Revist") mock_conn = mocks.MockRequestSessionApi() mock_ep = mock.Mock() mock_ep.provider.url = 'https://1.2.3.4' provider = cluster.NSXRequestsHTTPProvider() - self.assertRaises(nsx_exc.ResourceNotFound, + self.assertRaises(nsxlib_exc.ResourceNotFound, provider.validate_connection, mock.Mock(), mock_ep, mock_conn) @@ -190,17 +192,17 @@ class ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase): api = cluster.NSXClusteredAPI(http_provider=mock_provider) self.assertEqual(len(api.endpoints), 3) - self.assertRaises(nsx_exc.ServiceClusterUnavailable, + self.assertRaises(nsxlib_exc.ServiceClusterUnavailable, api.get, 'api/v1/transport-zones') def test_cluster_proxy_stale_revision(self): def stale_revision(): - raise nsx_exc.StaleRevision(manager='1.1.1.1', - operation='whatever') + raise nsxlib_exc.StaleRevision(manager='1.1.1.1', + operation='whatever') api = self.mock_nsx_clustered_api(session_response=stale_revision) - self.assertRaises(nsx_exc.StaleRevision, + self.assertRaises(nsxlib_exc.StaleRevision, api.get, 'api/v1/transport-zones') def test_cluster_proxy_connection_error(self): @@ -210,7 +212,7 @@ class ClusteredAPITestCase(nsxlib_testcase.NsxClientTestCase): api = self.mock_nsx_clustered_api(session_response=connect_timeout) api._validate = mock.Mock() - self.assertRaises(nsx_exc.ServiceClusterUnavailable, + self.assertRaises(nsxlib_exc.ServiceClusterUnavailable, api.get, 'api/v1/transport-zones') def test_cluster_round_robin_servicing(self): diff --git a/vmware_nsx/tests/unit/nsxlib/v3/test_qos_switching_profile.py b/vmware_nsx/tests/unit/nsxlib/v3/test_qos_switching_profile.py index 8549d72005..5fc8d47971 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/test_qos_switching_profile.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/test_qos_switching_profile.py @@ -16,12 +16,9 @@ import mock from oslo_log import log -from oslo_serialization import jsonutils -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.tests.unit.nsx_v3 import test_constants as test_constants_v3 from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase -from vmware_nsx.tests.unit.nsxlib.v3 import test_client LOG = log.getLogger(__name__) @@ -35,7 +32,7 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase): "tags": [] } if qos_marking: - body = nsxlib._update_dscp_in_args(body, qos_marking, dscp) + body = self.nsxlib._update_dscp_in_args(body, qos_marking, dscp) body["display_name"] = test_constants_v3.FAKE_NAME body["description"] = description @@ -66,7 +63,8 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase): break if qos_marking: - body = nsxlib._update_dscp_in_args(body, qos_marking, dscp) + body = self.nsxlib._update_dscp_in_args( + body, qos_marking, dscp) return body @@ -74,47 +72,39 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase): """ Test creating a qos-switching profile returns the correct response """ - api = self.mocked_rest_fns(nsxlib, 'client') - nsxlib.create_qos_switching_profile( - tags=[], - name=test_constants_v3.FAKE_NAME, - description=test_constants_v3.FAKE_NAME) - - test_client.assert_json_call( - 'post', api, - 'https://1.2.3.4/api/v1/switching-profiles', - data=jsonutils.dumps(self._body(), - sort_keys=True)) + with mock.patch.object(self.nsxlib.client, 'create') as create: + self.nsxlib.create_qos_switching_profile( + tags=[], + name=test_constants_v3.FAKE_NAME, + description=test_constants_v3.FAKE_NAME) + create.assert_called_with( + 'switching-profiles', self._body()) def test_update_qos_switching_profile(self): """ Test updating a qos-switching profile returns the correct response """ - api = self.mocked_rest_fns(nsxlib, 'client') - original_profile = self._body() new_description = "Test" - with mock.patch.object(nsxlib.client, 'get_resource', - return_value=original_profile): - # update the description of the profile - nsxlib.update_qos_switching_profile( - test_constants_v3.FAKE_QOS_PROFILE['id'], - tags=[], - description=new_description) + with mock.patch.object(self.nsxlib.client, 'get', + return_value=original_profile): + with mock.patch.object(self.nsxlib.client, 'update') as update: - test_client.assert_json_call( - 'put', api, - 'https://1.2.3.4/api/v1/switching-profiles/%s' - % test_constants_v3.FAKE_QOS_PROFILE['id'], - data=jsonutils.dumps(self._body(description=new_description), - sort_keys=True)) + # update the description of the profile + self.nsxlib.update_qos_switching_profile( + test_constants_v3.FAKE_QOS_PROFILE['id'], + tags=[], + description=new_description) + update.assert_called_with( + 'switching-profiles/%s' + % test_constants_v3.FAKE_QOS_PROFILE['id'], + self._body(description=new_description)) def test_enable_qos_switching_profile_shaping(self): """ Test updating a qos-switching profile returns the correct response """ - api = self.mocked_rest_fns(nsxlib, 'client') original_profile = self._body_with_shaping() burst_size = 100 @@ -122,37 +112,34 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase): average_bandwidth = 300 qos_marking = "untrusted" dscp = 10 - with mock.patch.object(nsxlib.client, 'get_resource', - return_value=original_profile): - # update the bw shaping of the profile - nsxlib.update_qos_switching_profile_shaping( - test_constants_v3.FAKE_QOS_PROFILE['id'], - shaping_enabled=True, - burst_size=burst_size, - peak_bandwidth=peak_bandwidth, - average_bandwidth=average_bandwidth, - qos_marking=qos_marking, - dscp=dscp) - test_client.assert_json_call( - 'put', api, - 'https://1.2.3.4/api/v1/switching-profiles/%s' - % test_constants_v3.FAKE_QOS_PROFILE['id'], - data=jsonutils.dumps( + with mock.patch.object(self.nsxlib.client, 'get', + return_value=original_profile): + with mock.patch.object(self.nsxlib.client, 'update') as update: + # update the bw shaping of the profile + self.nsxlib.update_qos_switching_profile_shaping( + test_constants_v3.FAKE_QOS_PROFILE['id'], + shaping_enabled=True, + burst_size=burst_size, + peak_bandwidth=peak_bandwidth, + average_bandwidth=average_bandwidth, + qos_marking=qos_marking, + dscp=dscp) + + update.assert_called_with( + 'switching-profiles/%s' + % test_constants_v3.FAKE_QOS_PROFILE['id'], self._body_with_shaping( shaping_enabled=True, burst_size=burst_size, peak_bandwidth=peak_bandwidth, average_bandwidth=average_bandwidth, - qos_marking="untrusted", dscp=10), - sort_keys=True)) + qos_marking="untrusted", dscp=10)) def test_disable_qos_switching_profile_shaping(self): """ Test updating a qos-switching profile returns the correct response """ - api = self.mocked_rest_fns(nsxlib, 'client') - burst_size = 100 peak_bandwidth = 200 average_bandwidth = 300 @@ -163,31 +150,27 @@ class NsxLibQosTestCase(nsxlib_testcase.NsxClientTestCase): average_bandwidth=average_bandwidth, qos_marking="untrusted", dscp=10) - with mock.patch.object(nsxlib.client, 'get_resource', - return_value=original_profile): - # update the bw shaping of the profile - nsxlib.update_qos_switching_profile_shaping( - test_constants_v3.FAKE_QOS_PROFILE['id'], - shaping_enabled=False, qos_marking="trusted") - test_client.assert_json_call( - 'put', api, - 'https://1.2.3.4/api/v1/switching-profiles/%s' - % test_constants_v3.FAKE_QOS_PROFILE['id'], - data=jsonutils.dumps( - self._body_with_shaping(qos_marking="trusted"), - sort_keys=True)) + with mock.patch.object(self.nsxlib.client, 'get', + return_value=original_profile): + with mock.patch.object(self.nsxlib.client, 'update') as update: + # update the bw shaping of the profile + self.nsxlib.update_qos_switching_profile_shaping( + test_constants_v3.FAKE_QOS_PROFILE['id'], + shaping_enabled=False, qos_marking="trusted") + + update.assert_called_with( + 'switching-profiles/%s' + % test_constants_v3.FAKE_QOS_PROFILE['id'], + self._body_with_shaping(qos_marking="trusted")) def test_delete_qos_switching_profile(self): """ Test deleting qos-switching-profile """ - api = self.mocked_rest_fns(nsxlib, 'client') - - nsxlib.delete_qos_switching_profile( - test_constants_v3.FAKE_QOS_PROFILE['id']) - - test_client.assert_json_call( - 'delete', api, - 'https://1.2.3.4/api/v1/switching-profiles/%s' - % test_constants_v3.FAKE_QOS_PROFILE['id']) + with mock.patch.object(self.nsxlib.client, 'delete') as delete: + self.nsxlib.delete_qos_switching_profile( + test_constants_v3.FAKE_QOS_PROFILE['id']) + delete.assert_called_with( + 'switching-profiles/%s' + % test_constants_v3.FAKE_QOS_PROFILE['id']) diff --git a/vmware_nsx/tests/unit/nsxlib/v3/test_switch.py b/vmware_nsx/tests/unit/nsxlib/v3/test_switch.py index 04d0b54d2a..6039acf6bd 100644 --- a/vmware_nsx/tests/unit/nsxlib/v3/test_switch.py +++ b/vmware_nsx/tests/unit/nsxlib/v3/test_switch.py @@ -13,14 +13,13 @@ # See the License for the specific language governing permissions and # limitations under the License. # + +import mock from oslo_log import log -from oslo_serialization import jsonutils from vmware_nsx.common import nsx_constants -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.tests.unit.nsx_v3 import mocks as nsx_v3_mocks from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase -from vmware_nsx.tests.unit.nsxlib.v3 import test_client LOG = log.getLogger(__name__) @@ -46,59 +45,49 @@ class NsxLibSwitchTestCase(nsxlib_testcase.NsxClientTestCase): """ Test creating a switch returns the correct response and 200 status """ - api = self.mocked_rest_fns(nsxlib, 'client') - nsxlib.create_logical_switch( - nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, []) - - test_client.assert_json_call( - 'post', api, - 'https://1.2.3.4/api/v1/logical-switches', - data=jsonutils.dumps(self._create_body(), sort_keys=True)) + with mock.patch.object(self.nsxlib.client, 'create') as create: + self.nsxlib.create_logical_switch( + nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, []) + create.assert_called_with('logical-switches', self._create_body()) def test_create_logical_switch_admin_down(self): """ Test creating switch with admin_state down """ - api = self.mocked_rest_fns(nsxlib, 'client') - nsxlib.create_logical_switch( - nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, - [], admin_state=False) + with mock.patch.object(self.nsxlib.client, 'create') as create: + self.nsxlib.create_logical_switch( + nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, + [], admin_state=False) - test_client.assert_json_call( - 'post', api, - 'https://1.2.3.4/api/v1/logical-switches', - data=jsonutils.dumps(self._create_body( - admin_state=nsx_constants.ADMIN_STATE_DOWN), - sort_keys=True)) + create.assert_called_with( + 'logical-switches', + self._create_body( + admin_state=nsx_constants.ADMIN_STATE_DOWN)) def test_create_logical_switch_vlan(self): """ Test creating switch with provider:network_type VLAN """ - api = self.mocked_rest_fns(nsxlib, 'client') - nsxlib.create_logical_switch( - nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, - [], vlan_id='123') + with mock.patch.object(self.nsxlib.client, 'create') as create: + self.nsxlib.create_logical_switch( + nsx_v3_mocks.FAKE_NAME, NsxLibSwitchTestCase._tz_id, + [], vlan_id='123') - test_client.assert_json_call( - 'post', api, - 'https://1.2.3.4/api/v1/logical-switches', - data=jsonutils.dumps(self._create_body(vlan_id='123'), - sort_keys=True)) + create.assert_called_with( + 'logical-switches', + self._create_body(vlan_id='123')) def test_delete_logical_switch(self): """ Test deleting switch """ - api = self.mocked_rest_fns(nsxlib, 'client') - fake_switch = nsx_v3_mocks.make_fake_switch() - nsxlib.delete_logical_switch(fake_switch['id']) - - test_client.assert_json_call( - 'delete', api, - 'https://1.2.3.4/api/v1/logical-switches/%s' - '?detach=true&cascade=true' % fake_switch['id']) + with mock.patch.object(self.nsxlib.client, 'delete') as delete: + fake_switch = nsx_v3_mocks.make_fake_switch() + self.nsxlib.delete_logical_switch(fake_switch['id']) + delete.assert_called_with( + 'logical-switches/%s' + '?detach=true&cascade=true' % fake_switch['id']) diff --git a/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py b/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py index bacab5e263..62fb020dea 100644 --- a/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py +++ b/vmware_nsx/tests/unit/services/l2gateway/test_nsxv3_driver.py @@ -29,7 +29,6 @@ from neutron.tests import base from neutron_lib import exceptions as n_exc from vmware_nsx.common import nsx_constants -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.services.l2gateway.nsx_v3 import driver as nsx_v3_driver from vmware_nsx.tests.unit.nsx_v3 import mocks as nsx_v3_mocks from vmware_nsx.tests.unit.nsx_v3 import test_plugin as test_nsx_v3_plugin @@ -85,8 +84,9 @@ class TestNsxV3L2GatewayDriver(test_l2gw_db.L2GWTestCase, "nsx_v3") nsx_v3_driver.NsxV3Driver(mock.MagicMock()) l2gws = self.driver._get_l2_gateways(self.context) - def_bridge_cluster_id = nsxlib.get_bridge_cluster_id_by_name_or_id( - def_bridge_cluster_name) + def_bridge_cluster_id = ( + self.nsxlib.get_bridge_cluster_id_by_name_or_id( + def_bridge_cluster_name)) def_l2gw = None for l2gw in l2gws: for device in l2gw['devices']: diff --git a/vmware_nsx/tests/unit/services/qos/test_nsxv3_notification.py b/vmware_nsx/tests/unit/services/qos/test_nsxv3_notification.py index de9b91a5b7..12bdc00c79 100644 --- a/vmware_nsx/tests/unit/services/qos/test_nsxv3_notification.py +++ b/vmware_nsx/tests/unit/services/qos/test_nsxv3_notification.py @@ -26,15 +26,14 @@ from neutron.tests.unit.services.qos import base from vmware_nsx.common import utils from vmware_nsx.db import db as nsx_db -from vmware_nsx.nsxlib import v3 as nsxlib from vmware_nsx.services.qos.nsx_v3 import utils as qos_utils -from vmware_nsx.tests.unit.nsxlib.v3 import nsxlib_testcase +from vmware_nsx.tests.unit.nsx_v3 import test_plugin PLUGIN_NAME = 'vmware_nsx.plugins.nsx_v3.plugin.NsxV3Plugin' -class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, - base.BaseQosTestCase): +class TestQosNsxV3Notification(base.BaseQosTestCase, + test_plugin.NsxV3PluginTestCaseMixin): def setUp(self): super(TestQosNsxV3Notification, self).setUp() @@ -47,7 +46,6 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, ['vmware_nsx.tests.unit.services.qos.fake_notifier.' 'DummyNotificationDriver'], "qos") - self.qos_plugin = qos_plugin.QoSPlugin() self.ctxt = context.Context('fake_user', 'fake_tenant') self.policy_data = { @@ -81,7 +79,7 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, mock.patch( 'neutron.objects.qos.policy.QosPolicy.obj_load_attr').start() mock.patch.object(nsx_db, 'get_switch_profile_by_qos_policy', - return_value=self.fake_profile_id).start() + return_value=self.fake_profile_id).start() self.peak_bw_multiplier = cfg.CONF.NSX.qos_peak_bw_multiplier @@ -91,8 +89,10 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, @mock.patch.object(nsx_db, 'add_qos_policy_profile_mapping') def test_policy_create_profile(self, fake_db_add, fake_rbac_create): # test the switch profile creation when a QoS policy is created - with mock.patch.object(nsxlib, 'create_qos_switching_profile', - return_value=self.fake_profile) as create_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.create_qos_switching_profile', + return_value=self.fake_profile + ) as create_profile: with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.policy.QosPolicy.create'): @@ -117,8 +117,9 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, # test the switch profile update when a QoS policy is updated fields = base_object.get_updatable_fields( policy_object.QosPolicy, self.policy_data['policy']) - with mock.patch.object(nsxlib, - 'update_qos_switching_profile') as update_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.update_qos_switching_profile' + ) as update_profile: with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=self.policy): with mock.patch('neutron.objects.qos.policy.QosPolicy.update'): @@ -147,8 +148,10 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, setattr(_policy, "rules", [self.rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): - with mock.patch.object(nsxlib, - 'update_qos_switching_profile_shaping') as update_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.' + 'update_qos_switching_profile_shaping' + ) as update_profile: with mock.patch('neutron.objects.db.api.update_object', return_value=self.rule_data): self.qos_plugin.update_policy_bandwidth_limit_rule( @@ -188,8 +191,10 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, setattr(_policy, "rules", [rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): - with mock.patch.object(nsxlib, - 'update_qos_switching_profile_shaping') as update_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.' + 'update_qos_switching_profile_shaping' + ) as update_profile: with mock.patch('neutron.objects.db.api.update_object', return_value=rule_data): self.qos_plugin.update_policy_bandwidth_limit_rule( @@ -219,8 +224,10 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, setattr(_policy, "rules", [self.dscp_rule]) with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): - with mock.patch.object(nsxlib, - 'update_qos_switching_profile_shaping') as update_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.' + 'update_qos_switching_profile_shaping' + ) as update_profile: with mock.patch('neutron.objects.db.api.' 'update_object', return_value=self.dscp_rule_data): self.qos_plugin.update_policy_dscp_marking_rule( @@ -250,8 +257,10 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, # as if it was deleted with mock.patch('neutron.objects.qos.policy.QosPolicy.get_object', return_value=_policy): - with mock.patch.object(nsxlib, - 'update_qos_switching_profile_shaping') as update_profile: + with mock.patch( + "vmware_nsx.nsxlib.v3.NsxLib." + "update_qos_switching_profile_shaping" + ) as update_profile: setattr(_policy, "rules", [self.rule]) self.qos_plugin.delete_policy_bandwidth_limit_rule( self.ctxt, self.rule.id, self.policy.id) @@ -269,7 +278,9 @@ class TestQosNsxV3Notification(nsxlib_testcase.NsxClientTestCase, @mock.patch('neutron.objects.db.api.get_object', return_value=None) def test_policy_delete_profile(self, *mocks): # test the switch profile deletion when a QoS policy is deleted - with mock.patch.object(nsxlib, 'delete_qos_switching_profile', - return_value=self.fake_profile) as delete_profile: + with mock.patch( + 'vmware_nsx.nsxlib.v3.NsxLib.delete_qos_switching_profile', + return_value=self.fake_profile + ) as delete_profile: self.qos_plugin.delete_policy(self.ctxt, self.policy.id) delete_profile.assert_called_once_with(self.fake_profile_id) diff --git a/vmware_nsx/tests/unit/shell/test_admin_utils.py b/vmware_nsx/tests/unit/shell/test_admin_utils.py index a6cfe9b7ed..b21a4845ae 100644 --- a/vmware_nsx/tests/unit/shell/test_admin_utils.py +++ b/vmware_nsx/tests/unit/shell/test_admin_utils.py @@ -32,9 +32,7 @@ from neutron_lbaas.services.loadbalancer.drivers.vmware import db # noqa from vmware_nsx._i18n import _ from vmware_nsx.common import config # noqa -from vmware_nsx.nsxlib.v3 import client as nsx_v3_client from vmware_nsx.nsxlib.v3 import resources as nsx_v3_resources -from vmware_nsx.plugins.nsx_v3 import plugin as nsx_v3_plugin from vmware_nsx.shell import resources from vmware_nsx.tests import unit as vmware from vmware_nsx.tests.unit.nsx_v.vshield import fake_vcns @@ -132,10 +130,7 @@ class TestNsxv3AdminUtils(AbstractTestAdminUtils, self._patchers.append(patcher) def _init_mock_plugin(self): - self._patch_object(nsx_v3_client, 'NSX3Client', - new=self._mock_client_module) - self._patch_object(nsx_v3_plugin, 'nsx_cluster', - new=self._mock_cluster_module) + test_v3_plugin._mock_nsx_backend_calls() # mock resources self._patch_object(nsx_v3_resources.LogicalPort,