# Copyright 2016 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. from oslo_log import log from vmware_nsxlib._i18n import _, _LW from vmware_nsxlib.v3 import client from vmware_nsxlib.v3 import cluster from vmware_nsxlib.v3 import exceptions from vmware_nsxlib.v3 import native_dhcp from vmware_nsxlib.v3 import nsx_constants from vmware_nsxlib.v3 import security from vmware_nsxlib.v3 import utils LOG = log.getLogger(__name__) class NsxLib(object): def __init__(self, nsxlib_config): self.nsxlib_config = nsxlib_config # create the Cluster self.cluster = cluster.NSXClusteredAPI(nsxlib_config) # create the Client self.client = client.NSX3Client( self.cluster, nsx_api_managers=nsxlib_config.nsx_api_managers, max_attempts=nsxlib_config.max_attempts) # init the api object self.general_apis = utils.NsxLibApiBase( self.client, nsxlib_config) self.port_mirror = NsxLibPortMirror( self.client, nsxlib_config) self.bridge_endpoint = NsxLibBridgeEndpoint( self.client, nsxlib_config) self.logical_switch = NsxLibLogicalSwitch( self.client, nsxlib_config) self.logical_router = NsxLibLogicalRouter( self.client, nsxlib_config) self.qos_switching_profile = NsxLibQosSwitchingProfile( self.client, nsxlib_config) self.edge_cluster = NsxLibEdgeCluster( self.client, nsxlib_config) self.bridge_cluster = NsxLibBridgeCluster( self.client, nsxlib_config) self.transport_zone = NsxLibTransportZone( self.client, nsxlib_config) self.native_dhcp_profile = NsxLibDhcpProfile( self.client, nsxlib_config) self.native_md_proxy = NsxLibMetadataProxy( self.client, nsxlib_config) self.firewall_section = security.NsxLibFirewallSection( self.client, nsxlib_config) self.ns_group = security.NsxLibNsGroup( self.client, nsxlib_config, self.firewall_section) self.native_dhcp = native_dhcp.NsxLibNativeDhcp( self.client, nsxlib_config) super(NsxLib, self).__init__() def get_version(self): node = self.client.get("node") version = node.get('node_version') return version def build_v3_api_version_tag(self): return self.general_apis.build_v3_api_version_tag() def is_internal_resource(self, nsx_resource): return self.general_apis.is_internal_resource(nsx_resource) def build_v3_tags_payload(self, resource, resource_type, project_name): return self.general_apis.build_v3_tags_payload( resource, resource_type, project_name) def reinitialize_cluster(self, resource, event, trigger, **kwargs): self.cluster._reinit_cluster() class NsxLibPortMirror(utils.NsxLibApiBase): def create_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_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) class NsxLibBridgeEndpoint(utils.NsxLibApiBase): def create(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(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) class NsxLibLogicalSwitch(utils.NsxLibApiBase): def create(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 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 self.client.create(resource, body) def delete(self, lswitch_id): # Using internal method so we can access max_attempts in the decorator @utils.retry_upon_exception( exceptions.StaleRevision, max_attempts=self.nsxlib_config.max_attempts) def _do_delete(): resource = ('logical-switches/%s?detach=true&cascade=true' % lswitch_id) self.client.delete(resource) _do_delete() def get(self, logical_switch_id): resource = "logical-switches/%s" % logical_switch_id return self.client.get(resource) def update(self, lswitch_id, name=None, admin_state=None, tags=None): # Using internal method so we can access max_attempts in the decorator @utils.retry_upon_exception( exceptions.StaleRevision, max_attempts=self.nsxlib_config.max_attempts) def _do_update(): resource = "logical-switches/%s" % lswitch_id lswitch = self.get(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) return _do_update() class NsxLibQosSwitchingProfile(utils.NsxLibApiBase): def _build_args(self, tags, name=None, description=None): body = {"resource_type": "QosSwitchingProfile", "tags": tags} return self._update_args( body, name=name, description=description) def _update_args(self, body, name=None, description=None): if name: body["display_name"] = name if description: body["description"] = description return body def _get_resource_type(self, direction): if direction == nsx_constants.EGRESS: return nsx_constants.EGRESS_SHAPING return nsx_constants.INGRESS_SHAPING def _enable_shaping_in_args(self, body, burst_size=None, peak_bandwidth=None, average_bandwidth=None, direction=None): resource_type = self._get_resource_type(direction) for shaper in body["shaper_configuration"]: if shaper["resource_type"] == resource_type: 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, direction=None): resource_type = self._get_resource_type(direction) for shaper in body["shaper_configuration"]: if shaper["resource_type"] == resource_type: 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(self, tags, name=None, description=None): resource = 'switching-profiles' body = self._build_args(tags, name, description) return self.client.create(resource, body) def update(self, profile_id, tags, name=None, description=None): resource = 'switching-profiles/%s' % profile_id # get the current configuration body = self.get(profile_id) # update the relevant fields body = self._update_args(body, name, description) return self._update_resource_with_retry(resource, body) def update_shaping(self, profile_id, shaping_enabled=False, burst_size=None, peak_bandwidth=None, average_bandwidth=None, qos_marking=None, dscp=None, direction=nsx_constants.INGRESS): resource = 'switching-profiles/%s' % profile_id # get the current configuration body = self.get(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, direction=direction) else: body = self._disable_shaping_in_args(body, direction=direction) body = self._update_dscp_in_args(body, qos_marking, dscp) return self._update_resource_with_retry(resource, body) def get(self, profile_id): resource = 'switching-profiles/%s' % profile_id return self.client.get(resource) def delete(self, profile_id): resource = 'switching-profiles/%s' % profile_id self.client.delete(resource) class NsxLibLogicalRouter(utils.NsxLibApiBase): 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=self.cluster.nsx_api_managers, 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 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_advertisement(self, logical_router_id, **kwargs): resource = ('logical-routers/%s/routing/advertisement' % logical_router_id) return self._update_resource_with_retry(resource, kwargs) def get_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') class NsxLibEdgeCluster(utils.NsxLibApiBase): def get(self, edge_cluster_uuid): resource = "edge-clusters/%s" % edge_cluster_uuid return self.client.get(resource) class NsxLibTransportZone(utils.NsxLibApiBase): def get_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') class NsxLibDhcpProfile(utils.NsxLibApiBase): def get_id_by_name_or_id(self, name_or_id): """Get a dhcp profile by it's display name or uuid Return the dhcp profile data, or raise an exception if not found or not unique """ return self._get_resource_by_name_or_id(name_or_id, 'dhcp/server-profiles') class NsxLibMetadataProxy(utils.NsxLibApiBase): def get_id_by_name_or_id(self, name_or_id): """Get a metadata proxy by it's display name or uuid Return the metadata proxy data, or raise an exception if not found or not unique """ return self._get_resource_by_name_or_id(name_or_id, 'md-proxies') class NsxLibBridgeCluster(utils.NsxLibApiBase): def get_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')