diff --git a/neutron/plugins/cisco/cfg_agent/device_drivers/csr1kv/cisco_csr1kv_snippets.py b/neutron/plugins/cisco/cfg_agent/device_drivers/csr1kv/cisco_csr1kv_snippets.py index be033a6eab..89b7c37de9 100644 --- a/neutron/plugins/cisco/cfg_agent/device_drivers/csr1kv/cisco_csr1kv_snippets.py +++ b/neutron/plugins/cisco/cfg_agent/device_drivers/csr1kv/cisco_csr1kv_snippets.py @@ -58,28 +58,32 @@ ENABLE_INTF = """ #=================================================# # Create VRF -# $(config)ip routing -# $(config)ip vrf nrouter-e7d4y5 +# $(config)vrf definition nrouter-e7d4y5 +# $(config-vrf)address-family ipv4 +# $(config-vrf-af)exit-address-family +# $(config-vrf)address-family ipv6 +# $(config-vrf-af)exit-address-family #=================================================# CREATE_VRF = """ - ip routing - ip vrf %s + vrf definition %s + address-family ipv4 + exit-address-family + address-family ipv6 + exit-address-family """ #=================================================# # Remove VRF -# $(config)ip routing -# $(config)no ip vrf nrouter-e7d4y5 +# $(config)no vrf definition nrouter-e7d4y5 #=================================================# REMOVE_VRF = """ - ip routing - no ip vrf %s + no vrf definition %s """ @@ -96,7 +100,7 @@ CREATE_SUBINTERFACE = """ interface %s encapsulation dot1Q %s - ip vrf forwarding %s + vrf forwarding %s ip address %s %s @@ -127,7 +131,7 @@ SET_INTC_HSRP = """ interface %s - ip vrf forwarding %s + vrf forwarding %s standby version 2 standby %s priority %s standby %s ip %s diff --git a/neutron/plugins/cisco/db/l3/l3_router_appliance_db.py b/neutron/plugins/cisco/db/l3/l3_router_appliance_db.py index 01515fff47..1284081455 100644 --- a/neutron/plugins/cisco/db/l3/l3_router_appliance_db.py +++ b/neutron/plugins/cisco/db/l3/l3_router_appliance_db.py @@ -22,10 +22,13 @@ from sqlalchemy.sql import expression as expr from neutron.common import constants as l3_constants from neutron.common import exceptions as n_exc from neutron.common import rpc as n_rpc +from neutron.common import topics from neutron import context as n_context +from neutron.db import agents_db from neutron.db import extraroute_db from neutron.db import l3_db from neutron.db import models_v2 +from neutron.db import portbindings_db as p_binding from neutron.extensions import providernet as pr_net from neutron.openstack.common import lockutils from neutron.openstack.common import log as logging @@ -573,3 +576,53 @@ class L3RouterApplianceDBMixin(extraroute_db.ExtraRoute_dbonly_mixin): active=True) else: return [] + + def get_active_routers_for_host(self, context, host): + query = context.session.query( + l3_models.RouterHostingDeviceBinding.router_id) + query = query.join( + models_v2.Port, + l3_models.RouterHostingDeviceBinding.hosting_device_id == + models_v2.Port.device_id) + query = query.join(p_binding.PortBindingPort) + query = query.filter(p_binding.PortBindingPort.host == host) + query = query.filter(models_v2.Port.name == 'mgmt') + router_ids = [item[0] for item in query] + if router_ids: + return self.get_sync_data_ext(context, router_ids=router_ids, + active=True) + else: + return [] + + @staticmethod + def _agent_state_filter(check_active, last_heartbeat): + """Filters only active agents, if requested.""" + if not check_active: + return True + return not agents_db.AgentDbMixin.is_agent_down(last_heartbeat) + + def get_host_for_router(self, context, router, admin_state_up=None, + check_active=False): + query = context.session.query(agents_db.Agent.host, + agents_db.Agent.heartbeat_timestamp) + query = query.join( + p_binding.PortBindingPort, + p_binding.PortBindingPort.host == agents_db.Agent.host) + query = query.join( + models_v2.Port, + models_v2.Port.id == p_binding.PortBindingPort.port_id) + query = query.join( + l3_models.RouterHostingDeviceBinding, + l3_models.RouterHostingDeviceBinding.hosting_device_id == + models_v2.Port.device_id) + query = query.filter( + agents_db.Agent.topic == topics.L3_AGENT, + l3_models.RouterHostingDeviceBinding.router_id == router) + if admin_state_up is not None: + query = query.filter( + agents_db.Agent.admin_state_up == admin_state_up) + entry = query.first() + if entry and L3RouterApplianceDBMixin._agent_state_filter(check_active, + entry[1]): + return entry[0] + return "" diff --git a/neutron/plugins/cisco/l3/configdrive_templates/csr1kv_cfg_template b/neutron/plugins/cisco/l3/configdrive_templates/csr1kv_cfg_template index 4b7d09e840..9bba2c884a 100644 --- a/neutron/plugins/cisco/l3/configdrive_templates/csr1kv_cfg_template +++ b/neutron/plugins/cisco/l3/configdrive_templates/csr1kv_cfg_template @@ -39,6 +39,13 @@ interface GigabitEthernet1 ip address no shutdown +virtual-service csr_mgmt + ip shared host-interface GigabitEthernet1 + activate + +remote-management + restful-api local-port 55443 + ip route 0.0.0.0 0.0.0.0 GigabitEthernet1 ip name-server diff --git a/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py b/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py index 8ddefb1f15..392ffdc27f 100644 --- a/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py +++ b/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py @@ -30,7 +30,6 @@ HEADER_CONTENT_TYPE_JSON = {'content-type': 'application/json'} URL_BASE = 'https://%(host)s/api/v1/%(resource)s' # CSR RESTapi URIs - URI_VPN_IPSEC_POLICIES = 'vpn-svc/ipsec/policies' URI_VPN_IPSEC_POLICIES_ID = URI_VPN_IPSEC_POLICIES + '/%s' URI_VPN_IKE_POLICIES = 'vpn-svc/ike/policies' @@ -62,10 +61,12 @@ class CsrRestClient(object): def __init__(self, settings): self.port = str(settings.get('protocol_port', 55443)) self.host = ':'.join([settings.get('rest_mgmt_ip', ''), self.port]) - self.tunnel_ip = settings.get('external_ip', '') self.auth = (settings['username'], settings['password']) - self.tunnel_if_name = settings.get('tunnel_if_name', '') + self.inner_if_name = settings.get('inner_if_name', '') + self.outer_if_name = settings.get('outer_if_name', '') self.token = None + self.vrf = settings.get('vrf', '') + self.vrf_prefix = 'vrf/%s/' % self.vrf if self.vrf else "" self.status = requests.codes.OK self.timeout = settings.get('timeout') self.max_tries = 5 @@ -212,6 +213,8 @@ class CsrRestClient(object): return self._do_request('DELETE', resource, more_headers=HEADER_CONTENT_TYPE_JSON) + # VPN Specific APIs + def create_ike_policy(self, policy_info): base_ike_policy_info = {u'version': u'v1', u'local-auth-method': u'pre-share'} @@ -226,19 +229,22 @@ class CsrRestClient(object): payload=base_ipsec_policy_info) def create_pre_shared_key(self, psk_info): - return self.post_request(URI_VPN_IKE_KEYRINGS, payload=psk_info) + return self.post_request(self.vrf_prefix + URI_VPN_IKE_KEYRINGS, + payload=psk_info) def create_ipsec_connection(self, connection_info): base_conn_info = { u'vpn-type': u'site-to-site', u'ip-version': u'ipv4', u'local-device': { - u'tunnel-ip-address': self.tunnel_ip, - u'ip-address': self.tunnel_if_name + u'tunnel-ip-address': self.outer_if_name, + u'ip-address': self.inner_if_name } } connection_info.update(base_conn_info) - return self.post_request(URI_VPN_SITE_TO_SITE, + if self.vrf: + connection_info[u'tunnel-vrf'] = self.vrf + return self.post_request(self.vrf_prefix + URI_VPN_SITE_TO_SITE, payload=connection_info) def configure_ike_keepalive(self, keepalive_info): @@ -247,11 +253,12 @@ class CsrRestClient(object): return self.put_request(URI_VPN_IKE_KEEPALIVE, keepalive_info) def create_static_route(self, route_info): - return self.post_request(URI_ROUTING_STATIC_ROUTES, + return self.post_request(self.vrf_prefix + URI_ROUTING_STATIC_ROUTES, payload=route_info) def delete_static_route(self, route_id): - return self.delete_request(URI_ROUTING_STATIC_ROUTES_ID % route_id) + return self.delete_request( + self.vrf_prefix + URI_ROUTING_STATIC_ROUTES_ID % route_id) def set_ipsec_connection_state(self, tunnel, admin_up=True): """Set the IPSec site-to-site connection (tunnel) admin state. @@ -259,10 +266,12 @@ class CsrRestClient(object): Note: When a tunnel is created, it will be admin up. """ info = {u'vpn-interface-name': tunnel, u'enabled': admin_up} - return self.put_request(URI_VPN_SITE_TO_SITE_STATE % tunnel, info) + return self.put_request( + self.vrf_prefix + URI_VPN_SITE_TO_SITE_STATE % tunnel, info) def delete_ipsec_connection(self, conn_id): - return self.delete_request(URI_VPN_SITE_TO_SITE_ID % conn_id) + return self.delete_request( + self.vrf_prefix + URI_VPN_SITE_TO_SITE_ID % conn_id) def delete_ipsec_policy(self, policy_id): return self.delete_request(URI_VPN_IPSEC_POLICIES_ID % policy_id) @@ -271,10 +280,12 @@ class CsrRestClient(object): return self.delete_request(URI_VPN_IKE_POLICIES_ID % policy_id) def delete_pre_shared_key(self, key_id): - return self.delete_request(URI_VPN_IKE_KEYRINGS_ID % key_id) + return self.delete_request( + self.vrf_prefix + URI_VPN_IKE_KEYRINGS_ID % key_id) def read_tunnel_statuses(self): - results = self.get_request(URI_VPN_SITE_ACTIVE_SESSIONS) + results = self.get_request(self.vrf_prefix + + URI_VPN_SITE_ACTIVE_SESSIONS) if self.status != requests.codes.OK or not results: return [] tunnels = [(t[u'vpn-interface-name'], t[u'status']) diff --git a/neutron/services/vpn/service_drivers/cisco_cfg_loader.py b/neutron/services/vpn/service_drivers/cisco_cfg_loader.py deleted file mode 100644 index d866508345..0000000000 --- a/neutron/services/vpn/service_drivers/cisco_cfg_loader.py +++ /dev/null @@ -1,211 +0,0 @@ -# Copyright 2014 Cisco Systems, 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. - -"""Interim code to obtain router information for Cisco CSR - -This obtains information on the Cisco CSR router from an INI file. This is -an interim solution, until the Cisco L3 router plugin code is up-streamed. -Once that happens, this code and UTs will be removed and the API calls to -the L3 router will be used. - -To use this code, the Neutron server is started with a config_file that -points to an INI file with router configuration. The router would be created -(manually) in Nova, the INI file is then updated with the router information, -and then VPN IPSec site-to-site connections can be created using that router. -""" - -import netaddr -import re - -from oslo.config import cfg - -from neutron.db import l3_db -from neutron.db import models_v2 -from neutron.openstack.common.gettextutils import _LE, _LI -from neutron.openstack.common import log as logging -from neutron.services.vpn.device_drivers import ( - cisco_csr_rest_client as csr_client) - - -LOG = logging.getLogger(__name__) -tunnel_if_re = re.compile(r'^GigabitEthernet[123]') - - -def get_available_csrs_from_config(config_files): - """Read INI for available Cisco CSRs that driver can use. - - Loads management port, tunnel IP, user, and password information for - available CSRs from configuration file. Driver will use this info to - configure VPN connections. The CSR is associated 1:1 with a Neutron - router. To identify which CSR to use for a VPN service, the public - (GW) IP of the Neutron router will be used as an index into the CSR - config info. - """ - multi_parser = cfg.MultiConfigParser() - LOG.info(_LI("Scanning config files %s for Cisco CSR configurations"), - config_files) - try: - read_ok = multi_parser.read(config_files) - except cfg.ParseError as pe: - LOG.error(_LE("Config file parse error: %s"), pe) - return {} - - if len(read_ok) != len(config_files): - raise cfg.Error(_("Unable to parse config files %s for Cisco CSR " - "info") % config_files) - csrs_found = {} - for parsed_file in multi_parser.parsed: - for parsed_item in parsed_file.keys(): - device_type, sep, for_router = parsed_item.partition(':') - if device_type.lower() == 'cisco_csr_rest': - try: - netaddr.IPNetwork(for_router) - except netaddr.core.AddrFormatError: - LOG.error(_LE("Ignoring Cisco CSR configuration entry - " - "router IP %s is not valid"), for_router) - continue - entry = parsed_file[parsed_item] - # Check for missing fields - try: - rest_mgmt_ip = entry['rest_mgmt'][0] - tunnel_ip = entry['tunnel_ip'][0] - username = entry['username'][0] - password = entry['password'][0] - host = entry['host'][0] - tunnel_if = entry['tunnel_if'][0] - except KeyError as ke: - LOG.error(_LE("Ignoring Cisco CSR for router %(router)s " - "- missing %(field)s setting"), - {'router': for_router, 'field': str(ke)}) - continue - # Validate fields - try: - timeout = float(entry['timeout'][0]) - except ValueError: - LOG.error(_LE("Ignoring Cisco CSR for router %s - " - "timeout is not a floating point number"), - for_router) - continue - except KeyError: - timeout = csr_client.TIMEOUT - try: - netaddr.IPAddress(rest_mgmt_ip) - except netaddr.core.AddrFormatError: - LOG.error(_LE("Ignoring Cisco CSR for subnet %s - " - "REST management is not an IP address"), - for_router) - continue - try: - netaddr.IPAddress(tunnel_ip) - except netaddr.core.AddrFormatError: - LOG.error(_LE("Ignoring Cisco CSR for router %s - " - "local tunnel is not an IP address"), - for_router) - continue - m = tunnel_if_re.match(tunnel_if) - if not m: - LOG.error(_LE("Malformed interface name for Cisco " - "CSR router entry - %s"), tunnel_if) - continue - csrs_found[for_router] = {'rest_mgmt_ip': rest_mgmt_ip, - 'tunnel_ip': tunnel_ip, - 'username': username, - 'password': password, - 'host': host, - 'tunnel_if': tunnel_if, - 'timeout': timeout} - - LOG.debug("Found CSR for router %(router)s: %(info)s", - {'router': for_router, - 'info': csrs_found[for_router]}) - LOG.debug("Found %d router entries", len(csrs_found)) - return csrs_found - - -def _get_router_id_via_external_ip(context, external_ip): - '''Find router ID for router with matching GW port IP.''' - query = context.session.query(l3_db.Router) - query = query.join(models_v2.Port, - l3_db.Router.gw_port_id == models_v2.Port.id) - query = query.join(models_v2.IPAllocation, - models_v2.IPAllocation.port_id == models_v2.Port.id) - query = query.filter(models_v2.IPAllocation.ip_address == external_ip) - router = query.first() - if router: - return router.id - - -def get_active_routers_for_host(context, host): - '''Get list of routers from INI file that use host requested.''' - routers = [] - configured_routers = get_available_csrs_from_config(cfg.CONF.config_file) - if not configured_routers: - LOG.error(_LE("No routers found in INI file!")) - return routers - for router_ip, info in configured_routers.items(): - if host == info['host']: - router_id = _get_router_id_via_external_ip(context, router_ip) - if router_id: - LOG.debug("Found router %(router)s on host %(host)s", - {'router': router_id, 'host': host}) - routers.append({ - 'id': router_id, - 'hosting_device': { - 'management_ip_address': info['rest_mgmt_ip'], - 'credentials': {'username': info['username'], - 'password': info['password']} - }, - 'tunnel_if': info['tunnel_if'], - 'tunnel_ip': info['tunnel_ip'] - }) - else: - LOG.error(_LE("Unable to lookup router ID based on router's " - "public IP (%s) in INI file"), router_ip) - if not routers: - LOG.error(_LE("No matching routers on host %s"), host) - return routers - - -def _get_external_ip_for_router(context, router_id): - '''Find port that is the gateway port for router.''' - query = context.session.query(models_v2.Port) - query = query.join(l3_db.Router, - l3_db.Router.gw_port_id == models_v2.Port.id) - query = query.filter(l3_db.Router.id == router_id) - gw_port = query.first() - if gw_port: - return gw_port.fixed_ips[0]['ip_address'] - - -def get_host_for_router(context, router_id): - '''Find out GW port for router and look-up in INI file to get host. - - For this interim solution, there is the possibility that the INI file is - not configured correctly and either there are no entries or no matching - entry. An error will be reported in log and resource will remain in the - same state (e.g. PENDING_CREATE). - ''' - routers = get_available_csrs_from_config(cfg.CONF.config_file) - if not routers: - LOG.error(_LE("No routers found in INI file!")) - return '' - router_public_ip = _get_external_ip_for_router(context, router_id) - if router_public_ip: - router = routers.get(router_public_ip) - if router: - LOG.debug("Found host %(host)s for router %(router)s", - {'host': router['host'], 'router': router_id}) - return router['host'] - LOG.error(_LE("Unable to find host for router %s"), router_id) - return '' diff --git a/neutron/services/vpn/service_drivers/cisco_ipsec.py b/neutron/services/vpn/service_drivers/cisco_ipsec.py index 28e3d589fe..8e338f529e 100644 --- a/neutron/services/vpn/service_drivers/cisco_ipsec.py +++ b/neutron/services/vpn/service_drivers/cisco_ipsec.py @@ -15,10 +15,10 @@ from neutron.common import rpc as n_rpc from neutron.db.vpn import vpn_db from neutron.openstack.common import log as logging +from neutron.plugins.cisco.l3.plugging_drivers import ( + n1kv_plugging_constants as n1kv_constants) from neutron.services.vpn.common import topics from neutron.services.vpn import service_drivers -from neutron.services.vpn.service_drivers import ( - cisco_cfg_loader as via_cfg_file) from neutron.services.vpn.service_drivers import cisco_csr_db as csr_id_map from neutron.services.vpn.service_drivers import cisco_validator @@ -30,6 +30,7 @@ LIFETIME_LIMITS = {'IKE Policy': {'min': 60, 'max': 86400}, 'IPSec Policy': {'min': 120, 'max': 2592000}} MIN_CSR_MTU = 1500 MAX_CSR_MTU = 9192 +VRF_SUFFIX_LEN = 6 class CiscoCsrIPsecVpnDriverCallBack(n_rpc.RpcCallback): @@ -59,7 +60,8 @@ class CiscoCsrIPsecVpnDriverCallBack(n_rpc.RpcCallback): def get_vpn_services_on_host(self, context, host=None): """Returns info on the VPN services on the host.""" - routers = via_cfg_file.get_active_routers_for_host(context, host) + routers = self.driver.l3_plugin.get_active_routers_for_host(context, + host) host_vpn_services = [] for router in routers: vpn_services = self.get_vpn_services_using(context, router['id']) @@ -96,11 +98,8 @@ class CiscoCsrIPsecVpnAgentApi(service_drivers.BaseIPsecVpnAgentApi, admin_context = context.is_admin and context or context.elevated() if not version: version = self.RPC_API_VERSION - host = via_cfg_file.get_host_for_router(admin_context, router_id) - if not host: - # NOTE: This is a config error for workaround. At this point we - # can't set state of resource to error. - return + host = self.driver.l3_plugin.get_host_for_router(admin_context, + router_id) LOG.debug('Notify agent at %(topic)s.%(host)s the message ' '%(method)s %(args)s for router %(router)s', {'topic': self.topic, @@ -191,17 +190,27 @@ class CiscoCsrIPsecVPNDriver(service_drivers.VpnDriver): 'ike_policy_id': u'%d' % ike_id, 'ipsec_policy_id': u'%s' % ipsec_id} - def _create_tunnel_interface(self, router_info): - return router_info['tunnel_if'] + def _create_interface(self, interface_info): + hosting_info = interface_info['hosting_info'] + vlan = hosting_info['segmentation_id'] + # Port name "currently" is t{1,2}_p:1, as only one router per CSR, + # but will keep a semi-generic algorithm + port_name = hosting_info['hosting_port_name'] + name, sep, num = port_name.partition(':') + offset = 1 if name in n1kv_constants.T2_PORT_NAME else 0 + if_num = int(num) * 2 + offset + return 'GigabitEthernet%d.%d' % (if_num, vlan) def _get_router_info(self, router_info): hosting_device = router_info['hosting_device'] return {'rest_mgmt_ip': hosting_device['management_ip_address'], - 'external_ip': router_info['tunnel_ip'], 'username': hosting_device['credentials']['username'], 'password': hosting_device['credentials']['password'], - 'tunnel_if_name': self._create_tunnel_interface(router_info), - # TODO(pcm): Add protocol_port, if avail from L3 router plugin + 'inner_if_name': self._create_interface( + router_info['_interfaces'][0]), + 'outer_if_name': self._create_interface( + router_info['gw_port']), + 'vrf': 'nrouter-' + router_info['id'][:VRF_SUFFIX_LEN], 'timeout': 30} # Hard-coded for now def _make_vpnservice_dict(self, context, vpnservice, router_info): diff --git a/neutron/tests/unit/services/vpn/device_drivers/test_cisco_csr_rest.py b/neutron/tests/unit/services/vpn/device_drivers/test_cisco_csr_rest.py index b7092af457..5d86cead58 100644 --- a/neutron/tests/unit/services/vpn/device_drivers/test_cisco_csr_rest.py +++ b/neutron/tests/unit/services/vpn/device_drivers/test_cisco_csr_rest.py @@ -26,6 +26,7 @@ from neutron.tests import base dummy_policy_id = 'dummy-ipsec-policy-id-name' +TEST_VRF = 'nrouter-123456' BASE_URL = 'https://%s:55443/api/v1/' LOCAL_URL = 'https://localhost:55443/api/v1/' @@ -33,18 +34,18 @@ URI_HOSTNAME = 'global/host-name' URI_USERS = 'global/local-users' URI_AUTH = 'auth/token-services' URI_INTERFACE_GE1 = 'interfaces/GigabitEthernet1' -URI_PSK = 'vpn-svc/ike/keyrings' +URI_PSK = 'vrf/' + TEST_VRF + '/vpn-svc/ike/keyrings' URI_PSK_ID = URI_PSK + '/%s' URI_IKE_POLICY = 'vpn-svc/ike/policies' URI_IKE_POLICY_ID = URI_IKE_POLICY + '/%s' URI_IPSEC_POLICY = 'vpn-svc/ipsec/policies' URI_IPSEC_POLICY_ID = URI_IPSEC_POLICY + '/%s' -URI_IPSEC_CONN = 'vpn-svc/site-to-site' +URI_IPSEC_CONN = 'vrf/' + TEST_VRF + '/vpn-svc/site-to-site' URI_IPSEC_CONN_ID = URI_IPSEC_CONN + '/%s' URI_KEEPALIVE = 'vpn-svc/ike/keepalive' -URI_ROUTES = 'routing-svc/static-routes' +URI_ROUTES = 'vrf/' + TEST_VRF + '/routing-svc/static-routes' URI_ROUTES_ID = URI_ROUTES + '/%s' -URI_SESSIONS = 'vpn-svc/site-to-site/active/sessions' +URI_SESSIONS = 'vrf/' + TEST_VRF + '/vpn-svc/site-to-site/active/sessions' # Note: Helper functions to test reuse of IDs. @@ -69,6 +70,7 @@ class CiscoCsrBaseTestCase(base.BaseTestCase): self.base_url = BASE_URL % host self.requests = self.useFixture(mock_fixture.Fixture()) info = {'rest_mgmt_ip': host, 'tunnel_ip': tunnel_ip, + 'vrf': 'nrouter-123456', 'username': 'stack', 'password': 'cisco', 'timeout': timeout} self.csr = csr_client.CsrRestClient(info) @@ -1131,6 +1133,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): u'ipsec-policy-id': u'%s' % ipsec_policy_id, u'ike-profile-id': None, u'mtu': 1500, + u'tunnel-vrf': TEST_VRF, u'local-device': { u'ip-address': '10.3.0.1/24', u'tunnel-ip-address': '10.10.10.10' @@ -1161,6 +1164,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): u'ike-profile-id': None, u'vpn-type': u'site-to-site', u'mtu': 1500, + u'tunnel-vrf': TEST_VRF, u'ip-version': u'ipv4'} expected_connection.update(connection_info) location = self.csr.create_ipsec_connection(connection_info) @@ -1206,6 +1210,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): u'ike-profile-id': None, u'vpn-type': u'site-to-site', u'mtu': 1500, + u'tunnel-vrf': TEST_VRF, u'ip-version': u'ipv4'} expected_connection.update(connection_info) location = self.csr.create_ipsec_connection(connection_info) @@ -1213,7 +1218,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): self.csr.delete_ipsec_connection, tunnel_name) self.assertEqual(requests.codes.CREATED, self.csr.status) - self.assertIn('vpn-svc/site-to-site/' + tunnel_name, location) + self.assertIn(URI_IPSEC_CONN_ID % tunnel_name, location) # Check the hard-coded items that get set as well... self._helper_register_ipsec_conn_get(tunnel_name, override={ @@ -1247,6 +1252,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): } expected_connection = {u'kind': u'object#vpn-site-to-site', u'ike-profile-id': None, + u'tunnel-vrf': TEST_VRF, u'vpn-type': u'site-to-site', u'ip-version': u'ipv4'} expected_connection.update(connection_info) @@ -1285,6 +1291,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): } expected_connection = {u'kind': u'object#vpn-site-to-site', u'ike-profile-id': None, + u'tunnel-vrf': TEST_VRF, u'vpn-type': u'site-to-site', u'ip-version': u'ipv4'} expected_connection.update(connection_info) @@ -1427,6 +1434,7 @@ class TestCsrRestIPSecConnectionCreate(CiscoCsrBaseTestCase): } expected_connection = {u'kind': u'object#vpn-site-to-site', u'ike-profile-id': None, + u'tunnel-vrf': TEST_VRF, u'vpn-type': u'site-to-site', u'ip-version': u'ipv4'} expected_connection.update(connection_info) diff --git a/neutron/tests/unit/services/vpn/device_drivers/test_cisco_ipsec.py b/neutron/tests/unit/services/vpn/device_drivers/test_cisco_ipsec.py index c6a6c68463..f9bd534ef9 100644 --- a/neutron/tests/unit/services/vpn/device_drivers/test_cisco_ipsec.py +++ b/neutron/tests/unit/services/vpn/device_drivers/test_cisco_ipsec.py @@ -70,12 +70,10 @@ class TestCiscoCsrIPSecConnection(base.BaseTestCase): 'lifetime_value': 3600}, 'cisco': {'site_conn_id': 'Tunnel0', 'ike_policy_id': 222, - 'ipsec_policy_id': 333, - 'router_public_ip': '172.24.4.23'} + 'ipsec_policy_id': 333} } self.csr = mock.Mock(spec=csr_client.CsrRestClient) self.csr.status = 201 # All calls to CSR REST API succeed - self.csr.tunnel_ip = '172.24.4.23' self.ipsec_conn = ipsec_driver.CiscoCsrIPSecConnection(self.conn_info, self.csr) @@ -241,8 +239,7 @@ class TestCiscoCsrIPsecConnectionCreateTransforms(base.BaseTestCase): 'lifetime_value': 3600}, 'cisco': {'site_conn_id': 'Tunnel0', 'ike_policy_id': 222, - 'ipsec_policy_id': 333, - 'router_public_ip': '172.24.4.23'} + 'ipsec_policy_id': 333} } self.csr = mock.Mock(spec=csr_client.CsrRestClient) self.csr.tunnel_ip = '172.24.4.23' @@ -437,12 +434,14 @@ class TestCiscoCsrIPsecDeviceDriverSyncStatuses(base.BaseTestCase): ipsec_driver.CiscoCsrIPSecConnection, 'set_admin_state').start() self.csr = mock.Mock() - self.router_info = {u'router_info': {'rest_mgmt_ip': '2.2.2.2', - 'tunnel_ip': '1.1.1.3', - 'username': 'me', - 'password': 'password', - 'timeout': 120, - 'external_ip': u'1.1.1.1'}} + self.router_info = { + u'router_info': {'rest_mgmt_ip': '2.2.2.2', + 'tunnel_ip': '1.1.1.3', + 'username': 'me', + 'password': 'password', + 'timeout': 120, + 'outer_if_name': u'GigabitEthernet3.102', + 'inner_if_name': u'GigabitEthernet3.101'}} self.service123_data = {u'id': u'123', u'status': constants.DOWN, u'admin_state_up': False} diff --git a/neutron/tests/unit/services/vpn/service_drivers/test_cisco_config_loader.py b/neutron/tests/unit/services/vpn/service_drivers/test_cisco_config_loader.py deleted file mode 100644 index 37e5fd5464..0000000000 --- a/neutron/tests/unit/services/vpn/service_drivers/test_cisco_config_loader.py +++ /dev/null @@ -1,476 +0,0 @@ -# Copyright 2014 Cisco Systems, 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. - -"""Test module for interim implementation - to be removed later. - -This tests using an INI file to obtain Cisco CSR router information -for IPSec site-to-site connections. Once the Cisco L3 router plugin -blueprint has been up-streamed, this can be removed and production code -switched to use the L3 plugin methods for: - - get_host_for_router() - get_active_routers_for_host() - -TODO(pcm): remove module, when Cisco L3 router plugin is up-streamed. -""" - -import os -import tempfile - -import mock -from oslo.config import cfg - -from neutron import context as ctx -from neutron.openstack.common import uuidutils -from neutron.services.vpn.device_drivers import ( - cisco_csr_rest_client as csr_client) -from neutron.services.vpn.service_drivers import ( - cisco_cfg_loader as cfg_loader) -from neutron.tests import base - -_uuid = uuidutils.generate_uuid -FAKE_ROUTER_ID = _uuid() -CISCO_GET_ROUTER_IP = ('neutron.services.vpn.service_drivers.' - 'cisco_cfg_loader._get_external_ip_for_router') -CISCO_GET_ROUTER_ID = ('neutron.services.vpn.service_drivers.' - 'cisco_cfg_loader._get_router_id_via_external_ip') - - -def create_tempfile(contents): - (fd, path) = tempfile.mkstemp(prefix='test', suffix='.conf') - try: - os.write(fd, contents.encode('utf-8')) - finally: - os.close(fd) - return path - - -class TestCiscoCsrServiceDriverConfigLoading(base.BaseTestCase): - - def test_loading_csr_configuration(self): - """Ensure that Cisco CSR configs can be loaded from config files.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - expected = {'3.2.1.1': {'rest_mgmt_ip': '10.20.30.1', - 'tunnel_ip': '3.2.1.3', - 'username': 'me', - 'password': 'secret', - 'host': 'compute-node', - 'tunnel_if': 'GigabitEthernet3', - 'timeout': 5.0}} - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual(expected, csrs_found) - - def test_loading_config_without_timeout(self): - """Cisco CSR config without timeout will use default timeout.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n') - expected = {'3.2.1.1': {'rest_mgmt_ip': '10.20.30.1', - 'tunnel_ip': '3.2.1.3', - 'username': 'me', - 'password': 'secret', - 'host': 'compute-node', - 'tunnel_if': 'GigabitEthernet3', - 'timeout': csr_client.TIMEOUT}} - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual(expected, csrs_found) - - def test_skip_loading_duplicate_csr_configuration(self): - """Failure test that duplicate configurations are ignored.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 5.5.5.3\n' - 'tunnel_ip = 3.2.1.6\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n') - expected = {'3.2.1.1': {'rest_mgmt_ip': '10.20.30.1', - 'tunnel_ip': '3.2.1.3', - 'username': 'me', - 'password': 'secret', - 'host': 'compute-node', - 'tunnel_if': 'GigabitEthernet3', - 'timeout': 5.0}} - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual(expected, csrs_found) - - def test_fail_loading_config_with_invalid_timeout(self): - """Failure test of invalid timeout in config info.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = yes\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_fail_loading_config_missing_required_info(self): - """Failure test of config missing required info.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:1.1.1.0]\n' - # No rest_mgmt - 'tunnel_ip = 1.1.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - - '[CISCO_CSR_REST:2.2.2.0]\n' - 'rest_mgmt = 10.20.30.2\n' - # No tunnel_ip - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - - '[CISCO_CSR_REST:3.3.3.0]\n' - 'rest_mgmt = 10.20.30.3\n' - 'tunnel_ip = 3.3.3.3\n' - # No username - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - - '[CISCO_CSR_REST:4.4.4.0]\n' - 'rest_mgmt = 10.20.30.4\n' - 'tunnel_ip = 4.4.4.4\n' - 'username = me\n' - # No password - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - - '[CISCO_CSR_REST:5.5.5.0]\n' - 'rest_mgmt = 10.20.30.5\n' - 'tunnel_ip = 5.5.5.5' - 'username = me\n' - 'password = secret\n' - # No host - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n' - - '[CISCO_CSR_REST:6.6.6.0]\n' - 'rest_mgmt = 10.20.30.6\n' - 'tunnel_ip = 6.6.6.6' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - # No tunnel_if - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_fail_loading_config_with_invalid_router_id(self): - """Failure test of config with invalid rotuer ID.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:4.3.2.1.9]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 4.3.2.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_fail_loading_config_with_invalid_mgmt_ip(self): - """Failure test of configuration with invalid management IP address.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 1.1.1.1.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_fail_loading_config_with_invalid_tunnel_ip(self): - """Failure test of configuration with invalid tunnel IP address.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 1.1.1.1\n' - 'tunnel_ip = 3.2.1.4.5\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_failure_no_configurations_entries(self): - """Failure test config file without any CSR definitions.""" - cfg_file = create_tempfile('NO CISCO SECTION AT ALL\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_failure_no_csr_configurations_entries(self): - """Failure test config file without any CSR definitions.""" - cfg_file = create_tempfile('[SOME_CONFIG:123]\n' - 'username = me\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_missing_config_value(self): - """Failure test of config file missing a value for attribute.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = \n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - def test_ignores_invalid_attribute_in_config(self): - """Test ignoring of config file with invalid attribute.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 1.1.1.1\n' - 'bogus = abcdef\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 15.5\n') - expected = {'3.2.1.1': {'rest_mgmt_ip': '1.1.1.1', - 'tunnel_ip': '3.2.1.3', - 'username': 'me', - 'password': 'secret', - 'host': 'compute-node', - 'tunnel_if': 'GigabitEthernet3', - 'timeout': 15.5}} - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual(expected, csrs_found) - - def test_invalid_management_interface(self): - """Failure test of invalid management interface name.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 1.1.1.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = compute-node\n' - 'tunnel_if = GigabitEthernet9\n' - 'timeout = 5.0\n') - csrs_found = cfg_loader.get_available_csrs_from_config([cfg_file]) - self.assertEqual({}, csrs_found) - - -class TestCiscoCsrRouterInfo(base.BaseTestCase): - - def setUp(self): - super(TestCiscoCsrRouterInfo, self).setUp() - self.context = ctx.get_admin_context() - - def test_find_host_for_router(self): - """Look up host in INI file for a router.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet1\n' - 'mgmt_vlan = 100\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_IP, return_value='3.2.1.1').start() - self.assertEqual('ubuntu', - cfg_loader.get_host_for_router(self.context, - FAKE_ROUTER_ID)) - - def test_failed_to_find_host_as_no_routers_in_ini(self): - """Fail to find host, as no router info in INI file.""" - cfg_file = create_tempfile('\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_IP, return_value='5.5.5.5').start() - self.assertEqual('', - cfg_loader.get_host_for_router(self.context, - FAKE_ROUTER_ID)) - - def test_failed_no_matching_router_to_obtain_host(self): - """Fail to find INI info for router provided.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_IP, return_value='5.5.5.5').start() - self.assertEqual('', - cfg_loader.get_host_for_router(self.context, - FAKE_ROUTER_ID)) - - def test_failed_to_find_router_ip(self): - """Fail to lookup router IP, preventing search in INI file.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_IP, return_value=None).start() - self.assertEqual('', - cfg_loader.get_host_for_router(self.context, - FAKE_ROUTER_ID)) - - def _get_router_id_from_external_ip(self, context, ip): - if ip == '3.2.1.1': - return '123' - elif ip == '4.3.2.1': - return '456' - - def test_get_one_active_router_for_host(self): - """Get router info from INI for host specified.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.3\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet2\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_ID, - side_effect=self._get_router_id_from_external_ip).start() - expected = { - 'id': '123', - 'hosting_device': { - 'management_ip_address': '10.20.30.1', - 'credentials': {'username': 'me', 'password': 'secret'} - }, - 'tunnel_if': 'GigabitEthernet2', - 'tunnel_ip': '3.2.1.3' - } - routers = cfg_loader.get_active_routers_for_host(self.context, - "ubuntu") - self.assertEqual([expected], routers) - - def test_get_two_active_routers_for_host(self): - """Get info for two routers, from INI file, for host specified.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:3.2.1.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 3.2.1.1\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet2\n' - 'timeout = 5.0\n' - '[CISCO_CSR_REST:4.3.2.1]\n' - 'rest_mgmt = 10.20.30.2\n' - 'tunnel_ip = 4.3.2.1\n' - 'username = you\n' - 'password = insecure\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_ID, - side_effect=self._get_router_id_from_external_ip).start() - expected_a = { - 'id': '123', - 'hosting_device': { - 'management_ip_address': '10.20.30.1', - 'credentials': {'username': 'me', 'password': 'secret'} - }, - 'tunnel_if': 'GigabitEthernet2', - 'tunnel_ip': '3.2.1.1' - } - expected_b = { - 'id': '456', - 'hosting_device': { - 'management_ip_address': '10.20.30.2', - 'credentials': {'username': 'you', 'password': 'insecure'} - }, - 'tunnel_if': 'GigabitEthernet3', - 'tunnel_ip': '4.3.2.1' - } - routers = cfg_loader.get_active_routers_for_host(self.context, - "ubuntu") - sorted_routers = sorted(routers, key=lambda key: key['id']) - self.assertEqual([expected_a, expected_b], sorted_routers) - - def test_failure_to_find_routers_for_host(self): - """Fail to find a router in INI with matching host name.""" - routers = cfg_loader.get_active_routers_for_host(self.context, - "bogus") - self.assertEqual([], routers) - - def test_failure_to_lookup_router_id_for_host(self): - """Fail to get router UUID for router in INI matching host name.""" - cfg_file = create_tempfile( - '[CISCO_CSR_REST:6.6.6.1]\n' - 'rest_mgmt = 10.20.30.1\n' - 'tunnel_ip = 6.6.6.1\n' - 'username = me\n' - 'password = secret\n' - 'host = ubuntu\n' - 'tunnel_if = GigabitEthernet3\n' - 'timeout = 5.0\n') - cfg.CONF.set_override('config_file', [cfg_file]) - mock.patch(CISCO_GET_ROUTER_ID, - side_effect=self._get_router_id_from_external_ip).start() - routers = cfg_loader.get_active_routers_for_host(self.context, - "ubuntu") - self.assertEqual([], routers) diff --git a/neutron/tests/unit/services/vpn/service_drivers/test_cisco_ipsec.py b/neutron/tests/unit/services/vpn/service_drivers/test_cisco_ipsec.py index 10b9d987c8..2c18cc8e1c 100644 --- a/neutron/tests/unit/services/vpn/service_drivers/test_cisco_ipsec.py +++ b/neutron/tests/unit/services/vpn/service_drivers/test_cisco_ipsec.py @@ -74,14 +74,10 @@ class TestCiscoIPsecDriverValidation(base.BaseTestCase): def setUp(self): super(TestCiscoIPsecDriverValidation, self).setUp() - mock.patch('neutron.common.rpc.create_connection').start() self.l3_plugin = mock.Mock() mock.patch( 'neutron.manager.NeutronManager.get_service_plugins', return_value={constants.L3_ROUTER_NAT: self.l3_plugin}).start() - self.core_plugin = mock.Mock() - mock.patch('neutron.manager.NeutronManager.get_plugin', - return_value=self.core_plugin).start() self.context = n_ctx.Context('some_user', 'some_tenant') self.vpn_service = {'router_id': '123'} self.router = mock.Mock() @@ -337,18 +333,20 @@ class TestCiscoIPsecDriver(testlib_api.SqlTestCase): mock.patch('neutron.common.rpc.create_connection').start() service_plugin = mock.Mock() - service_plugin.get_host_for_router.return_value = FAKE_HOST - # TODO(pcm): Remove when Cisco L3 router plugin support available - mock.patch('neutron.services.vpn.service_drivers.' - 'cisco_cfg_loader.get_host_for_router', - return_value=FAKE_HOST).start() service_plugin._get_vpnservice.return_value = { 'router_id': _uuid() } - get_service_plugin = mock.patch( - 'neutron.manager.NeutronManager.get_service_plugins').start() - get_service_plugin.return_value = { - constants.L3_ROUTER_NAT: service_plugin} + + l3_plugin = mock.Mock() + mock.patch( + 'neutron.manager.NeutronManager.get_service_plugins', + return_value={constants.L3_ROUTER_NAT: l3_plugin}).start() + + l3_plugin.get_host_for_router.return_value = FAKE_HOST + l3_agent = mock.Mock() + l3_agent.host = 'some-host' + l3_plugin.get_l3_agents_hosting_routers.return_value = [l3_agent] + self.driver = ipsec_driver.CiscoCsrIPsecVPNDriver(service_plugin) mock.patch.object(csr_db, 'create_tunnel_mapping').start() self.context = n_ctx.Context('some_user', 'some_tenant') @@ -388,3 +386,58 @@ class TestCiscoIPsecDriver(testlib_api.SqlTestCase): self._test_update(self.driver.delete_vpnservice, [FAKE_VPN_SERVICE], {'reason': 'vpn-service-delete'}) + + +class TestCiscoIPsecDriverRequests(base.BaseTestCase): + + """Test handling device driver requests for service info.""" + + def setUp(self): + super(TestCiscoIPsecDriverRequests, self).setUp() + mock.patch('neutron.common.rpc.create_connection').start() + + service_plugin = mock.Mock() + self.driver = ipsec_driver.CiscoCsrIPsecVPNDriver(service_plugin) + + def test_build_router_tunnel_interface_name(self): + """Check formation of inner/outer interface name for CSR router.""" + router_info = { + '_interfaces': [ + {'hosting_info': {'segmentation_id': 100, + 'hosting_port_name': 't1_p:1'}} + ], + 'gw_port': + {'hosting_info': {'segmentation_id': 200, + 'hosting_port_name': 't2_p:1'}} + } + self.assertEqual( + 'GigabitEthernet2.100', + self.driver._create_interface(router_info['_interfaces'][0])) + self.assertEqual( + 'GigabitEthernet3.200', + self.driver._create_interface(router_info['gw_port'])) + + def test_build_router_info(self): + """Check creation of CSR info to send to device driver.""" + router_info = { + 'hosting_device': { + 'management_ip_address': '1.1.1.1', + 'credentials': {'username': 'me', 'password': 'secret'} + }, + 'gw_port': + {'hosting_info': {'segmentation_id': 101, + 'hosting_port_name': 't2_p:1'}}, + 'id': u'c607b58e-f150-4289-b83f-45623578d122', + '_interfaces': [ + {'hosting_info': {'segmentation_id': 100, + 'hosting_port_name': 't1_p:1'}} + ] + } + expected = {'rest_mgmt_ip': '1.1.1.1', + 'username': 'me', + 'password': 'secret', + 'inner_if_name': 'GigabitEthernet2.100', + 'outer_if_name': 'GigabitEthernet3.101', + 'vrf': 'nrouter-c607b5', + 'timeout': 30} + self.assertEqual(expected, self.driver._get_router_info(router_info))