From 13f9c5c372d052d630484afa44f6555fc4117326 Mon Sep 17 00:00:00 2001 From: armando-migliaccio Date: Wed, 15 Jan 2014 17:18:54 -0800 Subject: [PATCH] Rename Router related methods for VMware NSX plugin This is another step for the renaming/refactoring of nvplib and related modules. This is about routers. Partial-implements blueprint: nicira-plugin-renaming Change-Id: Ic69b2777fa1ae3125b8adf23943360e3fe18e4c2 --- neutron/plugins/nicira/NeutronPlugin.py | 157 ++- .../plugins/nicira/NeutronServicePlugin.py | 19 +- neutron/plugins/nicira/common/nsx_utils.py | 3 +- neutron/plugins/nicira/common/sync.py | 5 +- neutron/plugins/nicira/nsxlib/__init__.py | 16 - neutron/plugins/nicira/nsxlib/router.py | 676 +++++++++++++ neutron/plugins/nicira/nsxlib/versioning.py | 66 ++ neutron/plugins/nicira/nvplib.py | 698 ------------- .../tests/unit/nicira/nsxlib/test_router.py | 922 +++++++++++++++++ .../unit/nicira/nsxlib/test_versioning.py | 57 ++ .../tests/unit/nicira/test_nicira_plugin.py | 11 +- neutron/tests/unit/nicira/test_nsx_utils.py | 6 +- neutron/tests/unit/nicira/test_nvplib.py | 923 +----------------- 13 files changed, 1829 insertions(+), 1730 deletions(-) create mode 100644 neutron/plugins/nicira/nsxlib/router.py create mode 100644 neutron/plugins/nicira/nsxlib/versioning.py create mode 100644 neutron/tests/unit/nicira/nsxlib/test_router.py create mode 100644 neutron/tests/unit/nicira/nsxlib/test_versioning.py diff --git a/neutron/plugins/nicira/NeutronPlugin.py b/neutron/plugins/nicira/NeutronPlugin.py index c4ad96bea..df1674595 100644 --- a/neutron/plugins/nicira/NeutronPlugin.py +++ b/neutron/plugins/nicira/NeutronPlugin.py @@ -77,6 +77,7 @@ from neutron.plugins.nicira.extensions import maclearning as mac_ext from neutron.plugins.nicira.extensions import nvp_networkgw as networkgw from neutron.plugins.nicira.extensions import nvp_qos as ext_qos from neutron.plugins.nicira.nsxlib import queue as queuelib +from neutron.plugins.nicira.nsxlib import router as routerlib from neutron.plugins.nicira import NvpApiClient from neutron.plugins.nicira import nvplib @@ -257,7 +258,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, port_data['fixed_ips'], subnet_ids)) try: - lrouter_port = nvplib.create_router_lport( + lrouter_port = routerlib.create_router_lport( cluster, nsx_router_id, port_data.get('tenant_id', 'fake'), port_data.get('id', 'fake'), port_data.get('name', 'fake'), port_data.get('admin_state_up', True), ip_addresses, @@ -314,7 +315,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # Be safe and concede NAT rules might not exist. # Therefore use min_num_expected=0 for cidr in cidrs: - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "SourceNatRule", max_num_expected=1, min_num_expected=0, source_ip_addresses=cidr) @@ -324,7 +325,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # Set the SNAT rule for each subnet (only first IP) for cidr in cidrs: cidr_prefix = int(cidr.split('/')[1]) - nvplib.create_lrouter_snat_rule( + routerlib.create_lrouter_snat_rule( self.cluster, nsx_router_id, ip_addresses[0].split('/')[0], ip_addresses[0].split('/')[0], @@ -333,34 +334,34 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, def _update_router_port_attachment(self, cluster, context, nsx_router_id, port_data, - nvp_router_port_id, + nsx_router_port_id, attachment_type, attachment, attachment_vlan=None): - if not nvp_router_port_id: - nvp_router_port_id = self._find_router_gw_port(context, port_data) + if not nsx_router_port_id: + nsx_router_port_id = self._find_router_gw_port(context, port_data) try: - nvplib.plug_router_port_attachment(cluster, nsx_router_id, - nvp_router_port_id, - attachment, - attachment_type, - attachment_vlan) + routerlib.plug_router_port_attachment(cluster, nsx_router_id, + nsx_router_port_id, + attachment, + attachment_type, + attachment_vlan) LOG.debug(_("Attached %(att)s to NVP router port %(port)s"), - {'att': attachment, 'port': nvp_router_port_id}) + {'att': attachment, 'port': nsx_router_port_id}) except NvpApiClient.NvpApiException: # Must remove NVP logical port - nvplib.delete_router_lport(cluster, nsx_router_id, - nvp_router_port_id) + routerlib.delete_router_lport(cluster, nsx_router_id, + nsx_router_port_id) LOG.exception(_("Unable to plug attachment in NVP logical " "router port %(r_port_id)s, associated with " "Neutron %(q_port_id)s"), - {'r_port_id': nvp_router_port_id, + {'r_port_id': nsx_router_port_id, 'q_port_id': port_data.get('id')}) raise nvp_exc.NvpPluginException( err_msg=(_("Unable to plug attachment in router port " "%(r_port_id)s for neutron port id %(q_port_id)s " "on router %(router_id)s") % - {'r_port_id': nvp_router_port_id, + {'r_port_id': nsx_router_port_id, 'q_port_id': port_data.get('id'), 'router_id': nsx_router_id})) @@ -525,9 +526,9 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # Delete logical router port nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, port_data['device_id']) - nvp_switch_id, nvp_port_id = nsx_utils.get_nsx_switch_and_port_id( + nsx_switch_id, nsx_port_id = nsx_utils.get_nsx_switch_and_port_id( context.session, self.cluster, port_data['id']) - if not nvp_port_id: + if not nsx_port_id: LOG.warn(_("Neutron port %(port_id)s not found on NVP backend. " "Terminating delete operation. A dangling router port " "might have been left on router %(router_id)s"), @@ -535,17 +536,17 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'router_id': nsx_router_id}) return try: - nvplib.delete_peer_router_lport(self.cluster, - nsx_router_id, - nvp_switch_id, - nvp_port_id) + routerlib.delete_peer_router_lport(self.cluster, + nsx_router_id, + nsx_switch_id, + nsx_port_id) except NvpApiClient.NvpApiException: # Do not raise because the issue might as well be that the # router has already been deleted, so there would be nothing # to do here LOG.exception(_("Ignoring exception as this means the peer " "for port '%s' has already been deleted."), - nvp_port_id) + nsx_port_id) # Delete logical switch port self._nvp_delete_port(context, port_data) @@ -599,8 +600,8 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, port_data['network_id']) nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - lr_port = nvplib.find_router_gw_port(context, self.cluster, - nsx_router_id) + lr_port = routerlib.find_router_gw_port(context, self.cluster, + nsx_router_id) if not lr_port: raise nvp_exc.NvpPluginException( err_msg=(_("The gateway port for the NSX router %s " @@ -624,14 +625,14 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # regardless of what the user specifies in neutron nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, port_data['device_id']) - nvplib.update_router_lport(self.cluster, - nsx_router_id, - lr_port['uuid'], - port_data['tenant_id'], - port_data['id'], - port_data['name'], - True, - ip_addresses) + routerlib.update_router_lport(self.cluster, + nsx_router_id, + lr_port['uuid'], + port_data['tenant_id'], + port_data['id'], + port_data['name'], + True, + ip_addresses) ext_network = self.get_network(context, port_data['network_id']) if ext_network.get(pnet.NETWORK_TYPE) == NetworkTypes.L3_EXT: # Update attachment @@ -662,14 +663,14 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, router_id = port_data['device_id'] nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - nvplib.update_router_lport(self.cluster, - nsx_router_id, - lr_port['uuid'], - port_data['tenant_id'], - port_data['id'], - port_data['name'], - True, - ['0.0.0.0/31']) + routerlib.update_router_lport(self.cluster, + nsx_router_id, + lr_port['uuid'], + port_data['tenant_id'], + port_data['id'], + port_data['name'], + True, + ['0.0.0.0/31']) # Reset attachment self._update_router_port_attachment( self.cluster, context, nsx_router_id, port_data, @@ -1036,7 +1037,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'device_owner': ['network:router_interface']} router_iface_ports = self.get_ports(context, filters=port_filter) for port in router_iface_ports: - nvp_switch_id, nvp_port_id = nsx_utils.get_nsx_switch_and_port_id( + nsx_switch_id, nsx_port_id = nsx_utils.get_nsx_switch_and_port_id( context.session, self.cluster, id) # Before removing entry from Neutron DB, retrieve NSX switch # identifiers for removing them from backend @@ -1047,13 +1048,13 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # clean up network owned ports for port in router_iface_ports: try: - if nvp_port_id: + if nsx_port_id: nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, port['device_id']) - nvplib.delete_peer_router_lport(self.cluster, - nsx_router_id, - nvp_switch_id, - nvp_port_id) + routerlib.delete_peer_router_lport(self.cluster, + nsx_router_id, + nsx_switch_id, + nsx_port_id) else: LOG.warning(_("A nvp lport identifier was not found for " "neutron port '%s'. Unable to remove " @@ -1068,7 +1069,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # to do here LOG.warning(_("Ignoring exception as this means the peer for " "port '%s' has already been deleted."), - nvp_port_id) + nsx_port_id) # Do not go to NVP for external networks if not external: @@ -1395,7 +1396,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, tenant_id = self._get_tenant_id_for_create(context, router) distributed = router.get('distributed') try: - lrouter = nvplib.create_lrouter( + lrouter = routerlib.create_lrouter( self.cluster, router['id'], tenant_id, router['name'], nexthop, distributed=attr.is_attr_set(distributed) and distributed) @@ -1424,7 +1425,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'def_l3_gw_svc': self.cluster.default_l3_gw_service_uuid}) # Try and remove logical router from NVP - nvplib.delete_lrouter(self.cluster, lrouter['uuid']) + routerlib.delete_lrouter(self.cluster, lrouter['uuid']) # Return user a 500 with an apter message raise nvp_exc.NvpPluginException( err_msg=(_("Unable to create router %s on NSX backend") % @@ -1517,14 +1518,14 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, def _update_lrouter(self, context, router_id, name, nexthop, routes=None): nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - return nvplib.update_lrouter( + return routerlib.update_lrouter( self.cluster, nsx_router_id, name, nexthop, routes=routes) def _update_lrouter_routes(self, context, router_id, routes): nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - nvplib.update_explicit_routes_lrouter( + routerlib.update_explicit_routes_lrouter( self.cluster, nsx_router_id, routes) def update_router(self, context, router_id, router): @@ -1591,7 +1592,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, def _delete_lrouter(self, context, router_id, nsx_router_id): # The neutron router id (router_id) is ignored in this routine, # but used in plugins deriving from this one - nvplib.delete_lrouter(self.cluster, nsx_router_id) + routerlib.delete_lrouter(self.cluster, nsx_router_id) def delete_router(self, context, router_id): with context.session.begin(subtransactions=True): @@ -1660,7 +1661,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, cidr_prefix = int(subnet['cidr'].split('/')[1]) nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router['id']) - nvplib.create_lrouter_snat_rule( + routerlib.create_lrouter_snat_rule( self.cluster, nsx_router_id, snat_ip, snat_ip, order=NVP_EXTGW_NAT_RULES_ORDER - cidr_prefix, match_criteria={'source_ip_addresses': subnet['cidr']}) @@ -1670,7 +1671,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, if router.gw_port: nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router['id']) - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "SourceNatRule", max_num_expected=1, min_num_expected=1, source_ip_addresses=subnet['cidr']) @@ -1701,7 +1702,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # Fetch router from DB router = self._get_router(context, router_id) self._add_subnet_snat_rule(context, router, subnet) - nvplib.create_lrouter_nosnat_rule( + routerlib.create_lrouter_nosnat_rule( self.cluster, nsx_router_id, order=NVP_NOSNAT_RULES_ORDER, match_criteria={'destination_ip_addresses': subnet['cidr']}) @@ -1768,7 +1769,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # do not exist in 2.x deployments nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "NoSourceNatRule", max_num_expected=1, min_num_expected=0, destination_ip_addresses=subnet['cidr']) @@ -1790,19 +1791,19 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, # but used by derived classes try: # Remove DNAT rule for the floating IP - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "DestinationNatRule", max_num_expected=1, min_num_expected=min_num_rules_expected, destination_ip_addresses=floating_ip_address) # Remove SNAT rules for the floating IP - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "SourceNatRule", max_num_expected=1, min_num_expected=min_num_rules_expected, source_ip_addresses=internal_ip) - nvplib.delete_nat_rules_by_match( + routerlib.delete_nat_rules_by_match( self.cluster, nsx_router_id, "SourceNatRule", max_num_expected=1, min_num_expected=min_num_rules_expected, @@ -1824,17 +1825,17 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, router_id = fip_db.router_id nsx_router_id = nsx_utils.get_nsx_router_id( context.session, self.cluster, router_id) - nvp_gw_port_id = nvplib.find_router_gw_port( + nsx_gw_port_id = routerlib.find_router_gw_port( context, self.cluster, nsx_router_id)['uuid'] ext_neutron_port_db = self._get_port(context.elevated(), fip_db.floating_port_id) - nvp_floating_ips = self._build_ip_address_list( + nsx_floating_ips = self._build_ip_address_list( context.elevated(), ext_neutron_port_db['fixed_ips']) - nvplib.update_lrouter_port_ips(self.cluster, - nsx_router_id, - nvp_gw_port_id, - ips_to_add=[], - ips_to_remove=nvp_floating_ips) + routerlib.update_lrouter_port_ips(self.cluster, + nsx_router_id, + nsx_gw_port_id, + ips_to_add=[], + ips_to_remove=nsx_floating_ips) def _get_fip_assoc_data(self, context, fip, floatingip_db): if (('fixed_ip_address' in fip and fip['fixed_ip_address']) and @@ -1895,22 +1896,22 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, 'port_id': floatingip_db.fixed_port_id, 'fixed_ip_address': floatingip_db.fixed_ip_address, 'tenant_id': floatingip_db.tenant_id}) - nvp_gw_port_id = nvplib.find_router_gw_port( + nsx_gw_port_id = routerlib.find_router_gw_port( context, self.cluster, nsx_old_router_id)['uuid'] self._retrieve_and_delete_nat_rules( context, floating_ip, old_internal_ip, nsx_old_router_id) - nvplib.update_lrouter_port_ips( - self.cluster, nsx_old_router_id, nvp_gw_port_id, + routerlib.update_lrouter_port_ips( + self.cluster, nsx_old_router_id, nsx_gw_port_id, ips_to_add=[], ips_to_remove=nvp_floating_ips) if router_id: - nvp_gw_port_id = nvplib.find_router_gw_port( + nsx_gw_port_id = routerlib.find_router_gw_port( context, self.cluster, nsx_router_id)['uuid'] # Re-create NAT rules only if a port id is specified if fip.get('port_id'): try: # Setup DNAT rules for the floating IP - nvplib.create_lrouter_dnat_rule( + routerlib.create_lrouter_dnat_rule( self.cluster, nsx_router_id, internal_ip, order=NVP_FLOATINGIP_NAT_RULES_ORDER, match_criteria={'destination_ip_addresses': @@ -1928,7 +1929,7 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, internal_subnet_cidr = self._build_ip_address_list( context, internal_port['fixed_ips'], subnet_ids=subnet_ids)[0] - nvplib.create_lrouter_snat_rule( + routerlib.create_lrouter_snat_rule( self.cluster, nsx_router_id, floating_ip, floating_ip, order=NVP_NOSNAT_RULES_ORDER - 1, match_criteria={'source_ip_addresses': @@ -1937,17 +1938,15 @@ class NvpPluginV2(addr_pair_db.AllowedAddressPairsMixin, internal_ip}) # setup snat rule such that src ip of a IP packet when # using floating is the floating ip itself. - nvplib.create_lrouter_snat_rule( + routerlib.create_lrouter_snat_rule( self.cluster, nsx_router_id, floating_ip, floating_ip, order=NVP_FLOATINGIP_NAT_RULES_ORDER, match_criteria={'source_ip_addresses': internal_ip}) # Add Floating IP address to router_port - nvplib.update_lrouter_port_ips(self.cluster, - nsx_router_id, - nvp_gw_port_id, - ips_to_add=nvp_floating_ips, - ips_to_remove=[]) + routerlib.update_lrouter_port_ips( + self.cluster, nsx_router_id, nsx_gw_port_id, + ips_to_add=nvp_floating_ips, ips_to_remove=[]) except NvpApiClient.NvpApiException: LOG.exception(_("An error occurred while creating NAT " "rules on the NVP platform for floating " diff --git a/neutron/plugins/nicira/NeutronServicePlugin.py b/neutron/plugins/nicira/NeutronServicePlugin.py index 348a381f0..da0c7c2a9 100644 --- a/neutron/plugins/nicira/NeutronServicePlugin.py +++ b/neutron/plugins/nicira/NeutronServicePlugin.py @@ -36,6 +36,7 @@ from neutron.plugins.nicira.dbexts import vcns_db from neutron.plugins.nicira.dbexts import vcns_models from neutron.plugins.nicira.extensions import servicerouter as sr from neutron.plugins.nicira import NeutronPlugin +from neutron.plugins.nicira.nsxlib import router as routerlib from neutron.plugins.nicira import NvpApiClient from neutron.plugins.nicira import nvplib from neutron.plugins.nicira.vshield.common import ( @@ -425,7 +426,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, neutron_port_id = '' pname = name[:36] + '-lp' admin_status_enabled = True - lr_port = nvplib.create_router_lport( + lr_port = routerlib.create_router_lport( self.cluster, lrouter['uuid'], tenant_id, neutron_port_id, pname, admin_status_enabled, [vcns_const.INTEGRATION_LR_IPADDRESS]) @@ -464,7 +465,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, msg = _("Unable to create integration logic switch " "for router %s") % name LOG.exception(msg) - nvplib.delete_lrouter(self.cluster, lrouter['uuid']) + routerlib.delete_lrouter(self.cluster, lrouter['uuid']) raise q_exc.NeutronException(message=msg) try: @@ -474,7 +475,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, msg = _("Unable to add router interface to integration lswitch " "for router %s") % name LOG.exception(msg) - nvplib.delete_lrouter(self.cluster, lrouter['uuid']) + routerlib.delete_lrouter(self.cluster, lrouter['uuid']) raise q_exc.NeutronException(message=msg) try: @@ -484,7 +485,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, msg = (_("Unable to create advance service router for %s") % name) LOG.exception(msg) self.vcns_driver.delete_lswitch(lswitch('uuid')) - nvplib.delete_lrouter(self.cluster, lrouter['uuid']) + routerlib.delete_lrouter(self.cluster, lrouter['uuid']) raise q_exc.NeutronException(message=msg) lrouter['status'] = service_constants.PENDING_CREATE @@ -516,7 +517,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, self.vcns_driver.delete_edge(router_id, edge_id, jobdata=jobdata) # delete NSX logical router - nvplib.delete_lrouter(self.cluster, nsx_router_id) + routerlib.delete_lrouter(self.cluster, nsx_router_id) if id in self._router_type: del self._router_type[router_id] @@ -563,7 +564,7 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, def _get_nvp_lrouter_status(self, id): try: - lrouter = nvplib.get_lrouter(self.cluster, id) + lrouter = routerlib.get_lrouter(self.cluster, id) lr_status = lrouter["_relations"]["LogicalRouterStatus"] if lr_status["fabric_status"]: nvp_status = RouterStatus.ROUTER_STATUS_ACTIVE @@ -587,9 +588,9 @@ class NvpAdvancedPlugin(sr_db.ServiceRouter_mixin, def _get_all_nvp_lrouters_statuses(self, tenant_id, fields): # get nvp lrouters status - nvp_lrouters = nvplib.get_lrouters(self.cluster, - tenant_id, - fields) + nvp_lrouters = routerlib.get_lrouters(self.cluster, + tenant_id, + fields) nvp_status = {} for nvp_lrouter in nvp_lrouters: diff --git a/neutron/plugins/nicira/common/nsx_utils.py b/neutron/plugins/nicira/common/nsx_utils.py index 56b149854..208b2ea74 100644 --- a/neutron/plugins/nicira/common/nsx_utils.py +++ b/neutron/plugins/nicira/common/nsx_utils.py @@ -18,6 +18,7 @@ from neutron.openstack.common import log from neutron.plugins.nicira.dbexts import nicira_db from neutron.plugins.nicira import nsx_cluster +from neutron.plugins.nicira.nsxlib import router as routerlib from neutron.plugins.nicira import NvpApiClient from neutron.plugins.nicira import nvplib @@ -155,7 +156,7 @@ def get_nsx_router_id(session, cluster, neutron_router_id): # Find logical router from backend. # This is a rather expensive query, but it won't be executed # more than once for each router in Neutron's lifetime - nsx_routers = nvplib.query_lrouters( + nsx_routers = routerlib.query_lrouters( cluster, '*', filters={'tag': neutron_router_id, 'tag_scope': 'q_router_id'}) diff --git a/neutron/plugins/nicira/common/sync.py b/neutron/plugins/nicira/common/sync.py index 19543a781..dbbedf61c 100644 --- a/neutron/plugins/nicira/common/sync.py +++ b/neutron/plugins/nicira/common/sync.py @@ -27,6 +27,7 @@ from neutron.openstack.common import loopingcall from neutron.openstack.common import timeutils from neutron.plugins.nicira.common import exceptions as nvp_exc from neutron.plugins.nicira.common import nsx_utils +from neutron.plugins.nicira.nsxlib import router as routerlib from neutron.plugins.nicira import NvpApiClient from neutron.plugins.nicira import nvplib @@ -191,7 +192,7 @@ class NvpSynchronizer(): nvplib.LSWITCH_RESOURCE, fields='uuid,tags,fabric_status', relations='LogicalSwitchStatus') LR_URI = nvplib._build_uri_path( - nvplib.LROUTER_RESOURCE, fields='uuid,tags,fabric_status', + routerlib.LROUTER_RESOURCE, fields='uuid,tags,fabric_status', relations='LogicalRouterStatus') LP_URI = nvplib._build_uri_path( nvplib.LSWITCHPORT_RESOURCE, @@ -319,7 +320,7 @@ class NvpSynchronizer(): # 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']) - lrouter = nvplib.get_lrouter( + lrouter = routerlib.get_lrouter( self._cluster, nsx_router_id) except exceptions.NotFound: # NOTE(salv-orlando): We should be catching diff --git a/neutron/plugins/nicira/nsxlib/__init__.py b/neutron/plugins/nicira/nsxlib/__init__.py index c020e3bcd..e69de29bb 100644 --- a/neutron/plugins/nicira/nsxlib/__init__.py +++ b/neutron/plugins/nicira/nsxlib/__init__.py @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 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. diff --git a/neutron/plugins/nicira/nsxlib/router.py b/neutron/plugins/nicira/nsxlib/router.py new file mode 100644 index 000000000..b76b63254 --- /dev/null +++ b/neutron/plugins/nicira/nsxlib/router.py @@ -0,0 +1,676 @@ +# Copyright 2014 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 neutron.common import exceptions as exception +from neutron.openstack.common import excutils +from neutron.openstack.common import jsonutils +from neutron.openstack.common import log +from neutron.plugins.nicira.common import exceptions as nvp_exc +from neutron.plugins.nicira.common import utils +from neutron.plugins.nicira.nsxlib.versioning import DEFAULT_VERSION +from neutron.plugins.nicira.nsxlib.versioning import versioned +from neutron.plugins.nicira import NvpApiClient +from neutron.plugins.nicira.nvplib import _build_uri_path +from neutron.plugins.nicira.nvplib import do_request +from neutron.plugins.nicira.nvplib import get_all_query_pages +from neutron.plugins.nicira.nvplib import get_port + +HTTP_GET = "GET" +HTTP_POST = "POST" +HTTP_DELETE = "DELETE" +HTTP_PUT = "PUT" + +LROUTER_RESOURCE = "lrouter" +LROUTER_RESOURCE = "lrouter" +LROUTERPORT_RESOURCE = "lport/%s" % LROUTER_RESOURCE +LROUTERRIB_RESOURCE = "rib/%s" % LROUTER_RESOURCE +LROUTERNAT_RESOURCE = "nat/lrouter" +# Constants for NAT rules +MATCH_KEYS = ["destination_ip_addresses", "destination_port_max", + "destination_port_min", "source_ip_addresses", + "source_port_max", "source_port_min", "protocol"] + +LOG = log.getLogger(__name__) + + +def _prepare_lrouter_body(name, neutron_router_id, tenant_id, + router_type, distributed=None, **kwargs): + body = { + "display_name": utils.check_and_truncate(name), + "tags": utils.get_tags(os_tid=tenant_id, + q_router_id=neutron_router_id), + "routing_config": { + "type": router_type + }, + "type": "LogicalRouterConfig" + } + # add the distributed key only if not None (ie: True or False) + if distributed is not None: + body['distributed'] = distributed + if kwargs: + body["routing_config"].update(kwargs) + return body + + +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 do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE), + jsonutils.dumps(lrouter_obj), cluster=cluster) + + +def create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id, + display_name, nexthop): + """Create a NSX logical router on the specified cluster. + + :param cluster: The target NSX cluster + :param tenant_id: Identifier of the Openstack tenant for which + the logical router is being created + :param display_name: Descriptive name of this logical router + :param nexthop: External gateway IP address for the logical router + :raise NvpApiException: if there is a problem while communicating + with the NSX controller + """ + return _create_implicit_routing_lrouter( + cluster, neutron_router_id, tenant_id, display_name, nexthop) + + +def create_implicit_routing_lrouter_with_distribution( + cluster, neutron_router_id, tenant_id, display_name, + nexthop, distributed=None): + """Create a NSX logical router on the specified cluster. + + This function also allows for creating distributed lrouters + :param cluster: The target NSX cluster + :param tenant_id: Identifier of the Openstack tenant for which + the logical router is being created + :param display_name: Descriptive name of this logical router + :param nexthop: External gateway IP address for the logical router + :param distributed: True for distributed logical routers + :raise NvpApiException: if there is a problem while communicating + with the NSX controller + """ + return _create_implicit_routing_lrouter( + cluster, neutron_router_id, tenant_id, + display_name, nexthop, distributed) + + +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 delete_lrouter(cluster, lrouter_id): + do_request(HTTP_DELETE, _build_uri_path(LROUTER_RESOURCE, + resource_id=lrouter_id), + cluster=cluster) + + +def get_lrouter(cluster, lrouter_id): + return do_request(HTTP_GET, + _build_uri_path(LROUTER_RESOURCE, + resource_id=lrouter_id, + relations='LogicalRouterStatus'), + cluster=cluster) + + +def query_lrouters(cluster, fields=None, filters=None): + return get_all_query_pages( + _build_uri_path(LROUTER_RESOURCE, + fields=fields, + relations='LogicalRouterStatus', + filters=filters), + cluster) + + +def get_lrouters(cluster, tenant_id, fields=None, filters=None): + # FIXME(salv-orlando): Fields parameter is ignored in this routine + actual_filters = {} + if filters: + actual_filters.update(filters) + if tenant_id: + actual_filters['tag'] = tenant_id + actual_filters['tag_scope'] = 'os_tid' + lrouter_fields = "uuid,display_name,fabric_status,tags" + return query_lrouters(cluster, lrouter_fields, actual_filters) + + +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 get_explicit_routes_lrouter(cluster, router_id, protocol_type='static'): + static_filter = {'protocol': protocol_type} + existing_routes = do_request( + HTTP_GET, + _build_uri_path(LROUTERRIB_RESOURCE, + filters=static_filter, + fields="*", + parent_resource_id=router_id), + cluster=cluster)['results'] + return existing_routes + + +def delete_explicit_route_lrouter(cluster, router_id, route_id): + do_request(HTTP_DELETE, + _build_uri_path(LROUTERRIB_RESOURCE, + resource_id=route_id, + parent_resource_id=router_id), + 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 = do_request( + HTTP_POST, + _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 update_explicit_routes_lrouter(cluster, router_id, routes): + # Update in bulk: delete them all, and add the ones specified + # but keep track of what is been modified to allow roll-backs + # in case of failures + nsx_routes = get_explicit_routes_lrouter(cluster, router_id) + try: + deleted_routes = [] + added_routes = [] + # omit the default route (0.0.0.0/0) from the processing; + # this must be handled through the nexthop for the router + for route in nsx_routes: + prefix = route.get("destination") or route.get("prefix") + if prefix != '0.0.0.0/0': + delete_explicit_route_lrouter(cluster, + router_id, + route['uuid']) + deleted_routes.append(route) + for route in routes: + prefix = route.get("destination") or route.get("prefix") + if prefix != '0.0.0.0/0': + uuid = create_explicit_route_lrouter(cluster, + router_id, route) + added_routes.append(uuid) + except NvpApiClient.NvpApiException: + LOG.exception(_('Cannot update NSX routes %(routes)s for ' + 'router %(router_id)s'), + {'routes': routes, 'router_id': router_id}) + # Roll back to keep NSX in consistent state + with excutils.save_and_reraise_exception(): + if nsx_routes: + if deleted_routes: + for route in deleted_routes: + create_explicit_route_lrouter(cluster, + router_id, route) + if added_routes: + for route_id in added_routes: + delete_explicit_route_lrouter(cluster, + router_id, route_id) + return nsx_routes + + +def get_default_route_explicit_routing_lrouter_v33(cluster, router_id): + static_filter = {"protocol": "static", + "prefix": "0.0.0.0/0"} + default_route = do_request( + HTTP_GET, + _build_uri_path(LROUTERRIB_RESOURCE, + filters=static_filter, + fields="*", + parent_resource_id=router_id), + cluster=cluster)["results"][0] + return default_route + + +def get_default_route_explicit_routing_lrouter_v32(cluster, router_id): + # Scan all routes because 3.2 does not support query by prefix + all_routes = get_explicit_routes_lrouter(cluster, router_id) + for route in all_routes: + if route['prefix'] == '0.0.0.0/0': + return route + + +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"} + do_request(HTTP_PUT, + _build_uri_path(LROUTERRIB_RESOURCE, + resource_id=default_route['uuid'], + parent_resource_id=router_id), + jsonutils.dumps(new_default_route), + cluster=cluster) + + +def update_explicit_routing_lrouter(cluster, router_id, + display_name, next_hop, routes=None): + update_implicit_routing_lrouter(cluster, router_id, display_name, next_hop) + if next_hop: + update_default_gw_explicit_routing_lrouter(cluster, + router_id, next_hop) + if routes is not None: + return update_explicit_routes_lrouter(cluster, router_id, routes) + + +def query_lrouter_lports(cluster, lr_uuid, fields="*", + filters=None, relations=None): + uri = _build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lr_uuid, + fields=fields, filters=filters, relations=relations) + return do_request(HTTP_GET, uri, cluster=cluster)['results'] + + +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 = _build_uri_path(LROUTERPORT_RESOURCE, + parent_resource_id=lrouter_uuid) + result = 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 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 = _build_uri_path(LROUTERPORT_RESOURCE, + lrouter_port_uuid, + parent_resource_id=lrouter_uuid) + result = 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 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 delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid): + nsx_port = get_port(cluster, ls_uuid, lp_uuid, + relations="LogicalPortAttachment") + relations = nsx_port.get('_relations') + if relations: + att_data = relations.get('LogicalPortAttachment') + if att_data: + lrp_uuid = att_data.get('peer_port_uuid') + if lrp_uuid: + delete_router_lport(cluster, lr_uuid, lrp_uuid) + + +def find_router_gw_port(context, cluster, router_id): + """Retrieves the external gateway port for a NSX logical router.""" + + # Find the uuid of nsx ext gw logical router port + # TODO(salvatore-orlando): Consider storing it in Neutron DB + results = query_lrouter_lports( + cluster, router_id, + relations="LogicalPortAttachment") + for lport in results: + if '_relations' in lport: + attachment = lport['_relations'].get('LogicalPortAttachment') + if attachment and attachment.get('type') == 'L3GatewayAttachment': + return lport + + +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 = _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 nvp_exc.NvpInvalidAttachmentType( + attachment_type=nsx_attachment_type) + return do_request( + HTTP_PUT, uri, jsonutils.dumps(attach_obj), cluster=cluster) + + +def _create_nat_match_obj(**kwargs): + nat_match_obj = {'ethertype': 'IPv4'} + delta = set(kwargs.keys()) - set(MATCH_KEYS) + if delta: + raise Exception(_("Invalid keys for NAT match: %s"), delta) + nat_match_obj.update(kwargs) + return nat_match_obj + + +def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj): + LOG.debug(_("Creating NAT rule: %s"), nat_rule_obj) + uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id) + return do_request(HTTP_POST, uri, jsonutils.dumps(nat_rule_obj), + cluster=cluster) + + +def _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj): + return {"to_source_ip_address_min": min_src_ip, + "to_source_ip_address_max": max_src_ip, + "type": "SourceNatRule", + "match": nat_match_obj} + + +def create_lrouter_nosnat_rule_v2(cluster, _router_id, _match_criteria=None): + LOG.info(_("No SNAT rules cannot be applied as they are not available in " + "this version of the NSX platform")) + + +def create_lrouter_nodnat_rule_v2(cluster, _router_id, _match_criteria=None): + LOG.info(_("No DNAT rules cannot be applied as they are not available in " + "this version of the NSX platform")) + + +def create_lrouter_snat_rule_v2(cluster, router_id, + min_src_ip, max_src_ip, match_criteria=None): + + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj) + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def create_lrouter_dnat_rule_v2(cluster, router_id, dst_ip, + to_dst_port=None, match_criteria=None): + + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = { + "to_destination_ip_address_min": dst_ip, + "to_destination_ip_address_max": dst_ip, + "type": "DestinationNatRule", + "match": nat_match_obj + } + if to_dst_port: + nat_rule_obj['to_destination_port'] = to_dst_port + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def create_lrouter_nosnat_rule_v3(cluster, router_id, order=None, + match_criteria=None): + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = { + "type": "NoSourceNatRule", + "match": nat_match_obj + } + if order: + nat_rule_obj['order'] = order + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def create_lrouter_nodnat_rule_v3(cluster, router_id, order=None, + match_criteria=None): + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = { + "type": "NoDestinationNatRule", + "match": nat_match_obj + } + if order: + nat_rule_obj['order'] = order + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def create_lrouter_snat_rule_v3(cluster, router_id, min_src_ip, max_src_ip, + order=None, match_criteria=None): + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj) + if order: + nat_rule_obj['order'] = order + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def create_lrouter_dnat_rule_v3(cluster, router_id, dst_ip, to_dst_port=None, + order=None, match_criteria=None): + + nat_match_obj = _create_nat_match_obj(**match_criteria) + nat_rule_obj = { + "to_destination_ip_address": dst_ip, + "type": "DestinationNatRule", + "match": nat_match_obj + } + if to_dst_port: + nat_rule_obj['to_destination_port'] = to_dst_port + if order: + nat_rule_obj['order'] = order + return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) + + +def delete_nat_rules_by_match(cluster, router_id, rule_type, + max_num_expected, + min_num_expected=0, + **kwargs): + # remove nat rules + nat_rules = query_nat_rules(cluster, router_id) + to_delete_ids = [] + for r in nat_rules: + if (r['type'] != rule_type): + continue + + for key, value in kwargs.iteritems(): + if not (key in r['match'] and r['match'][key] == value): + break + else: + to_delete_ids.append(r['uuid']) + if not (len(to_delete_ids) in + range(min_num_expected, max_num_expected + 1)): + raise nvp_exc.NvpNatRuleMismatch(actual_rules=len(to_delete_ids), + min_rules=min_num_expected, + max_rules=max_num_expected) + + for rule_id in to_delete_ids: + delete_router_nat_rule(cluster, router_id, rule_id) + + +def delete_router_nat_rule(cluster, router_id, rule_id): + uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id) + do_request(HTTP_DELETE, uri, cluster=cluster) + + +def query_nat_rules(cluster, router_id, fields="*", filters=None): + uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id, + fields=fields, filters=filters) + return get_all_query_pages(uri, cluster) + + +# NOTE(salvatore-orlando): The following FIXME applies in general to +# each operation on list attributes. +# FIXME(salvatore-orlando): need a lock around the list of IPs on an iface +def update_lrouter_port_ips(cluster, lrouter_id, lport_id, + ips_to_add, ips_to_remove): + uri = _build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id) + try: + port = 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) + do_request(HTTP_PUT, uri, jsonutils.dumps(port), cluster=cluster) + except exception.NotFound as e: + # 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 nvp_exc.NvpPluginException(err_msg=msg) + except NvpApiClient.NvpApiException as e: + msg = _("An exception occurred while updating IP addresses on a " + "router logical port:%s") % str(e) + LOG.exception(msg) + raise nvp_exc.NvpPluginException(err_msg=msg) + + +ROUTER_FUNC_DICT = { + 'create_lrouter': { + 2: {DEFAULT_VERSION: create_implicit_routing_lrouter, }, + 3: {DEFAULT_VERSION: create_implicit_routing_lrouter, + 1: create_implicit_routing_lrouter_with_distribution, + 2: create_explicit_routing_lrouter, }, }, + 'update_lrouter': { + 2: {DEFAULT_VERSION: update_implicit_routing_lrouter, }, + 3: {DEFAULT_VERSION: update_implicit_routing_lrouter, + 2: update_explicit_routing_lrouter, }, }, + 'create_lrouter_dnat_rule': { + 2: {DEFAULT_VERSION: create_lrouter_dnat_rule_v2, }, + 3: {DEFAULT_VERSION: create_lrouter_dnat_rule_v3, }, }, + 'create_lrouter_snat_rule': { + 2: {DEFAULT_VERSION: create_lrouter_snat_rule_v2, }, + 3: {DEFAULT_VERSION: create_lrouter_snat_rule_v3, }, }, + 'create_lrouter_nosnat_rule': { + 2: {DEFAULT_VERSION: create_lrouter_nosnat_rule_v2, }, + 3: {DEFAULT_VERSION: create_lrouter_nosnat_rule_v3, }, }, + 'create_lrouter_nodnat_rule': { + 2: {DEFAULT_VERSION: create_lrouter_nodnat_rule_v2, }, + 3: {DEFAULT_VERSION: create_lrouter_nodnat_rule_v3, }, }, + 'get_default_route_explicit_routing_lrouter': { + 3: {DEFAULT_VERSION: get_default_route_explicit_routing_lrouter_v32, + 2: get_default_route_explicit_routing_lrouter_v32, }, }, +} + + +@versioned(ROUTER_FUNC_DICT) +def create_lrouter(cluster, *args, **kwargs): + if kwargs.get('distributed', None): + v = cluster.api_client.get_nvp_version() + if (v.major, v.minor) < (3, 1): + raise nvp_exc.NvpInvalidVersion(version=v) + return v + + +@versioned(ROUTER_FUNC_DICT) +def get_default_route_explicit_routing_lrouter(cluster, *args, **kwargs): + pass + + +@versioned(ROUTER_FUNC_DICT) +def update_lrouter(cluster, *args, **kwargs): + if kwargs.get('routes', None): + v = cluster.api_client.get_nvp_version() + if (v.major, v.minor) < (3, 2): + raise nvp_exc.NvpInvalidVersion(version=v) + return v + + +@versioned(ROUTER_FUNC_DICT) +def create_lrouter_dnat_rule(cluster, *args, **kwargs): + pass + + +@versioned(ROUTER_FUNC_DICT) +def create_lrouter_snat_rule(cluster, *args, **kwargs): + pass + + +@versioned(ROUTER_FUNC_DICT) +def create_lrouter_nosnat_rule(cluster, *args, **kwargs): + pass + + +@versioned(ROUTER_FUNC_DICT) +def create_lrouter_nodnat_rule(cluster, *args, **kwargs): + pass diff --git a/neutron/plugins/nicira/nsxlib/versioning.py b/neutron/plugins/nicira/nsxlib/versioning.py new file mode 100644 index 000000000..dfa4cb6b0 --- /dev/null +++ b/neutron/plugins/nicira/nsxlib/versioning.py @@ -0,0 +1,66 @@ +# Copyright 2014 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. + +import inspect + +from neutron.plugins.nicira import NvpApiClient + +DEFAULT_VERSION = -1 + + +def versioned(func_table): + + def versioned_function(wrapped_func): + func_name = wrapped_func.__name__ + + def dispatch_versioned_function(cluster, *args, **kwargs): + # Call the wrapper function, in case we need to + # run validation checks regarding versions. It + # should return the NVP version + v = (wrapped_func(cluster, *args, **kwargs) or + cluster.api_client.get_nvp_version()) + func = get_function_by_version(func_table, func_name, v) + func_kwargs = kwargs + arg_spec = inspect.getargspec(func) + if not arg_spec.keywords and not arg_spec.varargs: + # drop args unknown to function from func_args + arg_set = set(func_kwargs.keys()) + for arg in arg_set - set(arg_spec.args): + del func_kwargs[arg] + # NOTE(salvatore-orlando): shall we fail here if a required + # argument is not passed, or let the called function raise? + return func(cluster, *args, **func_kwargs) + + return dispatch_versioned_function + return versioned_function + + +def get_function_by_version(func_table, func_name, ver): + if ver: + if ver.major not in func_table[func_name]: + major = max(func_table[func_name].keys()) + minor = max(func_table[func_name][major].keys()) + if major > ver.major: + raise NotImplementedError(_("Operation may not be supported")) + else: + major = ver.major + minor = ver.minor + if ver.minor not in func_table[func_name][major]: + minor = DEFAULT_VERSION + return func_table[func_name][major][minor] + else: + msg = _('NSX version is not set. Unable to complete request ' + 'correctly. Check log for NSX communication errors.') + raise NvpApiClient.ServiceUnavailable(message=msg) diff --git a/neutron/plugins/nicira/nvplib.py b/neutron/plugins/nicira/nvplib.py index d151ebe83..3c5987063 100644 --- a/neutron/plugins/nicira/nvplib.py +++ b/neutron/plugins/nicira/nvplib.py @@ -21,14 +21,12 @@ import hashlib -import inspect import json #FIXME(danwent): I'd like this file to get to the point where it has # no neutron-specific logic in it from neutron.common import constants from neutron.common import exceptions as exception -from neutron.openstack.common import excutils from neutron.openstack.common import log from neutron.plugins.nicira.common import exceptions as nvp_exc from neutron.plugins.nicira.common import utils @@ -47,23 +45,10 @@ URI_PREFIX = "/ws.v1" # Resources exposed by NVP API LSWITCH_RESOURCE = "lswitch" LSWITCHPORT_RESOURCE = "lport/%s" % LSWITCH_RESOURCE -LROUTER_RESOURCE = "lrouter" -LROUTERPORT_RESOURCE = "lport/%s" % LROUTER_RESOURCE -LROUTERRIB_RESOURCE = "rib/%s" % LROUTER_RESOURCE -LROUTERNAT_RESOURCE = "nat/lrouter" -LQUEUE_RESOURCE = "lqueue" GWSERVICE_RESOURCE = "gateway-service" # Current neutron version NEUTRON_VERSION = version_info.release_string() -# Constants for NAT rules -MATCH_KEYS = ["destination_ip_addresses", "destination_port_max", - "destination_port_min", "source_ip_addresses", - "source_port_max", "source_port_min", "protocol"] -SNAT_KEYS = ["to_src_port_min", "to_src_port_max", "to_src_ip_min", - "to_src_ip_max"] - -DNAT_KEYS = ["to_dst_port", "to_dst_ip_min", "to_dst_ip_max"] # Maximum page size for a single request # NOTE(salv-orlando): This might become a version-dependent map should the # limit be raised in future versions @@ -91,30 +76,6 @@ def device_id_to_vm_id(device_id, obfuscate=False): return device_id -def version_dependent(wrapped_func): - func_name = wrapped_func.__name__ - - def dispatch_version_dependent_function(cluster, *args, **kwargs): - # Call the wrapper function, in case we need to - # run validation checks regarding versions. It - # should return the NVP version - v = (wrapped_func(cluster, *args, **kwargs) or - cluster.api_client.get_nvp_version()) - func = get_function_by_version(func_name, v) - func_kwargs = kwargs - arg_spec = inspect.getargspec(func) - if not arg_spec.keywords and not arg_spec.varargs: - # drop args unknown to function from func_args - arg_set = set(func_kwargs.keys()) - for arg in arg_set - set(arg_spec.args): - del func_kwargs[arg] - # NOTE(salvatore-orlando): shall we fail here if a required - # argument is not passed, or let the called function raise? - return func(cluster, *args, **func_kwargs) - - return dispatch_version_dependent_function - - def _build_uri_path(resource, resource_id=None, parent_resource_id=None, @@ -317,122 +278,12 @@ def create_l2_gw_service(cluster, tenant_id, display_name, devices): json.dumps(gwservice_obj), cluster=cluster) -def _prepare_lrouter_body(name, neutron_router_id, tenant_id, router_type, - distributed=None, **kwargs): - body = { - "display_name": utils.check_and_truncate(name), - "tags": [{"tag": tenant_id, "scope": "os_tid"}, - {"tag": neutron_router_id, "scope": "q_router_id"}, - {"tag": NEUTRON_VERSION, "scope": "quantum"}], - "routing_config": { - "type": router_type - }, - "type": "LogicalRouterConfig" - } - # add the distributed key only if not None (ie: True or False) - if distributed is not None: - body['distributed'] = distributed - if kwargs: - body["routing_config"].update(kwargs) - return body - - -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 do_request(HTTP_POST, _build_uri_path(LROUTER_RESOURCE), - json.dumps(lrouter_obj), cluster=cluster) - - -def create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id, - display_name, nexthop): - """Create a NVP logical router on the specified cluster. - - :param cluster: The target NVP cluster - :param tenant_id: Identifier of the Openstack tenant for which - the logical router is being created - :param display_name: Descriptive name of this logical router - :param nexthop: External gateway IP address for the logical router - :raise NvpApiException: if there is a problem while communicating - with the NVP controller - """ - return _create_implicit_routing_lrouter( - cluster, neutron_router_id, tenant_id, display_name, nexthop) - - -def create_implicit_routing_lrouter_with_distribution( - cluster, neutron_router_id, tenant_id, - display_name, nexthop, distributed=None): - """Create a NVP logical router on the specified cluster. - - This function also allows for creating distributed lrouters - :param cluster: The target NVP cluster - :param tenant_id: Identifier of the Openstack tenant for which - the logical router is being created - :param display_name: Descriptive name of this logical router - :param nexthop: External gateway IP address for the logical router - :param distributed: True for distributed logical routers - :raise NvpApiException: if there is a problem while communicating - with the NVP controller - """ - return _create_implicit_routing_lrouter( - cluster, neutron_router_id, tenant_id, - display_name, nexthop, distributed) - - -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), - json.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 - - -@version_dependent -def create_lrouter(cluster, *args, **kwargs): - if kwargs.get('distributed', None): - v = cluster.api_client.get_nvp_version() - if (v.major, v.minor) < (3, 1): - raise nvp_exc.NvpInvalidVersion(version=v) - return v - - -def delete_lrouter(cluster, lrouter_id): - do_request(HTTP_DELETE, _build_uri_path(LROUTER_RESOURCE, - resource_id=lrouter_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 get_lrouter(cluster, lrouter_id): - return do_request(HTTP_GET, - _build_uri_path(LROUTER_RESOURCE, - resource_id=lrouter_id, - relations='LogicalRouterStatus'), - cluster=cluster) - - def get_l2_gw_service(cluster, gateway_id): return do_request( "GET", _build_uri_path(GWSERVICE_RESOURCE, @@ -440,27 +291,6 @@ def get_l2_gw_service(cluster, gateway_id): cluster=cluster) -def query_lrouters(cluster, fields=None, filters=None): - return get_all_query_pages( - _build_uri_path(LROUTER_RESOURCE, - fields=fields, - relations='LogicalRouterStatus', - filters=filters), - cluster) - - -def get_lrouters(cluster, tenant_id, fields=None, filters=None): - # FIXME(salv-orlando): Fields parameter is ignored in this routine - actual_filters = {} - if filters: - actual_filters.update(filters) - if tenant_id: - actual_filters['tag'] = tenant_id - actual_filters['tag_scope'] = 'os_tid' - lrouter_fields = "uuid,display_name,fabric_status,tags" - return query_lrouters(cluster, lrouter_fields, actual_filters) - - def get_l2_gw_services(cluster, tenant_id=None, fields=None, filters=None): actual_filters = dict(filters or {}) @@ -485,164 +315,6 @@ def update_l2_gw_service(cluster, gateway_id, display_name): json.dumps(gwservice_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), - json.dumps(lrouter_obj), - cluster=cluster) - - -def get_explicit_routes_lrouter(cluster, router_id, protocol_type='static'): - static_filter = {'protocol': protocol_type} - existing_routes = do_request( - HTTP_GET, - _build_uri_path(LROUTERRIB_RESOURCE, - filters=static_filter, - fields="*", - parent_resource_id=router_id), - cluster=cluster)['results'] - return existing_routes - - -def delete_explicit_route_lrouter(cluster, router_id, route_id): - do_request(HTTP_DELETE, - _build_uri_path(LROUTERRIB_RESOURCE, - resource_id=route_id, - parent_resource_id=router_id), - 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 = do_request( - HTTP_POST, - _build_uri_path(LROUTERRIB_RESOURCE, - parent_resource_id=router_id), - json.dumps({ - "action": "accept", - "next_hop_ip": next_hop_ip, - "prefix": prefix, - "protocol": "static" - }), - cluster=cluster)['uuid'] - return uuid - - -def update_explicit_routes_lrouter(cluster, router_id, routes): - # Update in bulk: delete them all, and add the ones specified - # but keep track of what is been modified to allow roll-backs - # in case of failures - nvp_routes = get_explicit_routes_lrouter(cluster, router_id) - try: - deleted_routes = [] - added_routes = [] - # omit the default route (0.0.0.0/0) from the processing; - # this must be handled through the nexthop for the router - for route in nvp_routes: - prefix = route.get("destination") or route.get("prefix") - if prefix != '0.0.0.0/0': - delete_explicit_route_lrouter(cluster, - router_id, - route['uuid']) - deleted_routes.append(route) - for route in routes: - prefix = route.get("destination") or route.get("prefix") - if prefix != '0.0.0.0/0': - uuid = create_explicit_route_lrouter(cluster, - router_id, route) - added_routes.append(uuid) - except NvpApiClient.NvpApiException: - LOG.exception(_('Cannot update NVP routes %(routes)s for ' - 'router %(router_id)s'), - {'routes': routes, 'router_id': router_id}) - # Roll back to keep NVP in consistent state - with excutils.save_and_reraise_exception(): - if nvp_routes: - if deleted_routes: - for route in deleted_routes: - create_explicit_route_lrouter(cluster, - router_id, route) - if added_routes: - for route_id in added_routes: - delete_explicit_route_lrouter(cluster, - router_id, route_id) - return nvp_routes - - -@version_dependent -def get_default_route_explicit_routing_lrouter(cluster, *args, **kwargs): - pass - - -def get_default_route_explicit_routing_lrouter_v33(cluster, router_id): - static_filter = {"protocol": "static", - "prefix": "0.0.0.0/0"} - default_route = do_request( - HTTP_GET, - _build_uri_path(LROUTERRIB_RESOURCE, - filters=static_filter, - fields="*", - parent_resource_id=router_id), - cluster=cluster)["results"][0] - return default_route - - -def get_default_route_explicit_routing_lrouter_v32(cluster, router_id): - # Scan all routes because 3.2 does not support query by prefix - all_routes = get_explicit_routes_lrouter(cluster, router_id) - for route in all_routes: - if route['prefix'] == '0.0.0.0/0': - return route - - -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"} - do_request(HTTP_PUT, - _build_uri_path(LROUTERRIB_RESOURCE, - resource_id=default_route['uuid'], - parent_resource_id=router_id), - json.dumps(new_default_route), - cluster=cluster) - - -def update_explicit_routing_lrouter(cluster, router_id, - display_name, next_hop, routes=None): - update_implicit_routing_lrouter(cluster, router_id, display_name, next_hop) - if next_hop: - update_default_gw_explicit_routing_lrouter(cluster, - router_id, next_hop) - if routes is not None: - return update_explicit_routes_lrouter(cluster, router_id, routes) - - -@version_dependent -def update_lrouter(cluster, *args, **kwargs): - if kwargs.get('routes', None): - v = cluster.api_client.get_nvp_version() - if (v.major, v.minor) < (3, 2): - raise nvp_exc.NvpInvalidVersion(version=v) - return v - - def delete_network(cluster, net_id, lswitch_id): delete_networks(cluster, net_id, [lswitch_id]) @@ -669,13 +341,6 @@ def query_lswitch_lports(cluster, ls_uuid, fields="*", return do_request(HTTP_GET, uri, cluster=cluster)['results'] -def query_lrouter_lports(cluster, lr_uuid, fields="*", - filters=None, relations=None): - uri = _build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lr_uuid, - fields=fields, filters=filters, relations=relations) - return do_request(HTTP_GET, uri, cluster=cluster)['results'] - - def delete_port(cluster, switch, port): uri = "/ws.v1/lswitch/" + switch + "/lport/" + port try: @@ -884,129 +549,6 @@ def create_lport(cluster, lswitch_uuid, tenant_id, neutron_port_id, return result -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.""" - tags = [dict(scope='os_tid', tag=tenant_id), - dict(scope='q_port_id', tag=neutron_port_id), - dict(scope='quantum', tag=NEUTRON_VERSION)] - - lport_obj = dict( - admin_status_enabled=admin_status_enabled, - display_name=display_name, - tags=tags, - 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 = _build_uri_path(LROUTERPORT_RESOURCE, - parent_resource_id=lrouter_uuid) - result = do_request(HTTP_POST, path, json.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 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=[dict(scope='os_tid', tag=tenant_id), - dict(scope='q_port_id', tag=neutron_port_id), - dict(scope='quantum', tag=NEUTRON_VERSION)], - ip_addresses=ip_addresses, - type="LogicalRouterPortConfig" - ) - # Do not pass null items to NVP - for key in lport_obj.keys(): - if lport_obj[key] is None: - del lport_obj[key] - path = _build_uri_path(LROUTERPORT_RESOURCE, - lrouter_port_uuid, - parent_resource_id=lrouter_uuid) - result = do_request(HTTP_PUT, path, - json.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 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 delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid): - nvp_port = get_port(cluster, ls_uuid, lp_uuid, - relations="LogicalPortAttachment") - relations = nvp_port.get('_relations') - if relations: - att_data = relations.get('LogicalPortAttachment') - if att_data: - lrp_uuid = att_data.get('peer_port_uuid') - if lrp_uuid: - delete_router_lport(cluster, lr_uuid, lrp_uuid) - - -def find_router_gw_port(context, cluster, router_id): - """Retrieves the external gateway port for a NVP logical router.""" - - # Find the uuid of nvp ext gw logical router port - # TODO(salvatore-orlando): Consider storing it in Neutron DB - results = query_lrouter_lports( - cluster, router_id, - relations="LogicalPortAttachment") - for lport in results: - if '_relations' in lport: - attachment = lport['_relations'].get('LogicalPortAttachment') - if attachment and attachment.get('type') == 'L3GatewayAttachment': - return lport - - -def plug_router_port_attachment(cluster, router_id, port_id, - attachment_uuid, nvp_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 = _build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id, - is_attachment=True) - attach_obj = {} - attach_obj["type"] = nvp_attachment_type - if nvp_attachment_type == "PatchAttachment": - attach_obj["peer_port_uuid"] = attachment_uuid - elif nvp_attachment_type == "L3GatewayAttachment": - attach_obj["l3_gateway_service_uuid"] = attachment_uuid - if attachment_vlan: - attach_obj['vlan_id'] = attachment_vlan - else: - raise nvp_exc.NvpInvalidAttachmentType( - attachment_type=nvp_attachment_type) - return do_request(HTTP_PUT, uri, json.dumps(attach_obj), cluster=cluster) - - def get_port_status(cluster, lswitch_id, port_id): """Retrieve the operational status of the port.""" try: @@ -1176,243 +718,3 @@ def delete_security_profile(cluster, spid): LOG.warn(_("Unable to find security profile %s on NSX backend"), spid) raise - - -def _create_nat_match_obj(**kwargs): - nat_match_obj = {'ethertype': 'IPv4'} - delta = set(kwargs.keys()) - set(MATCH_KEYS) - if delta: - raise Exception(_("Invalid keys for NAT match: %s"), delta) - nat_match_obj.update(kwargs) - return nat_match_obj - - -def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj): - LOG.debug(_("Creating NAT rule: %s"), nat_rule_obj) - uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id) - return do_request(HTTP_POST, uri, json.dumps(nat_rule_obj), - cluster=cluster) - - -def _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj): - return {"to_source_ip_address_min": min_src_ip, - "to_source_ip_address_max": max_src_ip, - "type": "SourceNatRule", - "match": nat_match_obj} - - -def create_lrouter_nosnat_rule_v2(cluster, _router_id, _match_criteria=None): - LOG.info(_("No SNAT rules cannot be applied as they are not available in " - "this version of the NVP platform")) - - -def create_lrouter_nodnat_rule_v2(cluster, _router_id, _match_criteria=None): - LOG.info(_("No DNAT rules cannot be applied as they are not available in " - "this version of the NVP platform")) - - -def create_lrouter_snat_rule_v2(cluster, router_id, - min_src_ip, max_src_ip, match_criteria=None): - - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj) - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -def create_lrouter_dnat_rule_v2(cluster, router_id, dst_ip, - to_dst_port=None, match_criteria=None): - - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = { - "to_destination_ip_address_min": dst_ip, - "to_destination_ip_address_max": dst_ip, - "type": "DestinationNatRule", - "match": nat_match_obj - } - if to_dst_port: - nat_rule_obj['to_destination_port'] = to_dst_port - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -def create_lrouter_nosnat_rule_v3(cluster, router_id, order=None, - match_criteria=None): - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = { - "type": "NoSourceNatRule", - "match": nat_match_obj - } - if order: - nat_rule_obj['order'] = order - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -def create_lrouter_nodnat_rule_v3(cluster, router_id, order=None, - match_criteria=None): - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = { - "type": "NoDestinationNatRule", - "match": nat_match_obj - } - if order: - nat_rule_obj['order'] = order - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -def create_lrouter_snat_rule_v3(cluster, router_id, min_src_ip, max_src_ip, - order=None, match_criteria=None): - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj) - if order: - nat_rule_obj['order'] = order - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -def create_lrouter_dnat_rule_v3(cluster, router_id, dst_ip, to_dst_port=None, - order=None, match_criteria=None): - - nat_match_obj = _create_nat_match_obj(**match_criteria) - nat_rule_obj = { - "to_destination_ip_address": dst_ip, - "type": "DestinationNatRule", - "match": nat_match_obj - } - if to_dst_port: - nat_rule_obj['to_destination_port'] = to_dst_port - if order: - nat_rule_obj['order'] = order - return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj) - - -@version_dependent -def create_lrouter_dnat_rule(cluster, *args, **kwargs): - pass - - -@version_dependent -def create_lrouter_snat_rule(cluster, *args, **kwargs): - pass - - -@version_dependent -def create_lrouter_nosnat_rule(cluster, *args, **kwargs): - pass - - -@version_dependent -def create_lrouter_nodnat_rule(cluster, *args, **kwargs): - pass - - -def delete_nat_rules_by_match(cluster, router_id, rule_type, - max_num_expected, - min_num_expected=0, - **kwargs): - # remove nat rules - nat_rules = query_nat_rules(cluster, router_id) - to_delete_ids = [] - for r in nat_rules: - if (r['type'] != rule_type): - continue - - for key, value in kwargs.iteritems(): - if not (key in r['match'] and r['match'][key] == value): - break - else: - to_delete_ids.append(r['uuid']) - if not (len(to_delete_ids) in - range(min_num_expected, max_num_expected + 1)): - raise nvp_exc.NvpNatRuleMismatch(actual_rules=len(to_delete_ids), - min_rules=min_num_expected, - max_rules=max_num_expected) - - for rule_id in to_delete_ids: - delete_router_nat_rule(cluster, router_id, rule_id) - - -def delete_router_nat_rule(cluster, router_id, rule_id): - uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id) - do_request(HTTP_DELETE, uri, cluster=cluster) - - -def query_nat_rules(cluster, router_id, fields="*", filters=None): - uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id, - fields=fields, filters=filters) - return get_all_query_pages(uri, cluster) - - -# NOTE(salvatore-orlando): The following FIXME applies in general to -# each operation on list attributes. -# FIXME(salvatore-orlando): need a lock around the list of IPs on an iface -def update_lrouter_port_ips(cluster, lrouter_id, lport_id, - ips_to_add, ips_to_remove): - uri = _build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id) - try: - port = 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) - do_request(HTTP_PUT, uri, json.dumps(port), cluster=cluster) - except exception.NotFound as e: - # 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 nvp_exc.NvpPluginException(err_msg=msg) - except NvpApiClient.NvpApiException as e: - msg = _("An exception occurred while updating IP addresses on a " - "router logical port:%s") % str(e) - LOG.exception(msg) - raise nvp_exc.NvpPluginException(err_msg=msg) - - -DEFAULT = -1 -NVPLIB_FUNC_DICT = { - 'create_lrouter': { - 2: {DEFAULT: create_implicit_routing_lrouter, }, - 3: {DEFAULT: create_implicit_routing_lrouter, - 1: create_implicit_routing_lrouter_with_distribution, - 2: create_explicit_routing_lrouter, }, }, - 'update_lrouter': { - 2: {DEFAULT: update_implicit_routing_lrouter, }, - 3: {DEFAULT: update_implicit_routing_lrouter, - 2: update_explicit_routing_lrouter, }, }, - 'create_lrouter_dnat_rule': { - 2: {DEFAULT: create_lrouter_dnat_rule_v2, }, - 3: {DEFAULT: create_lrouter_dnat_rule_v3, }, }, - 'create_lrouter_snat_rule': { - 2: {DEFAULT: create_lrouter_snat_rule_v2, }, - 3: {DEFAULT: create_lrouter_snat_rule_v3, }, }, - 'create_lrouter_nosnat_rule': { - 2: {DEFAULT: create_lrouter_nosnat_rule_v2, }, - 3: {DEFAULT: create_lrouter_nosnat_rule_v3, }, }, - 'create_lrouter_nodnat_rule': { - 2: {DEFAULT: create_lrouter_nodnat_rule_v2, }, - 3: {DEFAULT: create_lrouter_nodnat_rule_v3, }, }, - 'get_default_route_explicit_routing_lrouter': { - 3: {DEFAULT: get_default_route_explicit_routing_lrouter_v32, - 2: get_default_route_explicit_routing_lrouter_v32, }, }, -} - - -def get_function_by_version(func_name, nvp_ver): - if nvp_ver: - if nvp_ver.major not in NVPLIB_FUNC_DICT[func_name]: - major = max(NVPLIB_FUNC_DICT[func_name].keys()) - minor = max(NVPLIB_FUNC_DICT[func_name][major].keys()) - if major > nvp_ver.major: - raise NotImplementedError(_("Operation may not be supported")) - else: - major = nvp_ver.major - minor = nvp_ver.minor - if nvp_ver.minor not in NVPLIB_FUNC_DICT[func_name][major]: - minor = DEFAULT - return NVPLIB_FUNC_DICT[func_name][major][minor] - else: - msg = _('NVP version is not set. Unable to complete request ' - 'correctly. Check log for NVP communication errors.') - raise NvpApiClient.ServiceUnavailable(message=msg) diff --git a/neutron/tests/unit/nicira/nsxlib/test_router.py b/neutron/tests/unit/nicira/nsxlib/test_router.py new file mode 100644 index 000000000..59a9e74d6 --- /dev/null +++ b/neutron/tests/unit/nicira/nsxlib/test_router.py @@ -0,0 +1,922 @@ +# Copyright (c) 2014 VMware, Inc. +# +# 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 mock + +from neutron.common import exceptions +from neutron.openstack.common import uuidutils +from neutron.plugins.nicira.common import exceptions as nvp_exc +from neutron.plugins.nicira.nsxlib import router as routerlib +from neutron.plugins.nicira import NvpApiClient +from neutron.plugins.nicira import nvplib +from neutron.tests.unit.nicira.test_nvplib import NsxlibNegativeBaseTestCase +from neutron.tests.unit.nicira.test_nvplib import NvplibTestCase +from neutron.tests.unit import test_api_v2 + +_uuid = test_api_v2._uuid + + +class TestNatRules(NvplibTestCase): + + def _test_create_lrouter_dnat_rule(self, version): + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + new=lambda: NvpApiClient.NVPVersion(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 = nvplib._build_uri_path(routerlib.LROUTERNAT_RESOURCE, + nat_rule['uuid'], + lrouter['uuid']) + resp_obj = nvplib.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 test_create_lrouter_dnat_rule_v2(self): + self._test_create_lrouter_dnat_rule('2.9') + + def test_create_lrouter_dnat_rule_v31(self): + self._test_create_lrouter_dnat_rule('3.1') + + +class TestExplicitLRouters(NvplibTestCase): + + def setUp(self): + self.fake_version = '3.2' + super(TestExplicitLRouters, self).setUp() + + def _get_lrouter(self, tenant_id, router_name, router_id, relations=None): + schema = '/ws.v1/schema/RoutingTableRoutingConfig' + + router = {'display_name': router_name, + 'uuid': router_id, + 'tags': [{'scope': 'quantum', 'tag': nvplib.NEUTRON_VERSION}, + {'scope': 'os_tid', 'tag': '%s' % tenant_id}], + 'distributed': False, + 'routing_config': {'type': 'RoutingTableRoutingConfig', + '_schema': schema}, + '_schema': schema, + 'nat_synchronization_enabled': True, + 'replication_mode': 'service', + 'type': 'LogicalRouterConfig', + '_href': '/ws.v1/lrouter/%s' % router_id, } + if relations: + router['_relations'] = relations + return router + + def _get_single_route(self, router_id, route_id='fake_route_id_0', + prefix='0.0.0.0/0', next_hop_ip='1.1.1.1'): + return {'protocol': 'static', + '_href': '/ws.v1/lrouter/%s/rib/%s' % (router_id, route_id), + 'prefix': prefix, + '_schema': '/ws.v1/schema/RoutingTableEntry', + 'next_hop_ip': next_hop_ip, + 'action': 'accept', + 'uuid': route_id} + + def test_prepare_body_with_implicit_routing_config(self): + router_name = 'fake_router_name' + tenant_id = 'fake_tenant_id' + neutron_router_id = 'pipita_higuain' + router_type = 'SingleDefaultRouteImplicitRoutingConfig' + route_config = { + 'default_route_next_hop': {'gateway_ip_address': 'fake_address', + 'type': 'RouterNextHop'}, } + body = routerlib._prepare_lrouter_body(router_name, neutron_router_id, + tenant_id, router_type, + **route_config) + expected = {'display_name': 'fake_router_name', + 'routing_config': { + 'default_route_next_hop': + {'gateway_ip_address': 'fake_address', + 'type': 'RouterNextHop'}, + 'type': 'SingleDefaultRouteImplicitRoutingConfig'}, + 'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'}, + {'scope': 'q_router_id', 'tag': 'pipita_higuain'}, + {'scope': 'quantum', + 'tag': nvplib.NEUTRON_VERSION}], + 'type': 'LogicalRouterConfig'} + self.assertEqual(expected, body) + + def test_prepare_body_without_routing_config(self): + router_name = 'fake_router_name' + tenant_id = 'fake_tenant_id' + neutron_router_id = 'marekiaro_hamsik' + router_type = 'RoutingTableRoutingConfig' + body = routerlib._prepare_lrouter_body(router_name, neutron_router_id, + tenant_id, router_type) + expected = {'display_name': 'fake_router_name', + 'routing_config': {'type': 'RoutingTableRoutingConfig'}, + 'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'}, + {'scope': 'q_router_id', + 'tag': 'marekiaro_hamsik'}, + {'scope': 'quantum', + 'tag': nvplib.NEUTRON_VERSION}], + 'type': 'LogicalRouterConfig'} + self.assertEqual(expected, body) + + def test_get_lrouter(self): + tenant_id = 'fake_tenant_id' + router_name = 'fake_router_name' + router_id = 'fake_router_id' + relations = { + 'LogicalRouterStatus': + {'_href': '/ws.v1/lrouter/%s/status' % router_id, + 'lport_admin_up_count': 1, + '_schema': '/ws.v1/schema/LogicalRouterStatus', + 'lport_count': 1, + 'fabric_status': True, + 'type': 'LogicalRouterStatus', + 'lport_link_up_count': 0, }, } + + with mock.patch.object(routerlib, 'do_request', + return_value=self._get_lrouter(tenant_id, + router_name, + router_id, + relations)): + lrouter = routerlib.get_lrouter(self.fake_cluster, router_id) + self.assertTrue( + lrouter['_relations']['LogicalRouterStatus']['fabric_status']) + + def test_create_lrouter(self): + tenant_id = 'fake_tenant_id' + router_name = 'fake_router_name' + router_id = 'fake_router_id' + nexthop_ip = '10.0.0.1' + with mock.patch.object( + routerlib, 'do_request', + return_value=self._get_lrouter(tenant_id, + router_name, + router_id)): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + tenant_id, + router_name, nexthop_ip) + self.assertEqual(lrouter['routing_config']['type'], + 'RoutingTableRoutingConfig') + self.assertNotIn('default_route_next_hop', + lrouter['routing_config']) + + def test_update_lrouter_with_no_routes(self): + router_id = 'fake_router_id' + new_routes = [{"nexthop": "10.0.0.2", + "destination": "169.254.169.0/30"}, ] + + nvp_routes = [self._get_single_route(router_id)] + with mock.patch.object(routerlib, 'get_explicit_routes_lrouter', + return_value=nvp_routes): + with mock.patch.object(routerlib, 'create_explicit_route_lrouter', + return_value='fake_uuid'): + old_routes = routerlib.update_explicit_routes_lrouter( + self.fake_cluster, router_id, new_routes) + self.assertEqual(old_routes, nvp_routes) + + def test_update_lrouter_with_no_routes_raise_nsx_exception(self): + router_id = 'fake_router_id' + new_routes = [{"nexthop": "10.0.0.2", + "destination": "169.254.169.0/30"}, ] + + nsx_routes = [self._get_single_route(router_id)] + with mock.patch.object(routerlib, 'get_explicit_routes_lrouter', + return_value=nsx_routes): + with mock.patch.object(routerlib, 'create_explicit_route_lrouter', + side_effect=NvpApiClient.NvpApiException): + self.assertRaises(NvpApiClient.NvpApiException, + routerlib.update_explicit_routes_lrouter, + self.fake_cluster, router_id, new_routes) + + def test_update_lrouter_with_routes(self): + router_id = 'fake_router_id' + new_routes = [{"next_hop_ip": "10.0.0.2", + "prefix": "169.254.169.0/30"}, ] + + nsx_routes = [self._get_single_route(router_id), + self._get_single_route(router_id, 'fake_route_id_1', + '0.0.0.1/24', '10.0.0.3'), + self._get_single_route(router_id, 'fake_route_id_2', + '0.0.0.2/24', '10.0.0.4'), ] + + with mock.patch.object(routerlib, 'get_explicit_routes_lrouter', + return_value=nsx_routes): + with mock.patch.object(routerlib, 'delete_explicit_route_lrouter', + return_value=None): + with mock.patch.object(routerlib, + 'create_explicit_route_lrouter', + return_value='fake_uuid'): + old_routes = routerlib.update_explicit_routes_lrouter( + self.fake_cluster, router_id, new_routes) + self.assertEqual(old_routes, nsx_routes) + + def test_update_lrouter_with_routes_raises_nsx_expception(self): + router_id = 'fake_router_id' + new_routes = [{"nexthop": "10.0.0.2", + "destination": "169.254.169.0/30"}, ] + + nsx_routes = [self._get_single_route(router_id), + self._get_single_route(router_id, 'fake_route_id_1', + '0.0.0.1/24', '10.0.0.3'), + self._get_single_route(router_id, 'fake_route_id_2', + '0.0.0.2/24', '10.0.0.4'), ] + + with mock.patch.object(routerlib, 'get_explicit_routes_lrouter', + return_value=nsx_routes): + with mock.patch.object(routerlib, 'delete_explicit_route_lrouter', + side_effect=NvpApiClient.NvpApiException): + with mock.patch.object( + routerlib, 'create_explicit_route_lrouter', + return_value='fake_uuid'): + self.assertRaises( + NvpApiClient.NvpApiException, + routerlib.update_explicit_routes_lrouter, + self.fake_cluster, router_id, new_routes) + + +class RouterNegativeTestCase(NsxlibNegativeBaseTestCase): + + def test_create_lrouter_on_failure(self): + self.assertRaises(nvplib.NvpApiClient.NvpApiException, + routerlib.create_lrouter, + self.fake_cluster, + uuidutils.generate_uuid(), + 'pluto', + 'fake_router', + 'my_hop') + + def test_delete_lrouter_on_failure(self): + self.assertRaises(nvplib.NvpApiClient.NvpApiException, + routerlib.delete_lrouter, + self.fake_cluster, + 'fake_router') + + def test_get_lrouter_on_failure(self): + self.assertRaises(nvplib.NvpApiClient.NvpApiException, + routerlib.get_lrouter, + self.fake_cluster, + 'fake_router') + + def test_update_lrouter_on_failure(self): + self.assertRaises(nvplib.NvpApiClient.NvpApiException, + routerlib.update_lrouter, + self.fake_cluster, + 'fake_router', + 'pluto', + 'new_hop') + + +class TestLogicalRouters(NvplibTestCase): + + def _verify_lrouter(self, res_lrouter, + expected_uuid, + expected_display_name, + expected_nexthop, + expected_tenant_id, + expected_neutron_id=None, + expected_distributed=None): + self.assertEqual(res_lrouter['uuid'], expected_uuid) + nexthop = (res_lrouter['routing_config'] + ['default_route_next_hop']['gateway_ip_address']) + self.assertEqual(nexthop, expected_nexthop) + router_tags = self._build_tag_dict(res_lrouter['tags']) + self.assertIn('os_tid', router_tags) + self.assertEqual(res_lrouter['display_name'], expected_display_name) + self.assertEqual(expected_tenant_id, router_tags['os_tid']) + if expected_distributed is not None: + self.assertEqual(expected_distributed, + res_lrouter['distributed']) + if expected_neutron_id: + self.assertIn('q_router_id', router_tags) + self.assertEqual(expected_neutron_id, router_tags['q_router_id']) + + def test_get_lrouters(self): + lrouter_uuids = [routerlib.create_lrouter( + self.fake_cluster, 'whatever', 'pippo', 'fake-lrouter-%s' % k, + '10.0.0.1')['uuid'] for k in range(3)] + routers = routerlib.get_lrouters(self.fake_cluster, 'pippo') + for router in routers: + self.assertIn(router['uuid'], lrouter_uuids) + + def _create_lrouter(self, version, neutron_id=None, distributed=None): + with mock.patch.object( + self.fake_cluster.api_client, 'get_nvp_version', + return_value=NvpApiClient.NVPVersion(version)): + if not neutron_id: + neutron_id = uuidutils.generate_uuid() + lrouter = routerlib.create_lrouter( + self.fake_cluster, neutron_id, 'pippo', + 'fake-lrouter', '10.0.0.1', distributed=distributed) + return routerlib.get_lrouter(self.fake_cluster, + lrouter['uuid']) + + def test_create_and_get_lrouter_v30(self): + neutron_id = uuidutils.generate_uuid() + res_lrouter = self._create_lrouter('3.0', neutron_id=neutron_id) + self._verify_lrouter(res_lrouter, res_lrouter['uuid'], + 'fake-lrouter', '10.0.0.1', 'pippo', + expected_neutron_id=neutron_id) + + def test_create_and_get_lrouter_v31_centralized(self): + neutron_id = uuidutils.generate_uuid() + res_lrouter = self._create_lrouter('3.1', neutron_id=neutron_id, + distributed=False) + self._verify_lrouter(res_lrouter, res_lrouter['uuid'], + 'fake-lrouter', '10.0.0.1', 'pippo', + expected_neutron_id=neutron_id, + expected_distributed=False) + + def test_create_and_get_lrouter_v31_distributed(self): + neutron_id = uuidutils.generate_uuid() + res_lrouter = self._create_lrouter('3.1', neutron_id=neutron_id, + distributed=True) + self._verify_lrouter(res_lrouter, res_lrouter['uuid'], + 'fake-lrouter', '10.0.0.1', 'pippo', + expected_neutron_id=neutron_id, + expected_distributed=True) + + def test_create_and_get_lrouter_name_exceeds_40chars(self): + neutron_id = uuidutils.generate_uuid() + display_name = '*' * 50 + lrouter = routerlib.create_lrouter(self.fake_cluster, + neutron_id, + 'pippo', + display_name, + '10.0.0.1') + res_lrouter = routerlib.get_lrouter(self.fake_cluster, + lrouter['uuid']) + self._verify_lrouter(res_lrouter, lrouter['uuid'], + '*' * 40, '10.0.0.1', 'pippo', + expected_neutron_id=neutron_id) + + def _test_version_dependent_update_lrouter(self, version): + def foo(*args, **kwargs): + return version + + foo_func_dict = { + 'update_lrouter': { + 2: {-1: foo}, + 3: {-1: foo, 2: foo} + } + } + + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + return_value=NvpApiClient.NVPVersion(version)): + with mock.patch.dict(routerlib.ROUTER_FUNC_DICT, + foo_func_dict, clear=True): + return routerlib.update_lrouter( + self.fake_cluster, 'foo_router_id', 'foo_router_name', + 'foo_nexthop', routes={'foo_destination': 'foo_address'}) + + def test_version_dependent_update_lrouter_old_versions(self): + self.assertRaises(nvp_exc.NvpInvalidVersion, + self._test_version_dependent_update_lrouter, + "2.9") + self.assertRaises(nvp_exc.NvpInvalidVersion, + self._test_version_dependent_update_lrouter, + "3.0") + self.assertRaises(nvp_exc.NvpInvalidVersion, + self._test_version_dependent_update_lrouter, + "3.1") + + def test_version_dependent_update_lrouter_new_versions(self): + self.assertEqual("3.2", + self._test_version_dependent_update_lrouter("3.2")) + self.assertEqual("4.0", + self._test_version_dependent_update_lrouter("4.0")) + self.assertEqual("4.1", + self._test_version_dependent_update_lrouter("4.1")) + + def test_update_lrouter_no_nexthop(self): + neutron_id = uuidutils.generate_uuid() + lrouter = routerlib.create_lrouter(self.fake_cluster, + neutron_id, + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter = routerlib.update_lrouter(self.fake_cluster, + lrouter['uuid'], + 'new_name', + None) + res_lrouter = routerlib.get_lrouter(self.fake_cluster, + lrouter['uuid']) + self._verify_lrouter(res_lrouter, lrouter['uuid'], + 'new_name', '10.0.0.1', 'pippo', + expected_neutron_id=neutron_id) + + def test_update_lrouter(self): + neutron_id = uuidutils.generate_uuid() + lrouter = routerlib.create_lrouter(self.fake_cluster, + neutron_id, + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter = routerlib.update_lrouter(self.fake_cluster, + lrouter['uuid'], + 'new_name', + '192.168.0.1') + res_lrouter = routerlib.get_lrouter(self.fake_cluster, + lrouter['uuid']) + self._verify_lrouter(res_lrouter, lrouter['uuid'], + 'new_name', '192.168.0.1', 'pippo', + expected_neutron_id=neutron_id) + + def test_update_nonexistent_lrouter_raises(self): + self.assertRaises(exceptions.NotFound, + routerlib.update_lrouter, + self.fake_cluster, + 'whatever', + 'foo', '9.9.9.9') + + def test_delete_lrouter(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + routerlib.delete_lrouter(self.fake_cluster, lrouter['uuid']) + self.assertRaises(exceptions.NotFound, + routerlib.get_lrouter, + self.fake_cluster, + lrouter['uuid']) + + def test_query_lrouter_ports(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + router_port_uuids = [routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', + 'qp_id_%s' % k, 'port-%s' % k, True, + ['192.168.0.%s' % k], '00:11:22:33:44:55')['uuid'] + for k in range(3)] + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 3) + for res_port in ports: + self.assertIn(res_port['uuid'], router_port_uuids) + + def test_query_lrouter_lports_nonexistent_lrouter_raises(self): + self.assertRaises( + exceptions.NotFound, routerlib.create_router_lport, + self.fake_cluster, 'booo', 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + + def test_create_and_get_lrouter_port(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + res_port = ports[0] + port_tags = self._build_tag_dict(res_port['tags']) + self.assertEqual(['192.168.0.1'], res_port['ip_addresses']) + self.assertIn('os_tid', port_tags) + self.assertIn('q_port_id', port_tags) + self.assertEqual('pippo', port_tags['os_tid']) + self.assertEqual('neutron_port_id', port_tags['q_port_id']) + + def test_create_lrouter_port_nonexistent_router_raises(self): + self.assertRaises( + exceptions.NotFound, routerlib.create_router_lport, + self.fake_cluster, 'booo', 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + + def test_update_lrouter_port(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + routerlib.update_router_lport( + self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], + 'pippo', 'another_port_id', 'name', False, + ['192.168.0.1', '10.10.10.254']) + + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + res_port = ports[0] + port_tags = self._build_tag_dict(res_port['tags']) + self.assertEqual(['192.168.0.1', '10.10.10.254'], + res_port['ip_addresses']) + self.assertEqual('False', res_port['admin_status_enabled']) + self.assertIn('os_tid', port_tags) + self.assertIn('q_port_id', port_tags) + self.assertEqual('pippo', port_tags['os_tid']) + self.assertEqual('another_port_id', port_tags['q_port_id']) + + def test_update_lrouter_port_nonexistent_router_raises(self): + self.assertRaises( + exceptions.NotFound, routerlib.update_router_lport, + self.fake_cluster, 'boo-router', 'boo-port', 'pippo', + 'neutron_port_id', 'name', True, ['192.168.0.1']) + + def test_update_lrouter_port_nonexistent_port_raises(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + self.assertRaises( + exceptions.NotFound, routerlib.update_router_lport, + self.fake_cluster, lrouter['uuid'], 'boo-port', 'pippo', + 'neutron_port_id', 'name', True, ['192.168.0.1']) + + def test_delete_lrouter_port(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [], + '00:11:22:33:44:55') + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + routerlib.delete_router_lport(self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid']) + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertFalse(len(ports)) + + def test_delete_lrouter_port_nonexistent_router_raises(self): + self.assertRaises(exceptions.NotFound, + routerlib.delete_router_lport, + self.fake_cluster, 'xyz', 'abc') + + def test_delete_lrouter_port_nonexistent_port_raises(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + self.assertRaises(exceptions.NotFound, + routerlib.delete_router_lport, + self.fake_cluster, lrouter['uuid'], 'abc') + + def test_delete_peer_lrouter_port(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [], + '00:11:22:33:44:55') + + def fakegetport(*args, **kwargs): + return {'_relations': {'LogicalPortAttachment': + {'peer_port_uuid': lrouter_port['uuid']}}} + # mock get_port + with mock.patch.object(routerlib, 'get_port', new=fakegetport): + routerlib.delete_peer_router_lport(self.fake_cluster, + lrouter_port['uuid'], + 'whatwever', 'whatever') + + def test_update_lrouter_port_ips_add_only(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + routerlib.update_lrouter_port_ips( + self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], + ['10.10.10.254'], []) + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + res_port = ports[0] + self.assertEqual(['10.10.10.254', '192.168.0.1'], + res_port['ip_addresses']) + + def test_update_lrouter_port_ips_remove_only(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1', '10.10.10.254'], + '00:11:22:33:44:55') + routerlib.update_lrouter_port_ips( + self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], + [], ['10.10.10.254']) + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + res_port = ports[0] + self.assertEqual(['192.168.0.1'], res_port['ip_addresses']) + + def test_update_lrouter_port_ips_add_and_remove(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + routerlib.update_lrouter_port_ips( + self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], + ['10.10.10.254'], ['192.168.0.1']) + ports = routerlib.query_lrouter_lports( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(ports), 1) + res_port = ports[0] + self.assertEqual(['10.10.10.254'], res_port['ip_addresses']) + + def test_update_lrouter_port_ips_nonexistent_router_raises(self): + self.assertRaises( + nvp_exc.NvpPluginException, routerlib.update_lrouter_port_ips, + self.fake_cluster, 'boo-router', 'boo-port', [], []) + + def test_update_lrouter_port_ips_nsx_exception_raises(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + + def raise_nsx_exc(*args, **kwargs): + raise NvpApiClient.NvpApiException() + + with mock.patch.object(routerlib, 'do_request', new=raise_nsx_exc): + self.assertRaises( + nvp_exc.NvpPluginException, routerlib.update_lrouter_port_ips, + self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid'], [], []) + + def test_plug_lrouter_port_patch_attachment(self): + tenant_id = 'pippo' + transport_zones_config = [{'zone_uuid': _uuid(), + 'transport_type': 'stt'}] + lswitch = nvplib.create_lswitch(self.fake_cluster, + _uuid(), + tenant_id, 'fake-switch', + transport_zones_config) + lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'], + tenant_id, 'xyz', + 'name', 'device_id', True) + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + tenant_id, + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55:66') + result = routerlib.plug_router_port_attachment( + self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid'], + lport['uuid'], 'PatchAttachment') + self.assertEqual(lport['uuid'], + result['LogicalPortAttachment']['peer_port_uuid']) + + def test_plug_lrouter_port_l3_gw_attachment(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55:66') + result = routerlib.plug_router_port_attachment( + self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid'], + 'gw_att', 'L3GatewayAttachment') + self.assertEqual( + 'gw_att', + result['LogicalPortAttachment']['l3_gateway_service_uuid']) + + def test_plug_lrouter_port_l3_gw_attachment_with_vlan(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + result = routerlib.plug_router_port_attachment( + self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid'], + 'gw_att', 'L3GatewayAttachment', 123) + self.assertEqual( + 'gw_att', + result['LogicalPortAttachment']['l3_gateway_service_uuid']) + self.assertEqual( + '123', + result['LogicalPortAttachment']['vlan_id']) + + def test_plug_lrouter_port_invalid_attachment_type_raises(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + lrouter_port = routerlib.create_router_lport( + self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', + 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') + self.assertRaises(nvp_exc.NvpInvalidAttachmentType, + routerlib.plug_router_port_attachment, + self.fake_cluster, lrouter['uuid'], + lrouter_port['uuid'], 'gw_att', 'BadType') + + def _test_create_router_snat_rule(self, version): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + new=lambda: NvpApiClient.NVPVersion(version)): + routerlib.create_lrouter_snat_rule( + self.fake_cluster, lrouter['uuid'], + '10.0.0.2', '10.0.0.2', order=200, + match_criteria={'source_ip_addresses': '192.168.0.24'}) + rules = routerlib.query_nat_rules( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 1) + + def test_create_router_snat_rule_v3(self): + self._test_create_router_snat_rule('3.0') + + def test_create_router_snat_rule_v2(self): + self._test_create_router_snat_rule('2.0') + + def _test_create_router_dnat_rule(self, version, dest_port=None): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + return_value=NvpApiClient.NVPVersion(version)): + routerlib.create_lrouter_dnat_rule( + self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200, + dest_port=dest_port, + match_criteria={'destination_ip_addresses': '10.0.0.3'}) + rules = routerlib.query_nat_rules( + self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 1) + + def test_create_router_dnat_rule_v3(self): + self._test_create_router_dnat_rule('3.0') + + def test_create_router_dnat_rule_v2(self): + self._test_create_router_dnat_rule('2.0') + + def test_create_router_dnat_rule_v2_with_destination_port(self): + self._test_create_router_dnat_rule('2.0', 8080) + + def test_create_router_dnat_rule_v3_with_destination_port(self): + self._test_create_router_dnat_rule('3.0', 8080) + + def test_create_router_snat_rule_invalid_match_keys_raises(self): + # In this case the version does not make a difference + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + new=lambda: '2.0'): + self.assertRaises(AttributeError, + routerlib.create_lrouter_snat_rule, + self.fake_cluster, lrouter['uuid'], + '10.0.0.2', '10.0.0.2', order=200, + match_criteria={'foo': 'bar'}) + + def _test_create_router_nosnat_rule(self, version, expected=1): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + new=lambda: NvpApiClient.NVPVersion(version)): + routerlib.create_lrouter_nosnat_rule( + self.fake_cluster, lrouter['uuid'], + order=100, + match_criteria={'destination_ip_addresses': '192.168.0.0/24'}) + rules = routerlib.query_nat_rules( + self.fake_cluster, lrouter['uuid']) + # NoSNAT rules do not exist in V2 + self.assertEqual(len(rules), expected) + + def test_create_router_nosnat_rule_v2(self): + self._test_create_router_nosnat_rule('2.0', expected=0) + + def test_create_router_nosnat_rule_v3(self): + self._test_create_router_nosnat_rule('3.0') + + def _prepare_nat_rules_for_delete_tests(self): + lrouter = routerlib.create_lrouter(self.fake_cluster, + uuidutils.generate_uuid(), + 'pippo', + 'fake-lrouter', + '10.0.0.1') + # v2 or v3 makes no difference for this test + with mock.patch.object(self.fake_cluster.api_client, + 'get_nvp_version', + new=lambda: NvpApiClient.NVPVersion('2.0')): + routerlib.create_lrouter_snat_rule( + self.fake_cluster, lrouter['uuid'], + '10.0.0.2', '10.0.0.2', order=220, + match_criteria={'source_ip_addresses': '192.168.0.0/24'}) + routerlib.create_lrouter_snat_rule( + self.fake_cluster, lrouter['uuid'], + '10.0.0.3', '10.0.0.3', order=200, + match_criteria={'source_ip_addresses': '192.168.0.2/32'}) + routerlib.create_lrouter_dnat_rule( + self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200, + match_criteria={'destination_ip_addresses': '10.0.0.3'}) + return lrouter + + def test_delete_router_nat_rules_by_match_on_destination_ip(self): + lrouter = self._prepare_nat_rules_for_delete_tests() + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + routerlib.delete_nat_rules_by_match( + self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 1, 1, + destination_ip_addresses='10.0.0.3') + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 2) + + def test_delete_router_nat_rules_by_match_on_source_ip(self): + lrouter = self._prepare_nat_rules_for_delete_tests() + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + routerlib.delete_nat_rules_by_match( + self.fake_cluster, lrouter['uuid'], 'SourceNatRule', 1, 1, + source_ip_addresses='192.168.0.2/32') + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 2) + + def test_delete_router_nat_rules_by_match_no_match_expected(self): + lrouter = self._prepare_nat_rules_for_delete_tests() + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + routerlib.delete_nat_rules_by_match( + self.fake_cluster, lrouter['uuid'], 'SomeWeirdType', 0) + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + routerlib.delete_nat_rules_by_match( + self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 0, + destination_ip_addresses='99.99.99.99') + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + + def test_delete_router_nat_rules_by_match_no_match_raises(self): + lrouter = self._prepare_nat_rules_for_delete_tests() + rules = routerlib.query_nat_rules(self.fake_cluster, lrouter['uuid']) + self.assertEqual(len(rules), 3) + self.assertRaises( + nvp_exc.NvpNatRuleMismatch, + routerlib.delete_nat_rules_by_match, + self.fake_cluster, lrouter['uuid'], + 'SomeWeirdType', 1, 1) diff --git a/neutron/tests/unit/nicira/nsxlib/test_versioning.py b/neutron/tests/unit/nicira/nsxlib/test_versioning.py new file mode 100644 index 000000000..072c3f645 --- /dev/null +++ b/neutron/tests/unit/nicira/nsxlib/test_versioning.py @@ -0,0 +1,57 @@ +# Copyright (c) 2014 VMware, Inc. +# +# 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 neutron.plugins.nicira.nsxlib import router as routerlib +from neutron.plugins.nicira.nsxlib import versioning +from neutron.plugins.nicira import NvpApiClient +from neutron.tests import base + + +class TestVersioning(base.BaseTestCase): + + def test_function_handling_missing_minor(self): + version = NvpApiClient.NVPVersion('2.0') + function = versioning.get_function_by_version( + routerlib.ROUTER_FUNC_DICT, 'create_lrouter', version) + self.assertEqual(routerlib.create_implicit_routing_lrouter, + function) + + def test_function_handling_with_both_major_and_minor(self): + version = NvpApiClient.NVPVersion('3.2') + function = versioning.get_function_by_version( + routerlib.ROUTER_FUNC_DICT, 'create_lrouter', version) + self.assertEqual(routerlib.create_explicit_routing_lrouter, + function) + + def test_function_handling_with_newer_major(self): + version = NvpApiClient.NVPVersion('5.2') + function = versioning.get_function_by_version( + routerlib.ROUTER_FUNC_DICT, 'create_lrouter', version) + self.assertEqual(routerlib.create_explicit_routing_lrouter, + function) + + def test_function_handling_with_obsolete_major(self): + version = NvpApiClient.NVPVersion('1.2') + self.assertRaises(NotImplementedError, + versioning.get_function_by_version, + routerlib.ROUTER_FUNC_DICT, + 'create_lrouter', version) + + def test_function_handling_with_unknown_version(self): + self.assertRaises(NvpApiClient.ServiceUnavailable, + versioning.get_function_by_version, + routerlib.ROUTER_FUNC_DICT, + 'create_lrouter', None) diff --git a/neutron/tests/unit/nicira/test_nicira_plugin.py b/neutron/tests/unit/nicira/test_nicira_plugin.py index bdb848dfa..de6796912 100644 --- a/neutron/tests/unit/nicira/test_nicira_plugin.py +++ b/neutron/tests/unit/nicira/test_nicira_plugin.py @@ -638,7 +638,7 @@ class TestNiciraL3NatTestCase(NiciraL3NatTest, self._test_router_create_with_distributed(True, True) def test_router_create_distributed_with_new_nvp_versions(self): - with mock.patch.object(nvplib, 'create_explicit_route_lrouter'): + with mock.patch.object(nsxlib.router, 'create_explicit_route_lrouter'): self._test_router_create_with_distributed(True, True, '3.2') self._test_router_create_with_distributed(True, True, '4.0') self._test_router_create_with_distributed(True, True, '4.1') @@ -655,12 +655,13 @@ class TestNiciraL3NatTestCase(NiciraL3NatTest, def test_router_create_on_obsolete_platform(self): def obsolete_response(*args, **kwargs): - response = nvplib._create_implicit_routing_lrouter(*args, **kwargs) + response = (nsxlib.router. + _create_implicit_routing_lrouter(*args, **kwargs)) response.pop('distributed') return response with mock.patch.object( - nvplib, 'create_lrouter', new=obsolete_response): + nsxlib.router, 'create_lrouter', new=obsolete_response): self._test_router_create_with_distributed(None, False, '2.2') def _create_router_with_gw_info_for_test(self, subnet): @@ -673,7 +674,7 @@ class TestNiciraL3NatTestCase(NiciraL3NatTest, return router_req.get_response(self.ext_api) def test_router_create_nvp_error_returns_500(self, vlan_id=None): - with mock.patch.object(nvplib, + with mock.patch.object(nsxlib.router, 'create_router_lport', side_effect=NvpApiClient.NvpApiException): with self._create_l3_ext_network(vlan_id) as net: @@ -1019,7 +1020,7 @@ class TestNiciraL3NatTestCase(NiciraL3NatTest, with self._create_l3_ext_network() as net: with self.subnet(network=net) as s: with mock.patch.object( - nvplib, + nsxlib.router, 'do_request', side_effect=nvp_exc.MaintenanceInProgress): data = {'router': {'tenant_id': 'whatever'}} diff --git a/neutron/tests/unit/nicira/test_nsx_utils.py b/neutron/tests/unit/nicira/test_nsx_utils.py index 9aad462da..6262fdc3d 100644 --- a/neutron/tests/unit/nicira/test_nsx_utils.py +++ b/neutron/tests/unit/nicira/test_nsx_utils.py @@ -171,7 +171,8 @@ class NsxUtilsTestCase(base.BaseTestCase): # found for a given port identifier exp_lr_uuid = uuidutils.generate_uuid() self._mock_router_mapping_db_calls(None) - with mock.patch(nicira_method('query_lrouters'), + with mock.patch(nicira_method('query_lrouters', + module_name='nsxlib.router'), return_value=[{'uuid': exp_lr_uuid}]): self._verify_get_nsx_router_id(exp_lr_uuid) @@ -179,6 +180,7 @@ class NsxUtilsTestCase(base.BaseTestCase): # This test verifies that the function returns None if the mapping # are not found both in the db and in the backend self._mock_router_mapping_db_calls(None) - with mock.patch(nicira_method('query_lrouters'), + with mock.patch(nicira_method('query_lrouters', + module_name='nsxlib.router'), return_value=[]): self._verify_get_nsx_router_id(None) diff --git a/neutron/tests/unit/nicira/test_nvplib.py b/neutron/tests/unit/nicira/test_nvplib.py index 01e07f71a..740197d3d 100644 --- a/neutron/tests/unit/nicira/test_nvplib.py +++ b/neutron/tests/unit/nicira/test_nvplib.py @@ -22,7 +22,6 @@ import mock from neutron.common import constants from neutron.common import exceptions -from neutron.openstack.common import uuidutils from neutron.plugins.nicira.common import config # noqa from neutron.plugins.nicira.common import exceptions as nvp_exc from neutron.plugins.nicira.common import utils @@ -31,7 +30,6 @@ from neutron.plugins.nicira import NvpApiClient from neutron.plugins.nicira import nvplib from neutron.tests import base from neutron.tests.unit.nicira import fake_nvpapiclient -from neutron.tests.unit.nicira import nicira_method from neutron.tests.unit.nicira import NVPAPI_NAME from neutron.tests.unit.nicira import STUBS_PATH from neutron.tests.unit import test_api_v2 @@ -74,38 +72,7 @@ class NvplibTestCase(base.BaseTestCase): return dict((t['scope'], t['tag']) for t in tags) -class TestNvplibNatRules(NvplibTestCase): - - def _test_create_lrouter_dnat_rule(self, version): - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - new=lambda: NvpApiClient.NVPVersion(version)): - tenant_id = 'pippo' - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - tenant_id, - 'fake_router', - '192.168.0.1') - nat_rule = nvplib.create_lrouter_dnat_rule( - self.fake_cluster, lrouter['uuid'], '10.0.0.99', - match_criteria={'destination_ip_addresses': - '192.168.0.5'}) - uri = nvplib._build_uri_path(nvplib.LROUTERNAT_RESOURCE, - nat_rule['uuid'], - lrouter['uuid']) - resp_obj = nvplib.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 test_create_lrouter_dnat_rule_v2(self): - self._test_create_lrouter_dnat_rule('2.9') - - def test_create_lrouter_dnat_rule_v31(self): - self._test_create_lrouter_dnat_rule('3.1') - - -class NvplibNegativeTests(base.BaseTestCase): +class NsxlibNegativeBaseTestCase(base.BaseTestCase): def setUp(self): # mock nvp api client @@ -132,10 +99,13 @@ class NvplibNegativeTests(base.BaseTestCase): self.fake_cluster.req_timeout, self.fake_cluster.http_timeout, self.fake_cluster.retries, self.fake_cluster.redirects) - super(NvplibNegativeTests, self).setUp() + super(NsxlibNegativeBaseTestCase, self).setUp() self.addCleanup(self.fc.reset_all) self.addCleanup(self.mock_nvpapi.stop) + +class L2GatewayNegativeTestCase(NsxlibNegativeBaseTestCase): + def test_create_l2_gw_service_on_failure(self): self.assertRaises(nvplib.NvpApiClient.NvpApiException, nvplib.create_l2_gw_service, @@ -164,35 +134,6 @@ class NvplibNegativeTests(base.BaseTestCase): 'fake-gateway', 'pluto') - def test_create_lrouter_on_failure(self): - self.assertRaises(nvplib.NvpApiClient.NvpApiException, - nvplib.create_lrouter, - self.fake_cluster, - uuidutils.generate_uuid(), - 'pluto', - 'fake_router', - 'my_hop') - - def test_delete_lrouter_on_failure(self): - self.assertRaises(nvplib.NvpApiClient.NvpApiException, - nvplib.delete_lrouter, - self.fake_cluster, - 'fake_router') - - def test_get_lrouter_on_failure(self): - self.assertRaises(nvplib.NvpApiClient.NvpApiException, - nvplib.get_lrouter, - self.fake_cluster, - 'fake_router') - - def test_update_lrouter_on_failure(self): - self.assertRaises(nvplib.NvpApiClient.NvpApiException, - nvplib.update_lrouter, - self.fake_cluster, - 'fake_router', - 'pluto', - 'new_hop') - class TestNvplibL2Gateway(NvplibTestCase): @@ -404,824 +345,6 @@ class TestNvplibLogicalSwitches(NvplibTestCase): self.fake_cluster, 'whatever', ['whatever']) -class TestNvplibExplicitLRouters(NvplibTestCase): - - def setUp(self): - self.fake_version = '3.2' - super(TestNvplibExplicitLRouters, self).setUp() - - def _get_lrouter(self, tenant_id, router_name, router_id, relations=None): - schema = '/ws.v1/schema/RoutingTableRoutingConfig' - - router = {'display_name': router_name, - 'uuid': router_id, - 'tags': [{'scope': 'quantum', 'tag': nvplib.NEUTRON_VERSION}, - {'scope': 'os_tid', 'tag': '%s' % tenant_id}], - 'distributed': False, - 'routing_config': {'type': 'RoutingTableRoutingConfig', - '_schema': schema}, - '_schema': schema, - 'nat_synchronization_enabled': True, - 'replication_mode': 'service', - 'type': 'LogicalRouterConfig', - '_href': '/ws.v1/lrouter/%s' % router_id, } - if relations: - router['_relations'] = relations - return router - - def _get_single_route(self, router_id, route_id='fake_route_id_0', - prefix='0.0.0.0/0', next_hop_ip='1.1.1.1'): - return {'protocol': 'static', - '_href': '/ws.v1/lrouter/%s/rib/%s' % (router_id, route_id), - 'prefix': prefix, - '_schema': '/ws.v1/schema/RoutingTableEntry', - 'next_hop_ip': next_hop_ip, - 'action': 'accept', - 'uuid': route_id} - - def test_prepare_body_with_implicit_routing_config(self): - router_name = 'fake_router_name' - tenant_id = 'fake_tenant_id' - neutron_router_id = 'pipita_higuain' - router_type = 'SingleDefaultRouteImplicitRoutingConfig' - route_config = { - 'default_route_next_hop': {'gateway_ip_address': 'fake_address', - 'type': 'RouterNextHop'}, } - body = nvplib._prepare_lrouter_body(router_name, neutron_router_id, - tenant_id, router_type, - **route_config) - expected = {'display_name': 'fake_router_name', - 'routing_config': { - 'default_route_next_hop': - {'gateway_ip_address': 'fake_address', - 'type': 'RouterNextHop'}, - 'type': 'SingleDefaultRouteImplicitRoutingConfig'}, - 'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'}, - {'scope': 'q_router_id', 'tag': 'pipita_higuain'}, - {'scope': 'quantum', - 'tag': nvplib.NEUTRON_VERSION}], - 'type': 'LogicalRouterConfig'} - self.assertEqual(expected, body) - - def test_prepare_body_without_routing_config(self): - router_name = 'fake_router_name' - tenant_id = 'fake_tenant_id' - router_type = 'RoutingTableRoutingConfig' - neutron_router_id = 'marekiaro_hamsik' - body = nvplib._prepare_lrouter_body(router_name, neutron_router_id, - tenant_id, router_type) - expected = {'display_name': 'fake_router_name', - 'routing_config': {'type': 'RoutingTableRoutingConfig'}, - 'tags': [{'scope': 'os_tid', 'tag': 'fake_tenant_id'}, - {'scope': 'q_router_id', - 'tag': 'marekiaro_hamsik'}, - {'scope': 'quantum', - 'tag': nvplib.NEUTRON_VERSION}], - 'type': 'LogicalRouterConfig'} - self.assertEqual(expected, body) - - def test_get_lrouter(self): - tenant_id = 'fake_tenant_id' - router_name = 'fake_router_name' - router_id = 'fake_router_id' - relations = { - 'LogicalRouterStatus': - {'_href': '/ws.v1/lrouter/%s/status' % router_id, - 'lport_admin_up_count': 1, - '_schema': '/ws.v1/schema/LogicalRouterStatus', - 'lport_count': 1, - 'fabric_status': True, - 'type': 'LogicalRouterStatus', - 'lport_link_up_count': 0, }, } - - with mock.patch(nicira_method('do_request'), - return_value=self._get_lrouter(tenant_id, - router_name, - router_id, - relations)): - lrouter = nvplib.get_lrouter(self.fake_cluster, router_id) - self.assertTrue( - lrouter['_relations']['LogicalRouterStatus']['fabric_status']) - - def test_create_lrouter(self): - tenant_id = 'fake_tenant_id' - router_name = 'fake_router_name' - router_id = 'fake_router_id' - nexthop_ip = '10.0.0.1' - with mock.patch(nicira_method('do_request'), - return_value=self._get_lrouter(tenant_id, - router_name, - router_id)): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - tenant_id, - router_name, nexthop_ip) - self.assertEqual(lrouter['routing_config']['type'], - 'RoutingTableRoutingConfig') - self.assertNotIn('default_route_next_hop', - lrouter['routing_config']) - - def test_update_lrouter_nvp_with_no_routes(self): - router_id = 'fake_router_id' - new_routes = [{"nexthop": "10.0.0.2", - "destination": "169.254.169.0/30"}, ] - - nvp_routes = [self._get_single_route(router_id)] - with mock.patch(nicira_method('get_explicit_routes_lrouter'), - return_value=nvp_routes): - with mock.patch(nicira_method('create_explicit_route_lrouter'), - return_value='fake_uuid'): - old_routes = nvplib.update_explicit_routes_lrouter( - self.fake_cluster, router_id, new_routes) - self.assertEqual(old_routes, nvp_routes) - - def test_update_lrouter_nvp_with_no_routes_raise_nvp_exception(self): - router_id = 'fake_router_id' - new_routes = [{"nexthop": "10.0.0.2", - "destination": "169.254.169.0/30"}, ] - - nvp_routes = [self._get_single_route(router_id)] - with mock.patch(nicira_method('get_explicit_routes_lrouter'), - return_value=nvp_routes): - with mock.patch(nicira_method('create_explicit_route_lrouter'), - side_effect=NvpApiClient.NvpApiException): - self.assertRaises(NvpApiClient.NvpApiException, - nvplib.update_explicit_routes_lrouter, - self.fake_cluster, router_id, new_routes) - - def test_update_lrouter_with_routes(self): - router_id = 'fake_router_id' - new_routes = [{"next_hop_ip": "10.0.0.2", - "prefix": "169.254.169.0/30"}, ] - - nvp_routes = [self._get_single_route(router_id), - self._get_single_route(router_id, 'fake_route_id_1', - '0.0.0.1/24', '10.0.0.3'), - self._get_single_route(router_id, 'fake_route_id_2', - '0.0.0.2/24', '10.0.0.4'), ] - - with mock.patch(nicira_method('get_explicit_routes_lrouter'), - return_value=nvp_routes): - with mock.patch(nicira_method('delete_explicit_route_lrouter'), - return_value=None): - with mock.patch(nicira_method( - 'create_explicit_route_lrouter'), - return_value='fake_uuid'): - old_routes = nvplib.update_explicit_routes_lrouter( - self.fake_cluster, router_id, new_routes) - self.assertEqual(old_routes, nvp_routes) - - def test_update_lrouter_with_routes_raises_nvp_expception(self): - router_id = 'fake_router_id' - new_routes = [{"nexthop": "10.0.0.2", - "destination": "169.254.169.0/30"}, ] - - nvp_routes = [self._get_single_route(router_id), - self._get_single_route(router_id, 'fake_route_id_1', - '0.0.0.1/24', '10.0.0.3'), - self._get_single_route(router_id, 'fake_route_id_2', - '0.0.0.2/24', '10.0.0.4'), ] - - with mock.patch(nicira_method('get_explicit_routes_lrouter'), - return_value=nvp_routes): - with mock.patch(nicira_method('delete_explicit_route_lrouter'), - side_effect=NvpApiClient.NvpApiException): - with mock.patch( - nicira_method('create_explicit_route_lrouter'), - return_value='fake_uuid'): - self.assertRaises( - NvpApiClient.NvpApiException, - nvplib.update_explicit_routes_lrouter, - self.fake_cluster, router_id, new_routes) - - -class TestNvplibLogicalRouters(NvplibTestCase): - - def _verify_lrouter(self, res_lrouter, - expected_uuid, - expected_display_name, - expected_nexthop, - expected_tenant_id, - expected_neutron_id=None, - expected_distributed=None): - self.assertEqual(res_lrouter['uuid'], expected_uuid) - nexthop = (res_lrouter['routing_config'] - ['default_route_next_hop']['gateway_ip_address']) - self.assertEqual(nexthop, expected_nexthop) - router_tags = self._build_tag_dict(res_lrouter['tags']) - self.assertIn('os_tid', router_tags) - self.assertEqual(res_lrouter['display_name'], expected_display_name) - self.assertEqual(expected_tenant_id, router_tags['os_tid']) - if expected_distributed is not None: - self.assertEqual(expected_distributed, - res_lrouter['distributed']) - if expected_neutron_id: - self.assertIn('q_router_id', router_tags) - self.assertEqual(expected_neutron_id, router_tags['q_router_id']) - - def test_get_lrouters(self): - lrouter_uuids = [nvplib.create_lrouter( - self.fake_cluster, 'whatever', 'pippo', 'fake-lrouter-%s' % k, - '10.0.0.1')['uuid'] for k in range(3)] - routers = nvplib.get_lrouters(self.fake_cluster, 'pippo') - for router in routers: - self.assertIn(router['uuid'], lrouter_uuids) - - def _create_lrouter(self, version, neutron_id=None, distributed=None): - with mock.patch.object( - self.fake_cluster.api_client, 'get_nvp_version', - return_value=NvpApiClient.NVPVersion(version)): - if not neutron_id: - neutron_id = uuidutils.generate_uuid() - lrouter = nvplib.create_lrouter( - self.fake_cluster, neutron_id, 'pippo', - 'fake-lrouter', '10.0.0.1', distributed=distributed) - return nvplib.get_lrouter(self.fake_cluster, - lrouter['uuid']) - - def test_create_and_get_lrouter_v30(self): - neutron_id = uuidutils.generate_uuid() - res_lrouter = self._create_lrouter('3.0', neutron_id=neutron_id) - self._verify_lrouter(res_lrouter, res_lrouter['uuid'], - 'fake-lrouter', '10.0.0.1', 'pippo', - neutron_id) - - def test_create_and_get_lrouter_v31_centralized(self): - neutron_id = uuidutils.generate_uuid() - res_lrouter = self._create_lrouter('3.1', neutron_id=neutron_id, - distributed=False) - self._verify_lrouter(res_lrouter, res_lrouter['uuid'], - 'fake-lrouter', '10.0.0.1', 'pippo', - expected_neutron_id=neutron_id, - expected_distributed=False) - - def test_create_and_get_lrouter_v31_distributed(self): - neutron_id = uuidutils.generate_uuid() - res_lrouter = self._create_lrouter('3.1', neutron_id=neutron_id, - distributed=True) - self._verify_lrouter(res_lrouter, res_lrouter['uuid'], - 'fake-lrouter', '10.0.0.1', 'pippo', - expected_neutron_id=neutron_id, - expected_distributed=True) - - def test_create_and_get_lrouter_name_exceeds_40chars(self): - neutron_id = uuidutils.generate_uuid() - display_name = '*' * 50 - lrouter = nvplib.create_lrouter(self.fake_cluster, - neutron_id, - 'pippo', - display_name, - '10.0.0.1') - res_lrouter = nvplib.get_lrouter(self.fake_cluster, - lrouter['uuid']) - self._verify_lrouter(res_lrouter, lrouter['uuid'], - '*' * 40, '10.0.0.1', 'pippo', - expected_neutron_id=neutron_id) - - def _test_version_dependent_update_lrouter(self, version): - def foo(*args, **kwargs): - return version - - foo_func_dict = { - 'update_lrouter': { - 2: {-1: foo}, - 3: {-1: foo, 2: foo} - } - } - - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - return_value=NvpApiClient.NVPVersion(version)): - with mock.patch.dict(nvplib.NVPLIB_FUNC_DICT, - foo_func_dict, clear=True): - return nvplib.update_lrouter( - self.fake_cluster, 'foo_router_id', 'foo_router_name', - 'foo_nexthop', routes={'foo_destination': 'foo_address'}) - - def test_version_dependent_update_lrouter_old_versions(self): - self.assertRaises(nvp_exc.NvpInvalidVersion, - self._test_version_dependent_update_lrouter, - "2.9") - self.assertRaises(nvp_exc.NvpInvalidVersion, - self._test_version_dependent_update_lrouter, - "3.0") - self.assertRaises(nvp_exc.NvpInvalidVersion, - self._test_version_dependent_update_lrouter, - "3.1") - - def test_version_dependent_update_lrouter_new_versions(self): - self.assertEqual("3.2", - self._test_version_dependent_update_lrouter("3.2")) - self.assertEqual("4.0", - self._test_version_dependent_update_lrouter("4.0")) - self.assertEqual("4.1", - self._test_version_dependent_update_lrouter("4.1")) - - def test_update_lrouter_no_nexthop(self): - neutron_id = uuidutils.generate_uuid() - lrouter = nvplib.create_lrouter(self.fake_cluster, - neutron_id, - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter = nvplib.update_lrouter(self.fake_cluster, - lrouter['uuid'], - 'new_name', - None) - res_lrouter = nvplib.get_lrouter(self.fake_cluster, - lrouter['uuid']) - self._verify_lrouter(res_lrouter, lrouter['uuid'], - 'new_name', '10.0.0.1', 'pippo', - expected_neutron_id=neutron_id) - - def test_update_lrouter(self): - neutron_id = uuidutils.generate_uuid() - lrouter = nvplib.create_lrouter(self.fake_cluster, - neutron_id, - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter = nvplib.update_lrouter(self.fake_cluster, - lrouter['uuid'], - 'new_name', - '192.168.0.1') - res_lrouter = nvplib.get_lrouter(self.fake_cluster, - lrouter['uuid']) - self._verify_lrouter(res_lrouter, lrouter['uuid'], - 'new_name', '192.168.0.1', 'pippo', - expected_neutron_id=neutron_id) - - def test_update_nonexistent_lrouter_raises(self): - self.assertRaises(exceptions.NotFound, - nvplib.update_lrouter, - self.fake_cluster, - 'whatever', - 'foo', '9.9.9.9') - - def test_delete_lrouter(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - nvplib.delete_lrouter(self.fake_cluster, lrouter['uuid']) - self.assertRaises(exceptions.NotFound, - nvplib.get_lrouter, - self.fake_cluster, - lrouter['uuid']) - - def test_query_lrouter_ports(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - router_port_uuids = [nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', - 'qp_id_%s' % k, 'port-%s' % k, True, - ['192.168.0.%s' % k], '00:11:22:33:44:55')['uuid'] - for k in range(3)] - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 3) - for res_port in ports: - self.assertIn(res_port['uuid'], router_port_uuids) - - def test_query_lrouter_lports_nonexistent_lrouter_raises(self): - self.assertRaises( - exceptions.NotFound, nvplib.create_router_lport, - self.fake_cluster, 'booo', 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - - def test_create_and_get_lrouter_port(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - res_port = ports[0] - port_tags = self._build_tag_dict(res_port['tags']) - self.assertEqual(['192.168.0.1'], res_port['ip_addresses']) - self.assertIn('os_tid', port_tags) - self.assertIn('q_port_id', port_tags) - self.assertEqual('pippo', port_tags['os_tid']) - self.assertEqual('neutron_port_id', port_tags['q_port_id']) - - def test_create_lrouter_port_nonexistent_router_raises(self): - self.assertRaises( - exceptions.NotFound, nvplib.create_router_lport, - self.fake_cluster, 'booo', 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - - def test_update_lrouter_port(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - nvplib.update_router_lport( - self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], - 'pippo', 'another_port_id', 'name', False, - ['192.168.0.1', '10.10.10.254']) - - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - res_port = ports[0] - port_tags = self._build_tag_dict(res_port['tags']) - self.assertEqual(['192.168.0.1', '10.10.10.254'], - res_port['ip_addresses']) - self.assertEqual('False', res_port['admin_status_enabled']) - self.assertIn('os_tid', port_tags) - self.assertIn('q_port_id', port_tags) - self.assertEqual('pippo', port_tags['os_tid']) - self.assertEqual('another_port_id', port_tags['q_port_id']) - - def test_update_lrouter_port_nonexistent_router_raises(self): - self.assertRaises( - exceptions.NotFound, nvplib.update_router_lport, - self.fake_cluster, 'boo-router', 'boo-port', 'pippo', - 'neutron_port_id', 'name', True, ['192.168.0.1']) - - def test_update_lrouter_port_nonexistent_port_raises(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - self.assertRaises( - exceptions.NotFound, nvplib.update_router_lport, - self.fake_cluster, lrouter['uuid'], 'boo-port', 'pippo', - 'neutron_port_id', 'name', True, ['192.168.0.1']) - - def test_delete_lrouter_port(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [], - '00:11:22:33:44:55') - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - nvplib.delete_router_lport(self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid']) - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertFalse(len(ports)) - - def test_delete_lrouter_port_nonexistent_router_raises(self): - self.assertRaises(exceptions.NotFound, - nvplib.delete_router_lport, - self.fake_cluster, 'xyz', 'abc') - - def test_delete_lrouter_port_nonexistent_port_raises(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - self.assertRaises(exceptions.NotFound, - nvplib.delete_router_lport, - self.fake_cluster, lrouter['uuid'], 'abc') - - def test_delete_peer_lrouter_port(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'x', 'y', True, [], - '00:11:22:33:44:55') - - def fakegetport(*args, **kwargs): - return {'_relations': {'LogicalPortAttachment': - {'peer_port_uuid': lrouter_port['uuid']}}} - # mock get_port - with mock.patch.object(nvplib, 'get_port', new=fakegetport): - nvplib.delete_peer_router_lport(self.fake_cluster, - lrouter_port['uuid'], - 'whatwever', 'whatever') - - def test_update_lrouter_port_ips_add_only(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - nvplib.update_lrouter_port_ips( - self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], - ['10.10.10.254'], []) - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - res_port = ports[0] - self.assertEqual(['10.10.10.254', '192.168.0.1'], - res_port['ip_addresses']) - - def test_update_lrouter_port_ips_remove_only(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1', '10.10.10.254'], - '00:11:22:33:44:55') - nvplib.update_lrouter_port_ips( - self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], - [], ['10.10.10.254']) - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - res_port = ports[0] - self.assertEqual(['192.168.0.1'], res_port['ip_addresses']) - - def test_update_lrouter_port_ips_add_and_remove(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - nvplib.update_lrouter_port_ips( - self.fake_cluster, lrouter['uuid'], lrouter_port['uuid'], - ['10.10.10.254'], ['192.168.0.1']) - ports = nvplib.query_lrouter_lports(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(ports), 1) - res_port = ports[0] - self.assertEqual(['10.10.10.254'], res_port['ip_addresses']) - - def test_update_lrouter_port_ips_nonexistent_router_raises(self): - self.assertRaises( - nvp_exc.NvpPluginException, nvplib.update_lrouter_port_ips, - self.fake_cluster, 'boo-router', 'boo-port', [], []) - - def test_update_lrouter_port_ips_nvp_exception_raises(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - - def raise_nvp_exc(*args, **kwargs): - raise NvpApiClient.NvpApiException() - - with mock.patch.object(nvplib, 'do_request', new=raise_nvp_exc): - self.assertRaises( - nvp_exc.NvpPluginException, nvplib.update_lrouter_port_ips, - self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid'], [], []) - - def test_plug_lrouter_port_patch_attachment(self): - tenant_id = 'pippo' - transport_zones_config = [{'zone_uuid': _uuid(), - 'transport_type': 'stt'}] - lswitch = nvplib.create_lswitch(self.fake_cluster, - _uuid(), - tenant_id, 'fake-switch', - transport_zones_config) - lport = nvplib.create_lport(self.fake_cluster, lswitch['uuid'], - tenant_id, 'xyz', - 'name', 'device_id', True) - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - tenant_id, - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55:66') - result = nvplib.plug_router_port_attachment( - self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid'], - lport['uuid'], 'PatchAttachment') - self.assertEqual(lport['uuid'], - result['LogicalPortAttachment']['peer_port_uuid']) - - def test_plug_lrouter_port_l3_gw_attachment(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55:66') - result = nvplib.plug_router_port_attachment( - self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid'], - 'gw_att', 'L3GatewayAttachment') - self.assertEqual( - 'gw_att', - result['LogicalPortAttachment']['l3_gateway_service_uuid']) - - def test_plug_lrouter_port_l3_gw_attachment_with_vlan(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - result = nvplib.plug_router_port_attachment( - self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid'], - 'gw_att', 'L3GatewayAttachment', 123) - self.assertEqual( - 'gw_att', - result['LogicalPortAttachment']['l3_gateway_service_uuid']) - self.assertEqual( - '123', - result['LogicalPortAttachment']['vlan_id']) - - def test_plug_lrouter_port_invalid_attachment_type_raises(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - lrouter_port = nvplib.create_router_lport( - self.fake_cluster, lrouter['uuid'], 'pippo', 'neutron_port_id', - 'name', True, ['192.168.0.1'], '00:11:22:33:44:55') - self.assertRaises(nvp_exc.NvpInvalidAttachmentType, - nvplib.plug_router_port_attachment, - self.fake_cluster, lrouter['uuid'], - lrouter_port['uuid'], 'gw_att', 'BadType') - - def _test_create_router_snat_rule(self, version): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - new=lambda: NvpApiClient.NVPVersion(version)): - nvplib.create_lrouter_snat_rule( - self.fake_cluster, lrouter['uuid'], - '10.0.0.2', '10.0.0.2', order=200, - match_criteria={'source_ip_addresses': '192.168.0.24'}) - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 1) - - def test_create_router_snat_rule_v3(self): - self._test_create_router_snat_rule('3.0') - - def test_create_router_snat_rule_v2(self): - self._test_create_router_snat_rule('2.0') - - def _test_create_router_dnat_rule(self, version, dest_port=None): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - return_value=NvpApiClient.NVPVersion(version)): - nvplib.create_lrouter_dnat_rule( - self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200, - dest_port=dest_port, - match_criteria={'destination_ip_addresses': '10.0.0.3'}) - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 1) - - def test_create_router_dnat_rule_v3(self): - self._test_create_router_dnat_rule('3.0') - - def test_create_router_dnat_rule_v2(self): - self._test_create_router_dnat_rule('2.0') - - def test_create_router_dnat_rule_v2_with_destination_port(self): - self._test_create_router_dnat_rule('2.0', 8080) - - def test_create_router_dnat_rule_v3_with_destination_port(self): - self._test_create_router_dnat_rule('3.0', 8080) - - def test_create_router_snat_rule_invalid_match_keys_raises(self): - # In this case the version does not make a difference - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - new=lambda: '2.0'): - self.assertRaises(AttributeError, - nvplib.create_lrouter_snat_rule, - self.fake_cluster, lrouter['uuid'], - '10.0.0.2', '10.0.0.2', order=200, - match_criteria={'foo': 'bar'}) - - def _test_create_router_nosnat_rule(self, version, expected=1): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - new=lambda: NvpApiClient.NVPVersion(version)): - nvplib.create_lrouter_nosnat_rule( - self.fake_cluster, lrouter['uuid'], - order=100, - match_criteria={'destination_ip_addresses': '192.168.0.0/24'}) - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - # NoSNAT rules do not exist in V2 - self.assertEqual(len(rules), expected) - - def test_create_router_nosnat_rule_v2(self): - self._test_create_router_nosnat_rule('2.0', expected=0) - - def test_create_router_nosnat_rule_v3(self): - self._test_create_router_nosnat_rule('3.0') - - def _prepare_nat_rules_for_delete_tests(self): - lrouter = nvplib.create_lrouter(self.fake_cluster, - uuidutils.generate_uuid(), - 'pippo', - 'fake-lrouter', - '10.0.0.1') - # v2 or v3 makes no difference for this test - with mock.patch.object(self.fake_cluster.api_client, - 'get_nvp_version', - new=lambda: NvpApiClient.NVPVersion('2.0')): - nvplib.create_lrouter_snat_rule( - self.fake_cluster, lrouter['uuid'], - '10.0.0.2', '10.0.0.2', order=220, - match_criteria={'source_ip_addresses': '192.168.0.0/24'}) - nvplib.create_lrouter_snat_rule( - self.fake_cluster, lrouter['uuid'], - '10.0.0.3', '10.0.0.3', order=200, - match_criteria={'source_ip_addresses': '192.168.0.2/32'}) - nvplib.create_lrouter_dnat_rule( - self.fake_cluster, lrouter['uuid'], '192.168.0.2', order=200, - match_criteria={'destination_ip_addresses': '10.0.0.3'}) - return lrouter - - def test_delete_router_nat_rules_by_match_on_destination_ip(self): - lrouter = self._prepare_nat_rules_for_delete_tests() - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - nvplib.delete_nat_rules_by_match( - self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 1, 1, - destination_ip_addresses='10.0.0.3') - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 2) - - def test_delete_router_nat_rules_by_match_on_source_ip(self): - lrouter = self._prepare_nat_rules_for_delete_tests() - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - nvplib.delete_nat_rules_by_match( - self.fake_cluster, lrouter['uuid'], 'SourceNatRule', 1, 1, - source_ip_addresses='192.168.0.2/32') - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 2) - - def test_delete_router_nat_rules_by_match_no_match_expected(self): - lrouter = self._prepare_nat_rules_for_delete_tests() - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - nvplib.delete_nat_rules_by_match( - self.fake_cluster, lrouter['uuid'], 'SomeWeirdType', 0) - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - nvplib.delete_nat_rules_by_match( - self.fake_cluster, lrouter['uuid'], 'DestinationNatRule', 0, - destination_ip_addresses='99.99.99.99') - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - - def test_delete_router_nat_rules_by_match_no_match_raises(self): - lrouter = self._prepare_nat_rules_for_delete_tests() - rules = nvplib.query_nat_rules(self.fake_cluster, lrouter['uuid']) - self.assertEqual(len(rules), 3) - self.assertRaises( - nvp_exc.NvpNatRuleMismatch, - nvplib.delete_nat_rules_by_match, - self.fake_cluster, lrouter['uuid'], - 'SomeWeirdType', 1, 1) - - class TestNvplibSecurityProfile(NvplibTestCase): def test_create_and_get_security_profile(self): @@ -1515,38 +638,6 @@ class TestNvplibClusterManagement(NvplibTestCase): cluster=self.fake_cluster) -class TestNvplibVersioning(base.BaseTestCase): - - def test_function_handling_missing_minor(self): - version = NvpApiClient.NVPVersion('2.0') - function = nvplib.get_function_by_version('create_lrouter', version) - self.assertEqual(nvplib.create_implicit_routing_lrouter, - function) - - def test_function_handling_with_both_major_and_minor(self): - version = NvpApiClient.NVPVersion('3.2') - function = nvplib.get_function_by_version('create_lrouter', version) - self.assertEqual(nvplib.create_explicit_routing_lrouter, - function) - - def test_function_handling_with_newer_major(self): - version = NvpApiClient.NVPVersion('5.2') - function = nvplib.get_function_by_version('create_lrouter', version) - self.assertEqual(nvplib.create_explicit_routing_lrouter, - function) - - def test_function_handling_with_obsolete_major(self): - version = NvpApiClient.NVPVersion('1.2') - self.assertRaises(NotImplementedError, - nvplib.get_function_by_version, - 'create_lrouter', version) - - def test_function_handling_with_unknown_version(self): - self.assertRaises(NvpApiClient.ServiceUnavailable, - nvplib.get_function_by_version, - 'create_lrouter', None) - - class NvplibMiscTestCase(base.BaseTestCase): def test_check_and_truncate_name_with_none(self): @@ -1625,7 +716,3 @@ class NvplibMiscTestCase(base.BaseTestCase): (nvplib.URI_PREFIX, parent_res, par_id, child_res, res_id, 'doh')) self.assertEqual(expected, result) - - -def _nicira_method(method_name, module_name='nvplib'): - return '%s.%s.%s' % ('neutron.plugins.nicira', module_name, method_name) -- 2.45.2