def _lsn_port_configure_action(cluster, lsn_id, lsn_port_id, action, is_enabled, obj): nsxlib.do_request( HTTP_PUT, nsxlib._build_uri_path(LSERVICESNODE_RESOURCE, resource_id=lsn_id, extra_action=action), jsonutils.dumps({"enabled": is_enabled}), cluster=cluster, ) nsxlib.do_request( HTTP_PUT, nsxlib._build_uri_path( LSERVICESNODEPORT_RESOURCE, parent_resource_id=lsn_id, resource_id=lsn_port_id, extra_action=action ), jsonutils.dumps(obj), cluster=cluster, )
def create_lport(cluster, lswitch_uuid, tenant_id, neutron_port_id, display_name, device_id, admin_status_enabled, mac_address=None, fixed_ips=None, port_security_enabled=None, security_profiles=None, queue_id=None, mac_learning_enabled=None, allowed_address_pairs=None): """Creates a logical port on the assigned logical switch.""" display_name = utils.check_and_truncate(display_name) lport_obj = dict( admin_status_enabled=admin_status_enabled, display_name=display_name, tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id, vm_id=utils.device_id_to_vm_id(device_id)) ) _configure_extensions(lport_obj, mac_address, fixed_ips, port_security_enabled, security_profiles, queue_id, mac_learning_enabled, allowed_address_pairs) path = nsxlib._build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=lswitch_uuid) result = nsxlib.do_request(HTTP_POST, path, jsonutils.dumps(lport_obj), cluster=cluster) LOG.debug("Created logical port %(result)s on logical switch %(uuid)s", {'result': result['uuid'], 'uuid': lswitch_uuid}) return result
def test_cluster_method_not_implemented(self): self.assertRaises(api_exc.NsxApiException, nsxlib.do_request, nsxlib.HTTP_GET, nsxlib._build_uri_path('MY_FAKE_RESOURCE', resource_id='foo'), cluster=self.fake_cluster)
def create_lqueue(cluster, queue_data): params = { 'name': 'display_name', 'qos_marking': 'qos_marking', 'min': 'min_bandwidth_rate', 'max': 'max_bandwidth_rate', 'dscp': 'dscp' } queue_obj = dict( (nsx_name, queue_data.get(api_name)) for api_name, nsx_name in params.iteritems() if attr.is_attr_set(queue_data.get(api_name)) ) if 'display_name' in queue_obj: queue_obj['display_name'] = utils.check_and_truncate( queue_obj['display_name']) queue_obj['tags'] = utils.get_tags() try: return nsxlib.do_request(HTTP_POST, nsxlib._build_uri_path(LQUEUE_RESOURCE), jsonutils.dumps(queue_obj), cluster=cluster)['uuid'] except api_exc.NsxApiException: # FIXME(salv-orlando): This should not raise NeutronException with excutils.save_and_reraise_exception(): raise exception.NeutronException()
def test_build_uri_path_with_filters(self): filters = {"tag": 'foo', "tag_scope": "scope_foo"} result = nsxlib._build_uri_path('RESOURCE', filters=filters) expected = ( "%s/%s?tag_scope=scope_foo&tag=foo" % (nsxlib.URI_PREFIX, 'RESOURCE')) self.assertEqual(expected, result)
def update_security_profile(cluster, spid, name): return do_request(HTTP_PUT, _build_uri_path(SECPROF_RESOURCE, resource_id=spid), json.dumps({ "display_name": utils.check_and_truncate(name) }), cluster=cluster)
def plug_interface(cluster, lswitch_id, lport_id, att_obj): return nsxlib.do_request(HTTP_PUT, nsxlib._build_uri_path(LSWITCHPORT_RESOURCE, lport_id, lswitch_id, is_attachment=True), jsonutils.dumps(att_obj), cluster=cluster)
def create_l2_gw_service(cluster, tenant_id, display_name, devices): """Create a NSX Layer-2 Network Gateway Service. :param cluster: The target NSX cluster :param tenant_id: Identifier of the Openstack tenant for which the gateway service. :param display_name: Descriptive name of this gateway service :param devices: List of transport node uuids (and network interfaces on them) to use for the network gateway service :raise NsxApiException: if there is a problem while communicating with the NSX controller """ # NOTE(salvatore-orlando): This is a little confusing, but device_id in # NSX is actually the identifier a physical interface on the gateway # device, which in the Neutron API is referred as interface_name gateways = [{"transport_node_uuid": device['id'], "device_id": device['interface_name'], "type": "L2Gateway"} for device in devices] gwservice_obj = { "display_name": utils.check_and_truncate(display_name), "tags": utils.get_tags(os_tid=tenant_id), "gateways": gateways, "type": "L2GatewayServiceConfig" } return do_request( "POST", _build_uri_path(GWSERVICE_RESOURCE), json.dumps(gwservice_obj), cluster=cluster)
def query_lrouter_lports(cluster, lr_uuid, fields="*", filters=None, relations=None): uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lr_uuid, fields=fields, filters=filters, relations=relations) return nsxlib.do_request(HTTP_GET, uri, cluster=cluster)['results']
def get_port_by_neutron_tag(cluster, lswitch_uuid, neutron_port_id): """Get port by neutron tag. Returns the NSX UUID of the logical port with tag q_port_id equal to neutron_port_id or None if the port is not Found. """ uri = nsxlib._build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=lswitch_uuid, fields='uuid', filters={'tag': neutron_port_id, 'tag_scope': 'q_port_id'}) LOG.debug("Looking for port with q_port_id tag '%(neutron_port_id)s' " "on: '%(lswitch_uuid)s'", {'neutron_port_id': neutron_port_id, 'lswitch_uuid': lswitch_uuid}) res = nsxlib.do_request(HTTP_GET, uri, cluster=cluster) num_results = len(res["results"]) if num_results >= 1: if num_results > 1: LOG.warn(_LW("Found '%(num_ports)d' ports with " "q_port_id tag: '%(neutron_port_id)s'. " "Only 1 was expected."), {'num_ports': num_results, 'neutron_port_id': neutron_port_id}) return res["results"][0]
def get_lrouter(cluster, lrouter_id): return nsxlib.do_request(HTTP_GET, nsxlib._build_uri_path( LROUTER_RESOURCE, resource_id=lrouter_id, relations='LogicalRouterStatus'), cluster=cluster)
def query_lrouters(cluster, fields=None, filters=None): return nsxlib.get_all_query_pages( nsxlib._build_uri_path(LROUTER_RESOURCE, fields=fields, relations='LogicalRouterStatus', filters=filters), cluster)
def create_gateway_device( cluster, tenant_id, display_name, neutron_id, tz_uuid, connector_type, connector_ip, client_certificate ): body = _build_gateway_device_body( tenant_id, display_name, neutron_id, connector_type, connector_ip, client_certificate, tz_uuid ) return do_request(HTTP_POST, _build_uri_path(TRANSPORTNODE_RESOURCE), json.dumps(body), cluster=cluster)
def create_router_lport(cluster, lrouter_uuid, tenant_id, neutron_port_id, display_name, admin_status_enabled, ip_addresses, mac_address=None): """Creates a logical port on the assigned logical router.""" lport_obj = dict( admin_status_enabled=admin_status_enabled, display_name=display_name, tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id), ip_addresses=ip_addresses, type="LogicalRouterPortConfig" ) # Only add the mac_address to lport_obj if present. This is because # when creating the fake_ext_gw there is no mac_address present. if mac_address: lport_obj['mac_address'] = mac_address path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lrouter_uuid) result = nsxlib.do_request(HTTP_POST, path, jsonutils.dumps(lport_obj), cluster=cluster) LOG.debug("Created logical port %(lport_uuid)s on " "logical router %(lrouter_uuid)s", {'lport_uuid': result['uuid'], 'lrouter_uuid': lrouter_uuid}) return result
def get_lswitches(cluster, neutron_net_id): def lookup_switches_by_tag(): # Fetch extra logical switches lswitch_query_path = nsxlib._build_uri_path( LSWITCH_RESOURCE, fields="uuid,display_name,tags,lport_count", relations="LogicalSwitchStatus", filters={'tag': neutron_net_id, 'tag_scope': 'quantum_net_id'}) return nsxlib.get_all_query_pages(lswitch_query_path, cluster) lswitch_uri_path = nsxlib._build_uri_path(LSWITCH_RESOURCE, neutron_net_id, relations="LogicalSwitchStatus") results = [] try: ls = nsxlib.do_request(HTTP_GET, lswitch_uri_path, cluster=cluster) results.append(ls) for tag in ls['tags']: if (tag['scope'] == "multi_lswitch" and tag['tag'] == "True"): results.extend(lookup_switches_by_tag()) except exception.NotFound: # This is legit if the neutron network was created using # a post-Havana version of the plugin results.extend(lookup_switches_by_tag()) if results: return results else: raise exception.NetworkNotFound(net_id=neutron_net_id)
def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid, tenant_id, neutron_port_id, display_name, admin_status_enabled, ip_addresses): """Updates a logical port on the assigned logical router.""" lport_obj = dict( admin_status_enabled=admin_status_enabled, display_name=display_name, tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id), ip_addresses=ip_addresses, type="LogicalRouterPortConfig" ) # Do not pass null items to NSX for key in lport_obj.keys(): if lport_obj[key] is None: del lport_obj[key] path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lrouter_port_uuid, parent_resource_id=lrouter_uuid) result = nsxlib.do_request(HTTP_PUT, path, jsonutils.dumps(lport_obj), cluster=cluster) LOG.debug("Updated logical port %(lport_uuid)s on " "logical router %(lrouter_uuid)s", {'lport_uuid': lrouter_port_uuid, 'lrouter_uuid': lrouter_uuid}) return result
def plug_router_port_attachment(cluster, router_id, port_id, attachment_uuid, nsx_attachment_type, attachment_vlan=None): """Attach a router port to the given attachment. Current attachment types: - PatchAttachment [-> logical switch port uuid] - L3GatewayAttachment [-> L3GatewayService uuid] For the latter attachment type a VLAN ID can be specified as well. """ uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id, is_attachment=True) attach_obj = {} attach_obj["type"] = nsx_attachment_type if nsx_attachment_type == "PatchAttachment": attach_obj["peer_port_uuid"] = attachment_uuid elif nsx_attachment_type == "L3GatewayAttachment": attach_obj["l3_gateway_service_uuid"] = attachment_uuid if attachment_vlan: attach_obj['vlan_id'] = attachment_vlan else: raise nsx_exc.InvalidAttachmentType( attachment_type=nsx_attachment_type) return nsxlib.do_request( HTTP_PUT, uri, jsonutils.dumps(attach_obj), cluster=cluster)
def update_lrouter_port_ips(cluster, lrouter_id, lport_id, ips_to_add, ips_to_remove): uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id) try: port = nsxlib.do_request(HTTP_GET, uri, cluster=cluster) # TODO(salvatore-orlando): Enforce ips_to_add intersection with # ips_to_remove is empty ip_address_set = set(port['ip_addresses']) ip_address_set = ip_address_set - set(ips_to_remove) ip_address_set = ip_address_set | set(ips_to_add) # Set is not JSON serializable - convert to list port['ip_addresses'] = list(ip_address_set) nsxlib.do_request(HTTP_PUT, uri, jsonutils.dumps(port), cluster=cluster) except exception.NotFound: # FIXME(salv-orlando):avoid raising different exception data = {'lport_id': lport_id, 'lrouter_id': lrouter_id} msg = (_("Router Port %(lport_id)s not found on router " "%(lrouter_id)s") % data) LOG.exception(msg) raise nsx_exc.NsxPluginException(err_msg=msg) except api_exc.NsxApiException as e: msg = _("An exception occurred while updating IP addresses on a " "router logical port:%s") % str(e) LOG.exception(msg) raise nsx_exc.NsxPluginException(err_msg=msg)
def delete_router_lport(cluster, lrouter_uuid, lport_uuid): """Creates a logical port on the assigned logical router.""" path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_uuid, lrouter_uuid) nsxlib.do_request(HTTP_DELETE, path, cluster=cluster) LOG.debug( "Delete logical router port %(lport_uuid)s on " "logical router %(lrouter_uuid)s", {"lport_uuid": lport_uuid, "lrouter_uuid": lrouter_uuid}, )
def create_explicit_routing_lrouter(cluster, neutron_router_id, tenant_id, display_name, nexthop, distributed=None): lrouter_obj = _prepare_lrouter_body( display_name, neutron_router_id, tenant_id, "RoutingTableRoutingConfig", distributed=distributed ) router = do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE), jsonutils.dumps(lrouter_obj), cluster=cluster) default_gw = {"prefix": "0.0.0.0/0", "next_hop_ip": nexthop} create_explicit_route_lrouter(cluster, router["uuid"], default_gw) return router
def lsn_for_network_create(cluster, network_id): lsn_obj = { "edge_cluster_uuid": cluster.default_service_cluster_uuid, "tags": utils.get_tags(n_network_id=network_id), } return nsxlib.do_request( HTTP_POST, nsxlib._build_uri_path(LSERVICESNODE_RESOURCE), jsonutils.dumps(lsn_obj), cluster=cluster )["uuid"]
def delete_router_lport(cluster, lrouter_uuid, lport_uuid): """Creates a logical port on the assigned logical router.""" path = _build_uri_path(LROUTERPORT_RESOURCE, lport_uuid, lrouter_uuid) do_request(HTTP_DELETE, path, cluster=cluster) LOG.debug(_("Delete logical router port %(lport_uuid)s on " "logical router %(lrouter_uuid)s"), {'lport_uuid': lport_uuid, 'lrouter_uuid': lrouter_uuid})
def get_gateway_device_status(cluster, device_uuid): status_res = do_request(HTTP_GET, _build_uri_path(TRANSPORTNODE_RESOURCE, device_uuid, extra_action='status'), cluster=cluster) # Returns the connection status return status_res['connection']['connected']
def get_default_route_explicit_routing_lrouter_v33(cluster, router_id): static_filter = {"protocol": "static", "prefix": "0.0.0.0/0"} default_route = nsxlib.do_request( HTTP_GET, nsxlib._build_uri_path(LROUTERRIB_RESOURCE, filters=static_filter, fields="*", parent_resource_id=router_id), cluster=cluster, )["results"][0] return default_route
def get_explicit_routes_lrouter(cluster, router_id, protocol_type="static"): static_filter = {"protocol": protocol_type} existing_routes = nsxlib.do_request( HTTP_GET, nsxlib._build_uri_path(LROUTERRIB_RESOURCE, filters=static_filter, fields="*", parent_resource_id=router_id), cluster=cluster, )["results"] return existing_routes
def lsn_for_network_get(cluster, network_id): filters = {"tag": network_id, "tag_scope": "n_network_id"} results = nsxlib.do_request( HTTP_GET, nsxlib._build_uri_path(LSERVICESNODE_RESOURCE, fields="uuid", filters=filters), cluster=cluster )["results"] if not results: raise exception.NotFound() elif len(results) == 1: return results[0]["uuid"]
def get_lswitch_by_id(cluster, lswitch_id): try: lswitch_uri_path = nsxlib._build_uri_path( LSWITCH_RESOURCE, lswitch_id, relations="LogicalSwitchStatus") return nsxlib.do_request(HTTP_GET, lswitch_uri_path, cluster=cluster) except exception.NotFound: # FIXME(salv-orlando): this should not raise a neutron exception raise exception.NetworkNotFound(net_id=lswitch_id)
def _lsn_configure_action(cluster, lsn_id, action, is_enabled, obj): lsn_obj = {"enabled": is_enabled} lsn_obj.update(obj) nsxlib.do_request( HTTP_PUT, nsxlib._build_uri_path(LSERVICESNODE_RESOURCE, resource_id=lsn_id, extra_action=action), jsonutils.dumps(lsn_obj), cluster=cluster, )
def query_lswitch_lports(cluster, ls_uuid, fields="*", filters=None, relations=None): # Fix filter for attachments if filters and "attachment" in filters: filters['attachment_vif_uuid'] = filters["attachment"] del filters['attachment'] uri = _build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=ls_uuid, fields=fields, filters=filters, relations=relations) return do_request(HTTP_GET, uri, cluster=cluster)['results']
def lookup_switches_by_tag(): # Fetch extra logical switches lswitch_query_path = nsxlib._build_uri_path( LSWITCH_RESOURCE, fields="uuid,display_name,tags,lport_count", relations="LogicalSwitchStatus", filters={'tag': neutron_net_id, 'tag_scope': 'quantum_net_id'}) return nsxlib.get_all_query_pages(lswitch_query_path, cluster)
def update_gateway_device(cluster, gateway_id, tenant_id, display_name, neutron_id, tz_uuid, connector_type, connector_ip, client_certificate): body = _build_gateway_device_body(tenant_id, display_name, neutron_id, connector_type, connector_ip, client_certificate, tz_uuid) try: return nsxlib.do_request(HTTP_PUT, nsxlib._build_uri_path( TRANSPORTNODE_RESOURCE, resource_id=gateway_id), jsonutils.dumps(body, sort_keys=True), cluster=cluster) except api_exc.InvalidSecurityCertificate: raise nsx_exc.InvalidSecurityCertificate()
def update_default_gw_explicit_routing_lrouter(cluster, router_id, next_hop): default_route = get_default_route_explicit_routing_lrouter(cluster, router_id) if next_hop != default_route["next_hop_ip"]: new_default_route = {"action": "accept", "next_hop_ip": next_hop, "prefix": "0.0.0.0/0", "protocol": "static"} nsxlib.do_request(HTTP_PUT, nsxlib._build_uri_path( LROUTERRIB_RESOURCE, resource_id=default_route['uuid'], parent_resource_id=router_id), jsonutils.dumps(new_default_route), cluster=cluster)
def create_explicit_route_lrouter(cluster, router_id, route): next_hop_ip = route.get("nexthop") or route.get("next_hop_ip") prefix = route.get("destination") or route.get("prefix") uuid = nsxlib.do_request( HTTP_POST, nsxlib._build_uri_path(LROUTERRIB_RESOURCE, parent_resource_id=router_id), jsonutils.dumps({ "action": "accept", "next_hop_ip": next_hop_ip, "prefix": prefix, "protocol": "static" }), cluster=cluster)['uuid'] return uuid
def _create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id, display_name, nexthop, distributed=None): implicit_routing_config = { "default_route_next_hop": { "gateway_ip_address": nexthop, "type": "RouterNextHop" }, } lrouter_obj = _prepare_lrouter_body( display_name, neutron_router_id, tenant_id, "SingleDefaultRouteImplicitRoutingConfig", distributed=distributed, **implicit_routing_config) return nsxlib.do_request(HTTP_POST, nsxlib._build_uri_path(LROUTER_RESOURCE), jsonutils.dumps(lrouter_obj), cluster=cluster)
def update_implicit_routing_lrouter(cluster, r_id, display_name, nexthop): lrouter_obj = get_lrouter(cluster, r_id) if not display_name and not nexthop: # Nothing to update return lrouter_obj # It seems that this is faster than the doing an if on display_name lrouter_obj["display_name"] = (utils.check_and_truncate(display_name) or lrouter_obj["display_name"]) if nexthop: nh_element = lrouter_obj["routing_config"].get( "default_route_next_hop") if nh_element: nh_element["gateway_ip_address"] = nexthop return do_request(HTTP_PUT, _build_uri_path(LROUTER_RESOURCE, resource_id=r_id), jsonutils.dumps(lrouter_obj), cluster=cluster)
def lsn_port_create(cluster, lsn_id, port_data): port_obj = { "ip_address": port_data["ip_address"], "mac_address": port_data["mac_address"], "tags": utils.get_tags(n_mac_address=port_data["mac_address"], n_subnet_id=port_data["subnet_id"]), "type": "LogicalServicesNodePortConfig", } return do_request(HTTP_POST, _build_uri_path(LSERVICESNODEPORT_RESOURCE, parent_resource_id=lsn_id), json.dumps(port_obj), cluster=cluster)["uuid"]
def create_explicit_routing_lrouter(cluster, neutron_router_id, tenant_id, display_name, nexthop, distributed=None): lrouter_obj = _prepare_lrouter_body(display_name, neutron_router_id, tenant_id, "RoutingTableRoutingConfig", distributed=distributed) router = nsxlib.do_request(HTTP_POST, nsxlib._build_uri_path(LROUTER_RESOURCE), jsonutils.dumps(lrouter_obj), cluster=cluster) default_gw = {'prefix': '0.0.0.0/0', 'next_hop_ip': nexthop} create_explicit_route_lrouter(cluster, router['uuid'], default_gw) return router
def update_lswitch(cluster, lswitch_id, display_name, tenant_id=None, **kwargs): uri = nsxlib._build_uri_path(LSWITCH_RESOURCE, resource_id=lswitch_id) lswitch_obj = {"display_name": utils.check_and_truncate(display_name)} # NOTE: tag update will not 'merge' existing tags with new ones. tags = [] if tenant_id: tags = utils.get_tags(os_tid=tenant_id) # The 'tags' kwarg might existing and be None tags.extend(kwargs.get('tags') or []) if tags: lswitch_obj['tags'] = tags try: return nsxlib.do_request(HTTP_PUT, uri, jsonutils.dumps(lswitch_obj), cluster=cluster) except exception.NotFound as e: LOG.error(_LE("Network not found, Error: %s"), str(e)) raise exception.NetworkNotFound(net_id=lswitch_id)
def lsn_port_plug_network(cluster, lsn_id, lsn_port_id, lswitch_port_id): patch_obj = {"type": "PatchAttachment", "peer_port_uuid": lswitch_port_id} try: do_request(HTTP_PUT, _build_uri_path(LSERVICESNODEPORT_RESOURCE, parent_resource_id=lsn_id, resource_id=lsn_port_id, is_attachment=True), json.dumps(patch_obj), cluster=cluster) except api_exc.Conflict: # This restriction might be lifted at some point msg = (_("Attempt to plug Logical Services Node %(lsn)s into " "network with port %(port)s failed. PatchAttachment " "already exists with another port") % { 'lsn': lsn_id, 'port': lswitch_port_id }) LOG.exception(msg) raise nsx_exc.LsnConfigurationConflict(lsn_id=lsn_id)
def update_lswitch(cluster, lswitch_id, display_name, tenant_id=None, **kwargs): uri = _build_uri_path(LSWITCH_RESOURCE, resource_id=lswitch_id) lswitch_obj = { "display_name": utils.check_and_truncate(display_name), "tags": utils.get_tags(os_tid=tenant_id) } if "tags" in kwargs: lswitch_obj["tags"].extend(kwargs["tags"]) try: return do_request(HTTP_PUT, uri, json.dumps(lswitch_obj), cluster=cluster) except exception.NotFound as e: LOG.error(_("Network not found, Error: %s"), str(e)) raise exception.NetworkNotFound(net_id=lswitch_id)
def _test_create_lrouter_dnat_rule(self, version): with mock.patch.object(self.fake_cluster.api_client, 'get_version', new=lambda: Version(version)): tenant_id = 'pippo' lrouter = routerlib.create_lrouter(self.fake_cluster, uuidutils.generate_uuid(), tenant_id, 'fake_router', '192.168.0.1') nat_rule = routerlib.create_lrouter_dnat_rule( self.fake_cluster, lrouter['uuid'], '10.0.0.99', match_criteria={'destination_ip_addresses': '192.168.0.5'}) uri = nsxlib._build_uri_path(routerlib.LROUTERNAT_RESOURCE, nat_rule['uuid'], lrouter['uuid']) resp_obj = nsxlib.do_request("GET", uri, cluster=self.fake_cluster) self.assertEqual('DestinationNatRule', resp_obj['type']) self.assertEqual('192.168.0.5', resp_obj['match']['destination_ip_addresses'])
def create_lport(cluster, lswitch_uuid, tenant_id, neutron_port_id, display_name, device_id, admin_status_enabled, mac_address=None, fixed_ips=None, port_security_enabled=None, security_profiles=None, queue_id=None, mac_learning_enabled=None, allowed_address_pairs=None): """Creates a logical port on the assigned logical switch.""" display_name = utils.check_and_truncate(display_name) lport_obj = dict(admin_status_enabled=admin_status_enabled, display_name=display_name, tags=utils.get_tags( os_tid=tenant_id, q_port_id=neutron_port_id, vm_id=utils.device_id_to_vm_id(device_id))) _configure_extensions(lport_obj, mac_address, fixed_ips, port_security_enabled, security_profiles, queue_id, mac_learning_enabled, allowed_address_pairs) path = _build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=lswitch_uuid) result = do_request(HTTP_POST, path, json.dumps(lport_obj), cluster=cluster) LOG.debug(_("Created logical port %(result)s on logical switch %(uuid)s"), { 'result': result['uuid'], 'uuid': lswitch_uuid }) return result
def test_update_security_profile_rules(self): sec_prof = secgrouplib.create_security_profile( self.fake_cluster, _uuid(), 'pippo', {'name': 'test'}) ingress_rule = {'ethertype': 'IPv4'} egress_rule = {'ethertype': 'IPv4', 'profile_uuid': 'xyz'} new_rules = {'logical_port_egress_rules': [egress_rule], 'logical_port_ingress_rules': [ingress_rule]} secgrouplib.update_security_group_rules( self.fake_cluster, sec_prof['uuid'], new_rules) sec_prof_res = nsxlib.do_request( nsxlib.HTTP_GET, nsxlib._build_uri_path('security-profile', resource_id=sec_prof['uuid']), cluster=self.fake_cluster) self.assertEqual(sec_prof['uuid'], sec_prof_res['uuid']) # Check for builtin rules self.assertEqual(len(sec_prof_res['logical_port_egress_rules']), 2) self.assertIn(egress_rule, sec_prof_res['logical_port_egress_rules']) self.assertEqual(len(sec_prof_res['logical_port_ingress_rules']), 1) self.assertIn(ingress_rule, sec_prof_res['logical_port_ingress_rules'])
def create_lswitch(cluster, neutron_net_id, tenant_id, display_name, transport_zones_config, shared=None, **kwargs): # The tag scope adopts a slightly different naming convention for # historical reasons lswitch_obj = {"display_name": utils.check_and_truncate(display_name), "transport_zones": transport_zones_config, "replication_mode": cfg.CONF.NSX.replication_mode, "tags": utils.get_tags(os_tid=tenant_id, quantum_net_id=neutron_net_id)} # TODO(salv-orlando): Now that we have async status synchronization # this tag is perhaps not needed anymore if shared: lswitch_obj["tags"].append({"tag": "true", "scope": "shared"}) if "tags" in kwargs: lswitch_obj["tags"].extend(kwargs["tags"]) uri = nsxlib._build_uri_path(LSWITCH_RESOURCE) lswitch = nsxlib.do_request(HTTP_POST, uri, jsonutils.dumps(lswitch_obj), cluster=cluster) LOG.debug("Created logical switch: %s", lswitch['uuid']) return lswitch
def test_plug_l2_gw_port_attachment(self): tenant_id = 'pippo' node_uuid = _uuid() transport_zones_config = [{'zone_uuid': _uuid(), 'transport_type': 'stt'}] lswitch = switchlib.create_lswitch( self.fake_cluster, _uuid(), tenant_id, 'fake-switch', transport_zones_config) gw_id = self._create_gw_service(node_uuid, 'fake-gw')['uuid'] lport = switchlib.create_lport( self.fake_cluster, lswitch['uuid'], tenant_id, _uuid(), 'fake-gw-port', gw_id, True) l2gwlib.plug_l2_gw_service( self.fake_cluster, lswitch['uuid'], lport['uuid'], gw_id) uri = nsxlib._build_uri_path(switchlib.LSWITCHPORT_RESOURCE, lport['uuid'], lswitch['uuid'], is_attachment=True) resp_obj = nsxlib.do_request("GET", uri, cluster=self.fake_cluster) self.assertIn('LogicalPortAttachment', resp_obj) self.assertEqual(resp_obj['LogicalPortAttachment']['type'], 'L2GatewayAttachment')
def lsn_port_delete(cluster, lsn_id, lsn_port_id): return do_request(HTTP_DELETE, _build_uri_path(LSERVICESNODEPORT_RESOURCE, parent_resource_id=lsn_id, resource_id=lsn_port_id), cluster=cluster)
def query_nat_rules(cluster, router_id, fields="*", filters=None): uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id, fields=fields, filters=filters) return nsxlib.get_all_query_pages(uri, cluster)
def query_security_profiles(cluster, fields=None, filters=None): return nsxlib.get_all_query_pages( nsxlib._build_uri_path(SECPROF_RESOURCE, fields=fields, filters=filters), cluster)
def test_build_uri_path_plain(self): result = nsxlib._build_uri_path('RESOURCE') self.assertEqual("%s/%s" % (nsxlib.URI_PREFIX, 'RESOURCE'), result)
def delete_l2_gw_service(cluster, gateway_id): nsxlib.do_request(HTTP_DELETE, nsxlib._build_uri_path(GWSERVICE_RESOURCE, resource_id=gateway_id), cluster=cluster)
def get_l2_gw_service(cluster, gateway_id): return nsxlib.do_request(HTTP_GET, nsxlib._build_uri_path(GWSERVICE_RESOURCE, resource_id=gateway_id), cluster=cluster)
def test_build_uri_path_with_resource_id(self): res = 'RESOURCE' res_id = 'resource_id' result = nsxlib._build_uri_path(res, resource_id=res_id) expected = "%s/%s/%s" % (nsxlib.URI_PREFIX, res, res_id) self.assertEqual(expected, result)
def test_build_uri_path_with_filters(self): filters = {"tag": 'foo', "tag_scope": "scope_foo"} result = nsxlib._build_uri_path('RESOURCE', filters=filters) expected = ("%s/%s?tag_scope=scope_foo&tag=foo" % (nsxlib.URI_PREFIX, 'RESOURCE')) self.assertEqual(expected, result)
def test_build_uri_path_with_field(self): result = nsxlib._build_uri_path('RESOURCE', fields='uuid') expected = "%s/%s?fields=uuid" % (nsxlib.URI_PREFIX, 'RESOURCE') self.assertEqual(expected, result)
def get_l2_gw_service(cluster, gateway_id): return do_request( "GET", _build_uri_path(GWSERVICE_RESOURCE, resource_id=gateway_id), cluster=cluster)
def delete_l2_gw_service(cluster, gateway_id): do_request("DELETE", _build_uri_path(GWSERVICE_RESOURCE, resource_id=gateway_id), cluster=cluster)
def delete_gateway_device(cluster, device_uuid): return nsxlib.do_request(HTTP_DELETE, nsxlib._build_uri_path(TRANSPORTNODE_RESOURCE, device_uuid), cluster=cluster)
def update_security_profile(cluster, spid, name): return nsxlib.do_request( HTTP_PUT, nsxlib._build_uri_path(SECPROF_RESOURCE, resource_id=spid), jsonutils.dumps({"display_name": utils.check_and_truncate(name)}), cluster=cluster)
class NsxSynchronizer(): LS_URI = nsxlib._build_uri_path(switchlib.LSWITCH_RESOURCE, fields='uuid,tags,fabric_status', relations='LogicalSwitchStatus') LR_URI = nsxlib._build_uri_path(routerlib.LROUTER_RESOURCE, fields='uuid,tags,fabric_status', relations='LogicalRouterStatus') LP_URI = nsxlib._build_uri_path(switchlib.LSWITCHPORT_RESOURCE, parent_resource_id='*', fields='uuid,tags,fabric_status_up', relations='LogicalPortStatus') def __init__(self, plugin, cluster, state_sync_interval, req_delay, min_chunk_size, max_rand_delay=0): random.seed() self._nsx_cache = NsxCache() # Store parameters as instance members # NOTE(salv-orlando): apologies if it looks java-ish self._plugin = plugin self._cluster = cluster self._req_delay = req_delay self._sync_interval = state_sync_interval self._max_rand_delay = max_rand_delay # Validate parameters if self._sync_interval < self._req_delay: err_msg = (_("Minimum request delay:%(req_delay)s must not " "exceed synchronization interval:%(sync_interval)s") % { 'req_delay': self._req_delay, 'sync_interval': self._sync_interval }) LOG.error(err_msg) raise nsx_exc.NsxPluginException(err_msg=err_msg) # Backoff time in case of failures while fetching sync data self._sync_backoff = 1 # Store the looping call in an instance variable to allow unit tests # for controlling its lifecycle self._sync_looping_call = _start_loopingcall(min_chunk_size, state_sync_interval, self._synchronize_state) def _get_tag_dict(self, tags): return dict((tag.get('scope'), tag['tag']) for tag in tags) def synchronize_network(self, context, neutron_network_data, lswitches=None): """Synchronize a Neutron network with its NSX counterpart. This routine synchronizes a set of switches when a Neutron network is mapped to multiple lswitches. """ if not lswitches: # Try to get logical switches from nsx try: lswitches = nsx_utils.fetch_nsx_switches( context.session, self._cluster, neutron_network_data['id']) except exceptions.NetworkNotFound: # TODO(salv-orlando): We should be catching # api_exc.ResourceNotFound here # The logical switch was not found LOG.warning( _("Logical switch for neutron network %s not " "found on NSX."), neutron_network_data['id']) lswitches = [] else: for lswitch in lswitches: self._nsx_cache.update_lswitch(lswitch) # By default assume things go wrong status = constants.NET_STATUS_ERROR # In most cases lswitches will contain a single element for ls in lswitches: if not ls: # Logical switch was deleted break ls_status = ls['_relations']['LogicalSwitchStatus'] if not ls_status['fabric_status']: status = constants.NET_STATUS_DOWN break else: # No switch was down or missing. Set status to ACTIVE unless # there were no switches in the first place! if lswitches: status = constants.NET_STATUS_ACTIVE # Update db object if status == neutron_network_data['status']: # do nothing return with context.session.begin(subtransactions=True): try: network = self._plugin._get_network(context, neutron_network_data['id']) except exc.NoResultFound: pass else: network.status = status LOG.debug( _("Updating status for neutron resource %(q_id)s to:" " %(status)s"), { 'q_id': neutron_network_data['id'], 'status': status }) def _synchronize_lswitches(self, ctx, ls_uuids, scan_missing=False): if not ls_uuids and not scan_missing: return neutron_net_ids = set() neutron_nsx_mappings = {} # TODO(salvatore-orlando): Deal with the case the tag # has been tampered with for ls_uuid in ls_uuids: # If the lswitch has been deleted, get backup copy of data lswitch = (self._nsx_cache[ls_uuid].get('data') or self._nsx_cache[ls_uuid].get('data_bk')) tags = self._get_tag_dict(lswitch['tags']) neutron_id = tags.get('quantum_net_id') neutron_net_ids.add(neutron_id) neutron_nsx_mappings[neutron_id] = ( neutron_nsx_mappings.get(neutron_id, []) + [self._nsx_cache[ls_uuid]]) # Fetch neutron networks from database filters = {'router:external': [False]} if not scan_missing: filters['id'] = neutron_net_ids networks = self._plugin._get_collection( ctx, models_v2.Network, self._plugin._make_network_dict, filters=filters) for network in networks: lswitches = neutron_nsx_mappings.get(network['id'], []) lswitches = [lswitch.get('data') for lswitch in lswitches] self.synchronize_network(ctx, network, lswitches) def synchronize_router(self, context, neutron_router_data, lrouter=None): """Synchronize a neutron router with its NSX counterpart.""" if not lrouter: # Try to get router from nsx try: # This query will return the logical router status too nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self._cluster, neutron_router_data['id']) if nsx_router_id: lrouter = routerlib.get_lrouter(self._cluster, nsx_router_id) except exceptions.NotFound: # NOTE(salv-orlando): We should be catching # api_exc.ResourceNotFound here # The logical router was not found LOG.warning( _("Logical router for neutron router %s not " "found on NSX."), neutron_router_data['id']) if lrouter: # Update the cache self._nsx_cache.update_lrouter(lrouter) # Note(salv-orlando): It might worth adding a check to verify neutron # resource tag in nsx entity matches a Neutron id. # By default assume things go wrong status = constants.NET_STATUS_ERROR if lrouter: lr_status = ( lrouter['_relations']['LogicalRouterStatus']['fabric_status']) status = (lr_status and constants.NET_STATUS_ACTIVE or constants.NET_STATUS_DOWN) # Update db object if status == neutron_router_data['status']: # do nothing return with context.session.begin(subtransactions=True): try: router = self._plugin._get_router(context, neutron_router_data['id']) except exc.NoResultFound: pass else: router.status = status LOG.debug( _("Updating status for neutron resource %(q_id)s to:" " %(status)s"), { 'q_id': neutron_router_data['id'], 'status': status }) def _synchronize_lrouters(self, ctx, lr_uuids, scan_missing=False): if not lr_uuids and not scan_missing: return # TODO(salvatore-orlando): Deal with the case the tag # has been tampered with neutron_router_mappings = {} for lr_uuid in lr_uuids: lrouter = (self._nsx_cache[lr_uuid].get('data') or self._nsx_cache[lr_uuid].get('data_bk')) tags = self._get_tag_dict(lrouter['tags']) neutron_router_id = tags.get('q_router_id') if neutron_router_id: neutron_router_mappings[neutron_router_id] = ( self._nsx_cache[lr_uuid]) else: LOG.warn( _("Unable to find Neutron router id for " "NSX logical router: %s"), lr_uuid) # Fetch neutron routers from database filters = ({} if scan_missing else { 'id': neutron_router_mappings.keys() }) routers = self._plugin._get_collection(ctx, l3_db.Router, self._plugin._make_router_dict, filters=filters) for router in routers: lrouter = neutron_router_mappings.get(router['id']) self.synchronize_router(ctx, router, lrouter and lrouter.get('data')) def synchronize_port(self, context, neutron_port_data, lswitchport=None, ext_networks=None): """Synchronize a Neutron port with its NSX counterpart.""" # Skip synchronization for ports on external networks if not ext_networks: ext_networks = [ net['id'] for net in context.session.query(models_v2.Network).join( external_net_db.ExternalNetwork, ( models_v2.Network.id == external_net_db.ExternalNetwork.network_id)) ] if neutron_port_data['network_id'] in ext_networks: with context.session.begin(subtransactions=True): neutron_port_data['status'] = constants.PORT_STATUS_ACTIVE return if not lswitchport: # Try to get port from nsx try: ls_uuid, lp_uuid = nsx_utils.get_nsx_switch_and_port_id( context.session, self._cluster, neutron_port_data['id']) if lp_uuid: lswitchport = switchlib.get_port( self._cluster, ls_uuid, lp_uuid, relations='LogicalPortStatus') except (exceptions.PortNotFoundOnNetwork): # NOTE(salv-orlando): We should be catching # api_exc.ResourceNotFound here instead # of PortNotFoundOnNetwork when the id exists but # the logical switch port was not found LOG.warning( _("Logical switch port for neutron port %s " "not found on NSX."), neutron_port_data['id']) lswitchport = None else: # If lswitchport is not None, update the cache. # It could be none if the port was deleted from the backend if lswitchport: self._nsx_cache.update_lswitchport(lswitchport) # Note(salv-orlando): It might worth adding a check to verify neutron # resource tag in nsx entity matches Neutron id. # By default assume things go wrong status = constants.PORT_STATUS_ERROR if lswitchport: lp_status = (lswitchport['_relations']['LogicalPortStatus'] ['fabric_status_up']) status = (lp_status and constants.PORT_STATUS_ACTIVE or constants.PORT_STATUS_DOWN) # Update db object if status == neutron_port_data['status']: # do nothing return with context.session.begin(subtransactions=True): try: port = self._plugin._get_port(context, neutron_port_data['id']) except exc.NoResultFound: pass else: port.status = status LOG.debug( _("Updating status for neutron resource %(q_id)s to:" " %(status)s"), { 'q_id': neutron_port_data['id'], 'status': status }) def _synchronize_lswitchports(self, ctx, lp_uuids, scan_missing=False): if not lp_uuids and not scan_missing: return # Find Neutron port id by tag - the tag is already # loaded in memory, no reason for doing a db query # TODO(salvatore-orlando): Deal with the case the tag # has been tampered with neutron_port_mappings = {} for lp_uuid in lp_uuids: lport = (self._nsx_cache[lp_uuid].get('data') or self._nsx_cache[lp_uuid].get('data_bk')) tags = self._get_tag_dict(lport['tags']) neutron_port_id = tags.get('q_port_id') if neutron_port_id: neutron_port_mappings[neutron_port_id] = ( self._nsx_cache[lp_uuid]) # Fetch neutron ports from database # At the first sync we need to fetch all ports filters = ({} if scan_missing else { 'id': neutron_port_mappings.keys() }) # TODO(salv-orlando): Work out a solution for avoiding # this query ext_nets = [ net['id'] for net in ctx.session.query(models_v2.Network).join( external_net_db.ExternalNetwork, ( models_v2.Network.id == external_net_db.ExternalNetwork.network_id)) ] ports = self._plugin._get_collection(ctx, models_v2.Port, self._plugin._make_port_dict, filters=filters) for port in ports: lswitchport = neutron_port_mappings.get(port['id']) self.synchronize_port(ctx, port, lswitchport and lswitchport.get('data'), ext_networks=ext_nets) def _get_chunk_size(self, sp): # NOTE(salv-orlando): Try to use __future__ for this routine only? ratio = ((float(sp.total_size) / float(sp.chunk_size)) / (float(self._sync_interval) / float(self._req_delay))) new_size = max(1.0, ratio) * float(sp.chunk_size) return int(new_size) + (new_size - int(new_size) > 0) def _fetch_data(self, uri, cursor, page_size): # If not cursor there is nothing to retrieve if cursor: if cursor == 'start': cursor = None # Chunk size tuning might, in some conditions, make it larger # than 5,000, which is the maximum page size allowed by the NSX # API. In this case the request should be split in multiple # requests. This is not ideal, and therefore a log warning will # be emitted. num_requests = page_size / (MAX_PAGE_SIZE + 1) + 1 if num_requests > 1: LOG.warn( _("Requested page size is %(cur_chunk_size)d." "It might be necessary to do %(num_requests)d " "round-trips to NSX for fetching data. Please " "tune sync parameters to ensure chunk size " "is less than %(max_page_size)d"), { 'cur_chunk_size': page_size, 'num_requests': num_requests, 'max_page_size': MAX_PAGE_SIZE }) # Only the first request might return the total size, # subsequent requests will definetely not results, cursor, total_size = nsxlib.get_single_query_page( uri, self._cluster, cursor, min(page_size, MAX_PAGE_SIZE)) for _req in range(num_requests - 1): # If no cursor is returned break the cycle as there is no # actual need to perform multiple requests (all fetched) # This happens when the overall size of resources exceeds # the maximum page size, but the number for each single # resource type is below this threshold if not cursor: break req_results, cursor = nsxlib.get_single_query_page( uri, self._cluster, cursor, min(page_size, MAX_PAGE_SIZE))[:2] results.extend(req_results) # reset cursor before returning if we queried just to # know the number of entities return results, cursor if page_size else 'start', total_size return [], cursor, None def _fetch_nsx_data_chunk(self, sp): base_chunk_size = sp.chunk_size chunk_size = base_chunk_size + sp.extra_chunk_size LOG.info(_("Fetching up to %s resources " "from NSX backend"), chunk_size) fetched = ls_count = lr_count = lp_count = 0 lswitches = lrouters = lswitchports = [] if sp.ls_cursor or sp.ls_cursor == 'start': (lswitches, sp.ls_cursor, ls_count) = self._fetch_data(self.LS_URI, sp.ls_cursor, chunk_size) fetched = len(lswitches) if fetched < chunk_size and sp.lr_cursor or sp.lr_cursor == 'start': (lrouters, sp.lr_cursor, lr_count) = self._fetch_data(self.LR_URI, sp.lr_cursor, max(chunk_size - fetched, 0)) fetched += len(lrouters) if fetched < chunk_size and sp.lp_cursor or sp.lp_cursor == 'start': (lswitchports, sp.lp_cursor, lp_count) = self._fetch_data(self.LP_URI, sp.lp_cursor, max(chunk_size - fetched, 0)) fetched += len(lswitchports) if sp.current_chunk == 0: # No cursors were provided. Then it must be possible to # calculate the total amount of data to fetch sp.total_size = ls_count + lr_count + lp_count LOG.debug(_("Total data size: %d"), sp.total_size) sp.chunk_size = self._get_chunk_size(sp) # Calculate chunk size adjustment sp.extra_chunk_size = sp.chunk_size - base_chunk_size LOG.debug( _("Fetched %(num_lswitches)d logical switches, " "%(num_lswitchports)d logical switch ports," "%(num_lrouters)d logical routers"), { 'num_lswitches': len(lswitches), 'num_lswitchports': len(lswitchports), 'num_lrouters': len(lrouters) }) return (lswitches, lrouters, lswitchports) def _synchronize_state(self, sp): # If the plugin has been destroyed, stop the LoopingCall if not self._plugin: raise loopingcall.LoopingCallDone start = timeutils.utcnow() # Reset page cursor variables if necessary if sp.current_chunk == 0: sp.ls_cursor = sp.lr_cursor = sp.lp_cursor = 'start' LOG.info(_("Running state synchronization task. Chunk: %s"), sp.current_chunk) # Fetch chunk_size data from NSX try: (lswitches, lrouters, lswitchports) = (self._fetch_nsx_data_chunk(sp)) except (api_exc.RequestTimeout, api_exc.NsxApiException): sleep_interval = self._sync_backoff # Cap max back off to 64 seconds self._sync_backoff = min(self._sync_backoff * 2, 64) LOG.exception( _("An error occurred while communicating with " "NSX backend. Will retry synchronization " "in %d seconds"), sleep_interval) return sleep_interval LOG.debug(_("Time elapsed querying NSX: %s"), timeutils.utcnow() - start) if sp.total_size: num_chunks = ((sp.total_size / sp.chunk_size) + (sp.total_size % sp.chunk_size != 0)) else: num_chunks = 1 LOG.debug(_("Number of chunks: %d"), num_chunks) # Find objects which have changed on NSX side and need # to be synchronized (ls_uuids, lr_uuids, lp_uuids) = self._nsx_cache.process_updates(lswitches, lrouters, lswitchports) # Process removed objects only at the last chunk scan_missing = (sp.current_chunk == num_chunks - 1 and not sp.init_sync_performed) if sp.current_chunk == num_chunks - 1: self._nsx_cache.process_deletes() ls_uuids = self._nsx_cache.get_lswitches( changed_only=not scan_missing) lr_uuids = self._nsx_cache.get_lrouters( changed_only=not scan_missing) lp_uuids = self._nsx_cache.get_lswitchports( changed_only=not scan_missing) LOG.debug(_("Time elapsed hashing data: %s"), timeutils.utcnow() - start) # Get an admin context ctx = context.get_admin_context() # Synchronize with database self._synchronize_lswitches(ctx, ls_uuids, scan_missing=scan_missing) self._synchronize_lrouters(ctx, lr_uuids, scan_missing=scan_missing) self._synchronize_lswitchports(ctx, lp_uuids, scan_missing=scan_missing) # Increase chunk counter LOG.info( _("Synchronization for chunk %(chunk_num)d of " "%(total_chunks)d performed"), { 'chunk_num': sp.current_chunk + 1, 'total_chunks': num_chunks }) sp.current_chunk = (sp.current_chunk + 1) % num_chunks added_delay = 0 if sp.current_chunk == 0: # Ensure init_sync_performed is True if not sp.init_sync_performed: sp.init_sync_performed = True # Add additional random delay added_delay = random.randint(0, self._max_rand_delay) LOG.debug(_("Time elapsed at end of sync: %s"), timeutils.utcnow() - start) return self._sync_interval / num_chunks + added_delay
def lsn_delete(cluster, lsn_id): do_request(HTTP_DELETE, _build_uri_path(LSERVICESNODE_RESOURCE, resource_id=lsn_id), cluster=cluster)