--- /dev/null
+# neutron-rootwrap command filters for nodes on which neutron is
+# expected to control network
+#
+# This file should be owned by (and only-writeable by) the root user
+
+# format seems to be
+# cmd-name: filter-name, raw-command, user, args
+
+[Filters]
+
+ip: IpFilter, ip, root
+ip_exec: IpNetnsExecFilter, ip, root
+openswan: CommandFilter, ipsec, root
--- /dev/null
+[DEFAULT]
+# VPN-Agent configuration file
+# Note vpn-agent inherits l3-agent, so you can use configs on l3-agent also
+
+[vpnagent]
+#vpn device drivers which vpn agent will use
+#If we want to use multiple drivers, we need to define this option multiple times.
+#vpn_device_driver=neutron.services.vpn.device_drivers.ipsec.OpenSwanDriver
+#vpn_device_driver=another_driver
+
+[ipsec]
+#Status check interval
+#ipsec_status_check_interval=60
LOG.info(_("agent_updated by server side %s!"), payload)
-def main():
+def main(manager='neutron.agent.l3_agent.L3NATAgentWithStateReport'):
eventlet.monkey_patch()
conf = cfg.CONF
conf.register_opts(L3NATAgent.OPTS)
binary='neutron-l3-agent',
topic=topics.L3_AGENT,
report_interval=cfg.CONF.AGENT.report_interval,
- manager='neutron.agent.l3_agent.L3NATAgentWithStateReport')
+ manager=manager)
service.launch(server).wait()
from sqlalchemy import orm
from sqlalchemy.orm import exc
-from neutron.common import constants as q_constants
+from neutron.common import constants as n_constants
from neutron.db import agentschedulers_db as agent_db
from neutron.db import api as qdbapi
from neutron.db import db_base_plugin_v2 as base_db
from neutron.openstack.common import log as logging
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants
+from neutron.plugins.common import utils
LOG = logging.getLogger(__name__)
def assert_update_allowed(self, obj):
status = getattr(obj, 'status', None)
- if status != constants.ACTIVE:
+ if utils.in_pending_status(status):
raise vpnaas.VPNStateInvalid(id=id, state=status)
def _make_ipsec_site_connection_dict(self, ipsec_site_conn, fields=None):
)
context.session.delete(ipsec_site_conn_db)
+ def _get_ipsec_site_connection(
+ self, context, ipsec_site_conn_id):
+ return self._get_resource(
+ context, IPsecSiteConnection, ipsec_site_conn_id)
+
def get_ipsec_site_connection(self, context,
ipsec_site_conn_id, fields=None):
- ipsec_site_conn_db = self._get_resource(
- context, IPsecSiteConnection, ipsec_site_conn_id
- )
+ ipsec_site_conn_db = self._get_ipsec_site_connection(
+ context, ipsec_site_conn_id)
return self._make_ipsec_site_connection_dict(
ipsec_site_conn_db, fields)
def update_vpnservice(self, context, vpnservice_id, vpnservice):
vpns = vpnservice['vpnservice']
with context.session.begin(subtransactions=True):
- vpnservice = context.session.query(IPsecSiteConnection).filter_by(
- vpnservice_id=vpnservice_id).first()
- if vpnservice:
- raise vpnaas.VPNServiceInUse(vpnservice_id=vpnservice_id)
vpns_db = self._get_resource(context, VPNService, vpnservice_id)
self.assert_update_allowed(vpns_db)
if vpns:
plugin = manager.NeutronManager.get_plugin()
agent = plugin._get_agent_by_type_and_host(
- context, q_constants.AGENT_TYPE_L3, host)
+ context, n_constants.AGENT_TYPE_L3, host)
if not agent.admin_state_up:
return []
query = context.session.query(VPNService)
agent_db.RouterL3AgentBinding.l3_agent_id == agent.id)
return query
- def update_status_on_host(self, context, host, active_services):
+ def update_status_by_agent(self, context, service_status_info_list):
+ """Updating vpnservice and vpnconnection status.
+
+ :param context: context variable
+ :param service_status_info_list: list of status
+ The structure is
+ [{id: vpnservice_id,
+ status: ACTIVE|DOWN|ERROR,
+ updated_pending_status: True|False
+ ipsec_site_connections: {
+ ipsec_site_connection_id: {
+ status: ACTIVE|DOWN|ERROR,
+ updated_pending_status: True|False
+ }
+ }]
+ The agent will set updated_pending_status as True,
+ when agent update any pending status.
+ """
with context.session.begin(subtransactions=True):
- vpnservices = self._get_agent_hosting_vpn_services(
- context, host)
- for vpnservice in vpnservices:
- if vpnservice.id in active_services:
- if vpnservice.status != constants.ACTIVE:
- vpnservice.status = constants.ACTIVE
- else:
- if vpnservice.status != constants.ERROR:
- vpnservice.status = constants.ERROR
+ for vpnservice in service_status_info_list:
+ try:
+ vpnservice_db = self._get_vpnservice(
+ context, vpnservice['id'])
+ except vpnaas.VPNServiceNotFound:
+ LOG.warn(_('vpnservice %s in db is already deleted'),
+ vpnservice['id'])
+ continue
+
+ if (not utils.in_pending_status(vpnservice_db.status)
+ or vpnservice['updated_pending_status']):
+ vpnservice_db.status = vpnservice['status']
+ for conn_id, conn in vpnservice[
+ 'ipsec_site_connections'].items():
+ try:
+ conn_db = self._get_ipsec_site_connection(
+ context, conn_id)
+ except vpnaas.IPsecSiteConnectionNotFound:
+ continue
+ if (not utils.in_pending_status(conn_db.status)
+ or conn['updated_pending_status']):
+ conn_db.status = conn['status']
message = _("IPsecPolicy %(ipsecpolicy_id)s is still in use")
+class DeviceDriverImportError(qexception.NeutronException):
+ message = _("Can not load driver :%(device_driver)s")
+
+
vpn_supported_initiators = ['bi-directional', 'response-only']
vpn_supported_encryption_algorithms = ['3des', 'aes-128',
'aes-192', 'aes-256']
]
vpn_supported_transform_protocols = ['esp', 'ah', 'ah-esp']
vpn_supported_encapsulation_mode = ['tunnel', 'transport']
-vpn_supported_lifetime_units = ['seconds', 'kilobytes']
+#TODO(nati) add kilobytes when we support it
+vpn_supported_lifetime_units = ['seconds']
vpn_supported_pfs = ['group2', 'group5', 'group14']
vpn_supported_ike_versions = ['v1', 'v2']
vpn_supported_auth_mode = ['psk']
# Service operation status constants
ACTIVE = "ACTIVE"
+DOWN = "DOWN"
PENDING_CREATE = "PENDING_CREATE"
PENDING_UPDATE = "PENDING_UPDATE"
PENDING_DELETE = "PENDING_DELETE"
from neutron.common import exceptions as q_exc
from neutron.common import utils
+from neutron.plugins.common import constants
def verify_vlan_range(vlan_range):
else:
networks.setdefault(network, [])
return networks
+
+
+def in_pending_status(status):
+ return status in (constants.PENDING_CREATE,
+ constants.PENDING_UPDATE,
+ constants.PENDING_DELETE)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+from oslo.config import cfg
+
+from neutron.agent import l3_agent
+from neutron.extensions import vpnaas
+from neutron.openstack.common import importutils
+
+vpn_agent_opts = [
+ cfg.MultiStrOpt(
+ 'vpn_device_driver',
+ default=['neutron.services.vpn.device_drivers.'
+ 'ipsec.OpenSwanDriver'],
+ help=_("The vpn device drivers Neutron will use")),
+]
+cfg.CONF.register_opts(vpn_agent_opts, 'vpnagent')
+
+
+class VPNAgent(l3_agent.L3NATAgentWithStateReport):
+ """VPNAgent class which can handle vpn service drivers."""
+ def __init__(self, host, conf=None):
+ super(VPNAgent, self).__init__(host=host, conf=conf)
+ self.setup_device_drivers(host)
+
+ def setup_device_drivers(self, host):
+ """Setting up device drivers.
+
+ :param host: hostname. This is needed for rpc
+ Each devices will stays as processes.
+ They will communiate with
+ server side service plugin using rpc with
+ device specific rpc topic.
+ :returns: None
+ """
+ device_drivers = cfg.CONF.vpnagent.vpn_device_driver
+ self.devices = []
+ for device_driver in device_drivers:
+ try:
+ self.devices.append(
+ importutils.import_object(device_driver, self, host))
+ except ImportError:
+ raise vpnaas.DeviceDriverImportError(
+ device_driver=device_driver)
+
+ def get_namespace(self, router_id):
+ """Get namespace of router.
+
+ :router_id: router_id
+ :returns: namespace string.
+ Note if the router is not exist, this function
+ returns None
+ """
+ router_info = self.router_info.get(router_id)
+ if not router_info:
+ return
+ return router_info.ns_name()
+
+ def add_nat_rule(self, router_id, chain, rule, top=False):
+ """Add nat rule in namespace.
+
+ :param router_id: router_id
+ :param chain: a string of chain name
+ :param rule: a string of rule
+ :param top: if top is true, the rule
+ will be placed on the top of chain
+ Note if there is no rotuer, this method do nothing
+ """
+ router_info = self.router_info.get(router_id)
+ if not router_info:
+ return
+ router_info.iptables_manager.ipv4['nat'].add_rule(
+ chain, rule, top=top)
+
+ def remove_nat_rule(self, router_id, chain, rule, top=False):
+ """Remove nat rule in namespace.
+
+ :param router_id: router_id
+ :param chain: a string of chain name
+ :param rule: a string of rule
+ :param top: unused
+ needed to have same argument with add_nat_rule
+ """
+ router_info = self.router_info.get(router_id)
+ if not router_info:
+ return
+ router_info.iptables_manager.ipv4['nat'].remove_rule(
+ chain, rule)
+
+ def iptables_apply(self, router_id):
+ """Apply IPtables.
+
+ :param router_id: router_id
+ This method do nothing if there is no router
+ """
+ router_info = self.router_info.get(router_id)
+ if not router_info:
+ return
+ router_info.iptables_manager.apply()
+
+ def _router_added(self, router_id, router):
+ """Router added event.
+
+ This method overwrites parent class method.
+ :param router_id: id of added router
+ :param router: dict of rotuer
+ """
+ super(VPNAgent, self)._router_added(router_id, router)
+ for device in self.devices:
+ device.create_router(router_id)
+
+ def _router_removed(self, router_id):
+ """Router removed event.
+
+ This method overwrites parent class method.
+ :param router_id: id of removed router
+ """
+ super(VPNAgent, self)._router_removed(router_id)
+ for device in self.devices:
+ device.destroy_router(router_id)
+
+ def _process_routers(self, routers, all_routers=False):
+ """Router sync event.
+
+ This method overwrites parent class method.
+ :param routers: list of routers
+ """
+ super(VPNAgent, self)._process_routers(routers, all_routers)
+ for device in self.devices:
+ device.sync(self.context, routers)
+
+
+def main():
+ l3_agent.main(
+ manager='neutron.services.vpn.agent.VPNAgent')
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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.
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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.
+
+
+IPSEC_DRIVER_TOPIC = 'ipsec_driver'
+IPSEC_AGENT_TOPIC = 'ipsec_agent'
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 abc
+
+
+class DeviceDriver(object):
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, agent, host):
+ pass
+
+ @abc.abstractmethod
+ def sync(self, context, processes):
+ pass
+
+ @abc.abstractmethod
+ def create_router(self, process_id):
+ pass
+
+ @abc.abstractmethod
+ def destroy_router(self, process_id):
+ pass
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 abc
+import copy
+import os
+import re
+import shutil
+
+import jinja2
+import netaddr
+from oslo.config import cfg
+
+from neutron.agent.linux import ip_lib
+from neutron.agent.linux import utils
+from neutron.common import rpc as q_rpc
+from neutron import context
+from neutron.openstack.common import lockutils
+from neutron.openstack.common import log as logging
+from neutron.openstack.common import loopingcall
+from neutron.openstack.common import rpc
+from neutron.openstack.common.rpc import proxy
+from neutron.plugins.common import constants
+from neutron.plugins.common import utils as plugin_utils
+from neutron.services.vpn.common import topics
+from neutron.services.vpn import device_drivers
+
+LOG = logging.getLogger(__name__)
+TEMPLATE_PATH = os.path.dirname(__file__)
+
+ipsec_opts = [
+ cfg.StrOpt(
+ 'config_base_dir',
+ default='$state_path/ipsec',
+ help=_('Location to store ipsec server config files')),
+ cfg.IntOpt('ipsec_status_check_interval',
+ default=60,
+ help=_("Interval for checking ipsec status"))
+]
+cfg.CONF.register_opts(ipsec_opts, 'ipsec')
+
+openswan_opts = [
+ cfg.StrOpt(
+ 'ipsec_config_template',
+ default=os.path.join(
+ TEMPLATE_PATH,
+ 'template/openswan/ipsec.conf.template'),
+ help='Template file for ipsec configuration'),
+ cfg.StrOpt(
+ 'ipsec_secret_template',
+ default=os.path.join(
+ TEMPLATE_PATH,
+ 'template/openswan/ipsec.secret.template'),
+ help='Template file for ipsec secret configuration')
+]
+
+cfg.CONF.register_opts(openswan_opts, 'openswan')
+
+JINJA_ENV = None
+
+STATUS_MAP = {
+ 'erouted': constants.ACTIVE,
+ 'unrouted': constants.DOWN
+}
+
+
+def _get_template(template_file):
+ global JINJA_ENV
+ if not JINJA_ENV:
+ templateLoader = jinja2.FileSystemLoader(searchpath="/")
+ JINJA_ENV = jinja2.Environment(loader=templateLoader)
+ return JINJA_ENV.get_template(template_file)
+
+
+class BaseSwanProcess():
+ """Swan Family Process Manager
+
+ This class manages start/restart/stop ipsec process.
+ This class create/delete config template
+ """
+ __metaclass__ = abc.ABCMeta
+
+ binary = "ipsec"
+ CONFIG_DIRS = [
+ 'var/run',
+ 'log',
+ 'etc',
+ 'etc/ipsec.d/aacerts',
+ 'etc/ipsec.d/acerts',
+ 'etc/ipsec.d/cacerts',
+ 'etc/ipsec.d/certs',
+ 'etc/ipsec.d/crls',
+ 'etc/ipsec.d/ocspcerts',
+ 'etc/ipsec.d/policies',
+ 'etc/ipsec.d/private',
+ 'etc/ipsec.d/reqs',
+ 'etc/pki/nssdb/'
+ ]
+
+ DIALECT_MAP = {
+ "3des": "3des",
+ "aes-128": "aes128",
+ "aes-256": "aes256",
+ "aes-192": "aes192",
+ "group2": "modp1024",
+ "group5": "modp1536",
+ "group14": "modp2048",
+ "group15": "modp3072",
+ "bi-directional": "start",
+ "response-only": "add",
+ "v2": "insist",
+ "v1": "never"
+ }
+
+ def __init__(self, conf, root_helper, process_id,
+ vpnservice, namespace):
+ self.conf = conf
+ self.id = process_id
+ self.root_helper = root_helper
+ self.vpnservice = vpnservice
+ self.updated_pending_status = False
+ self.namespace = namespace
+ self.connection_status = {}
+ self.config_dir = os.path.join(
+ cfg.CONF.ipsec.config_base_dir, self.id)
+ self.etc_dir = os.path.join(self.config_dir, 'etc')
+ self.translate_dialect()
+
+ def translate_dialect(self):
+ if not self.vpnservice:
+ return
+ for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
+ self._dialect(ipsec_site_conn, 'initiator')
+ self._dialect(ipsec_site_conn['ikepolicy'], 'ike_version')
+ for key in ['encryption_algorithm',
+ 'auth_algorithm',
+ 'pfs']:
+ self._dialect(ipsec_site_conn['ikepolicy'], key)
+ self._dialect(ipsec_site_conn['ipsecpolicy'], key)
+
+ def _dialect(self, obj, key):
+ obj[key] = self.DIALECT_MAP.get(obj[key], obj[key])
+
+ @abc.abstractmethod
+ def ensure_configs(self):
+ pass
+
+ def ensure_config_file(self, kind, template, vpnservice):
+ """Update config file, based on current settings for service."""
+ config_str = self._gen_config_content(template, vpnservice)
+ config_file_name = self._get_config_filename(kind)
+ utils.replace_file(config_file_name, config_str)
+
+ def remove_config(self):
+ """Remove whole config file."""
+ shutil.rmtree(self.config_dir, ignore_errors=True)
+
+ def _get_config_filename(self, kind):
+ config_dir = self.etc_dir
+ return os.path.join(config_dir, kind)
+
+ def _ensure_dir(self, dir_path):
+ if not os.path.isdir(dir_path):
+ os.makedirs(dir_path, 0o755)
+
+ def ensure_config_dir(self, vpnservice):
+ """Create config directory if it does not exist."""
+ self._ensure_dir(self.config_dir)
+ for subdir in self.CONFIG_DIRS:
+ dir_path = os.path.join(self.config_dir, subdir)
+ self._ensure_dir(dir_path)
+
+ def _gen_config_content(self, template_file, vpnservice):
+ template = _get_template(template_file)
+ return template.render(
+ {'vpnservice': vpnservice,
+ 'state_path': cfg.CONF.state_path})
+
+ @abc.abstractmethod
+ def get_status(self):
+ pass
+
+ @property
+ def status(self):
+ if self.active:
+ return constants.ACTIVE
+ return constants.DOWN
+
+ @property
+ def active(self):
+ """Check if the process is active or not."""
+ if not self.namespace:
+ return False
+ try:
+ status = self.get_status()
+ self._update_connection_status(status)
+ except RuntimeError:
+ return False
+ return True
+
+ def update(self):
+ """Update Status based on vpnservice configuration."""
+ if self.vpnservice and not self.vpnservice['admin_state_up']:
+ self.disable()
+ else:
+ self.enable()
+
+ if plugin_utils.in_pending_status(self.vpnservice['status']):
+ self.updated_pending_status = True
+
+ self.vpnservice['status'] = self.status
+ for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
+ if plugin_utils.in_pending_status(ipsec_site_conn['status']):
+ conn_id = ipsec_site_conn['id']
+ conn_status = self.connection_status.get(conn_id)
+ if not conn_status:
+ continue
+ conn_status['updated_pending_status'] = True
+ ipsec_site_conn['status'] = conn_status['status']
+
+ def enable(self):
+ """Enabling the process."""
+ try:
+ self.ensure_configs()
+ if self.active:
+ self.restart()
+ else:
+ self.start()
+ except RuntimeError:
+ LOG.exception(
+ _("Failed to enable vpn process on router %s"),
+ self.id)
+
+ def disable(self):
+ """Disabling the process."""
+ try:
+ if self.active:
+ self.stop()
+ self.remove_config()
+ except RuntimeError:
+ LOG.exception(
+ _("Failed to disable vpn process on router %s"),
+ self.id)
+
+ @abc.abstractmethod
+ def restart(self):
+ """Restart process."""
+
+ @abc.abstractmethod
+ def start(self):
+ """Start process."""
+
+ @abc.abstractmethod
+ def stop(self):
+ """Stop process."""
+
+ def _update_connection_status(self, status_output):
+ for line in status_output.split('\n'):
+ m = re.search('\d\d\d "([a-f0-9\-]+).* (unrouted|erouted);', line)
+ if not m:
+ continue
+ connection_id = m.group(1)
+ status = m.group(2)
+ if not self.connection_status.get(connection_id):
+ self.connection_status[connection_id] = {
+ 'status': None,
+ 'updated_pending_status': False
+ }
+ self.connection_status[
+ connection_id]['status'] = STATUS_MAP[status]
+
+
+class OpenSwanProcess(BaseSwanProcess):
+ """OpenSwan Process manager class.
+
+ This process class uses three commands
+ (1) ipsec pluto: IPsec IKE keying daemon
+ (2) ipsec addconn: Adds new ipsec addconn
+ (3) ipsec whack: control interface for IPSEC keying daemon
+ """
+ def __init__(self, conf, root_helper, process_id,
+ vpnservice, namespace):
+ super(OpenSwanProcess, self).__init__(
+ conf, root_helper, process_id,
+ vpnservice, namespace)
+ self.secrets_file = os.path.join(
+ self.etc_dir, 'ipsec.secrets')
+ self.config_file = os.path.join(
+ self.etc_dir, 'ipsec.conf')
+ self.pid_path = os.path.join(
+ self.config_dir, 'var', 'run', 'pluto')
+
+ def _execute(self, cmd, check_exit_code=True):
+ """Execute command on namespace."""
+ ip_wrapper = ip_lib.IPWrapper(self.root_helper, self.namespace)
+ return ip_wrapper.netns.execute(
+ cmd,
+ check_exit_code=check_exit_code)
+
+ def ensure_configs(self):
+ """Generate config files which are needed for OpenSwan.
+
+ If there is no directory, this function will create
+ dirs.
+ """
+ self.ensure_config_dir(self.vpnservice)
+ self.ensure_config_file(
+ 'ipsec.conf',
+ self.conf.openswan.ipsec_config_template,
+ self.vpnservice)
+ self.ensure_config_file(
+ 'ipsec.secrets',
+ self.conf.openswan.ipsec_secret_template,
+ self.vpnservice)
+
+ def get_status(self):
+ return self._execute([self.binary,
+ 'whack',
+ '--ctlbase',
+ self.pid_path,
+ '--status'])
+
+ def restart(self):
+ """Restart the process."""
+ self.stop()
+ self.start()
+ return
+
+ def _get_nexthop(self, address):
+ routes = self._execute(
+ ['ip', 'route', 'get', address])
+ if routes.find('via') >= 0:
+ return routes.split(' ')[2]
+ return address
+
+ def _virtual_privates(self):
+ """Returns line of virtual_privates.
+
+ virtual_private contains the networks
+ that are allowed as subnet for the remote client.
+ """
+ virtual_privates = []
+ nets = [self.vpnservice['subnet']['cidr']]
+ for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
+ nets += ipsec_site_conn['peer_cidrs']
+ for net in nets:
+ version = netaddr.IPNetwork(net).version
+ virtual_privates.append('%%v%s:%s' % (version, net))
+ return ','.join(virtual_privates)
+
+ def start(self):
+ """Start the process.
+
+ Note: if there is not namespace yet,
+ just do nothing, and wait next event.
+ """
+ if not self.namespace:
+ return
+ virtual_private = self._virtual_privates()
+ #start pluto IKE keying daemon
+ self._execute([self.binary,
+ 'pluto',
+ '--ctlbase', self.pid_path,
+ '--ipsecdir', self.etc_dir,
+ '--use-netkey',
+ '--uniqueids',
+ '--nat_traversal',
+ '--secretsfile', self.secrets_file,
+ '--virtual_private', virtual_private
+ ])
+ #add connections
+ for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
+ nexthop = self._get_nexthop(ipsec_site_conn['peer_address'])
+ self._execute([self.binary,
+ 'addconn',
+ '--ctlbase', '%s.ctl' % self.pid_path,
+ '--defaultroutenexthop', nexthop,
+ '--config', self.config_file,
+ ipsec_site_conn['id']
+ ])
+ #TODO(nati) fix this when openswan is fixed
+ #Due to openswan bug, this command always exit with 3
+ #start whack ipsec keying daemon
+ self._execute([self.binary,
+ 'whack',
+ '--ctlbase', self.pid_path,
+ '--listen',
+ ], check_exit_code=False)
+
+ for ipsec_site_conn in self.vpnservice['ipsec_site_connections']:
+ if not ipsec_site_conn['initiator'] == 'start':
+ continue
+ #initiate ipsec connection
+ self._execute([self.binary,
+ 'whack',
+ '--ctlbase', self.pid_path,
+ '--name', ipsec_site_conn['id'],
+ '--asynchronous',
+ '--initiate'
+ ])
+
+ def disconnect(self):
+ if not self.namespace:
+ return
+ if not self.vpnservice:
+ return
+ for conn_id in self.connection_status:
+ self._execute([self.binary,
+ 'whack',
+ '--ctlbase', self.pid_path,
+ '--name', '%s/0x1' % conn_id,
+ '--terminate'
+ ])
+
+ def stop(self):
+ #Stop process using whack
+ #Note this will also stop pluto
+ self.disconnect()
+ self._execute([self.binary,
+ 'whack',
+ '--ctlbase', self.pid_path,
+ '--shutdown',
+ ])
+
+
+class IPsecVpnDriverApi(proxy.RpcProxy):
+ """IPSecVpnDriver RPC api."""
+ IPSEC_PLUGIN_VERSION = '1.0'
+
+ def get_vpn_services_on_host(self, context, host):
+ """Get list of vpnservices.
+
+ The vpnservices including related ipsec_site_connection,
+ ikepolicy and ipsecpolicy on this host
+ """
+ return self.call(context,
+ self.make_msg('get_vpn_services_on_host',
+ host=host),
+ version=self.IPSEC_PLUGIN_VERSION,
+ topic=self.topic)
+
+ def update_status(self, context, status):
+ """Update local status.
+
+ This method call updates status attribute of
+ VPNServices.
+ """
+ return self.cast(context,
+ self.make_msg('update_status',
+ status=status),
+ version=self.IPSEC_PLUGIN_VERSION,
+ topic=self.topic)
+
+
+class IPsecDriver(device_drivers.DeviceDriver):
+ """VPN Device Driver for IPSec.
+
+ This class is designed for use with L3-agent now.
+ However this driver will be used with another agent in future.
+ so the use of "Router" is kept minimul now.
+ Insted of router_id, we are using process_id in this code.
+ """
+
+ # history
+ # 1.0 Initial version
+
+ RPC_API_VERSION = '1.0'
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, agent, host):
+ self.agent = agent
+ self.conf = self.agent.conf
+ self.root_helper = self.agent.root_helper
+ self.host = host
+ self.conn = rpc.create_connection(new=True)
+ self.context = context.get_admin_context_without_session()
+ self.topic = topics.IPSEC_AGENT_TOPIC
+ node_topic = '%s.%s' % (self.topic, self.host)
+
+ self.processes = {}
+ self.process_status_cache = {}
+
+ self.conn.create_consumer(
+ node_topic,
+ self.create_rpc_dispatcher(),
+ fanout=False)
+ self.conn.consume_in_thread()
+ self.agent_rpc = IPsecVpnDriverApi(topics.IPSEC_DRIVER_TOPIC, '1.0')
+ self.process_status_cache_check = loopingcall.FixedIntervalLoopingCall(
+ self.report_status, self.context)
+ self.process_status_cache_check.start(
+ interval=self.conf.ipsec.ipsec_status_check_interval)
+
+ def create_rpc_dispatcher(self):
+ return q_rpc.PluginRpcDispatcher([self])
+
+ def _update_nat(self, vpnservice, func):
+ """Setting up nat rule in iptables.
+
+ We need to setup nat rule for ipsec packet.
+ :param vpnservice: vpnservices
+ :param func: self.add_nat_rule or self.remove_nat_rule
+ """
+ local_cidr = vpnservice['subnet']['cidr']
+ router_id = vpnservice['router_id']
+ for ipsec_site_connection in vpnservice['ipsec_site_connections']:
+ for peer_cidr in ipsec_site_connection['peer_cidrs']:
+ func(
+ router_id,
+ 'POSTROUTING',
+ '-s %s -d %s -m policy '
+ '--dir out --pol ipsec '
+ '-j ACCEPT ' % (local_cidr, peer_cidr),
+ top=True)
+ self.agent.iptables_apply(router_id)
+
+ def vpnservice_updated(self, context, **kwargs):
+ """Vpnservice updated rpc handler
+
+ VPN Service Driver will call this method
+ when vpnservices updated.
+ Then this method start sync with server.
+ """
+ self.sync(context, [])
+
+ @abc.abstractmethod
+ def create_process(self, process_id, vpnservice, namespace):
+ pass
+
+ def ensure_process(self, process_id, vpnservice=None):
+ """Ensuring process.
+
+ If the process dosen't exist, it will create process
+ and store it in self.processs
+ """
+ process = self.processes.get(process_id)
+ if not process or not process.namespace:
+ namespace = self.agent.get_namespace(process_id)
+ process = self.create_process(
+ process_id,
+ vpnservice,
+ namespace)
+ self.processes[process_id] = process
+ return process
+
+ def create_router(self, process_id):
+ """Handling create router event.
+
+ Agent calls this method, when the process namespace
+ is ready.
+ """
+ if process_id in self.processes:
+ # In case of vpnservice is created
+ # before router's namespace
+ process = self.processes[process_id]
+ self._update_nat(process.vpnservice, self.agent.add_nat_rule)
+ process.enable()
+
+ def destroy_router(self, process_id):
+ """Handling destroy_router event.
+
+ Agent calls this method, when the process namespace
+ is deleted.
+ """
+ if process_id in self.processes:
+ process = self.processes[process_id]
+ process.disable()
+ vpnservice = process.vpnservice
+ if vpnservice:
+ self._update_nat(vpnservice, self.agent.remove_nat_rule)
+ del self.processes[process_id]
+
+ def get_process_status_cache(self, process):
+ if not self.process_status_cache.get(process.id):
+ self.process_status_cache[process.id] = {
+ 'status': None,
+ 'id': process.vpnservice['id'],
+ 'updated_pending_status': False,
+ 'ipsec_site_connections': {}}
+ return self.process_status_cache[process.id]
+
+ def is_status_updated(self, process, previous_status):
+ if process.updated_pending_status:
+ return True
+ if process.status != previous_status['status']:
+ return True
+ if (process.connection_status !=
+ previous_status['ipsec_site_connections']):
+ return True
+
+ def unset_updated_pending_status(self, process):
+ process.updated_pending_status = False
+ for connection_status in process.connection_status.values():
+ connection_status['updated_pending_status'] = False
+
+ def copy_process_status(self, process):
+ return {
+ 'id': process.vpnservice['id'],
+ 'status': process.status,
+ 'updated_pending_status': process.updated_pending_status,
+ 'ipsec_site_connections': copy.deepcopy(process.connection_status)
+ }
+
+ def report_status(self, context):
+ status_changed_vpn_services = []
+ for process in self.processes.values():
+ previous_status = self.get_process_status_cache(process)
+ if self.is_status_updated(process, previous_status):
+ new_status = self.copy_process_status(process)
+ self.process_status_cache[process.id] = new_status
+ status_changed_vpn_services.append(new_status)
+ # We need unset updated_pending status after it
+ # is reported to the server side
+ self.unset_updated_pending_status(process)
+
+ if status_changed_vpn_services:
+ self.agent_rpc.update_status(
+ context,
+ status_changed_vpn_services)
+
+ @lockutils.synchronized('vpn-agent', 'neutron-')
+ def sync(self, context, routers):
+ """Sync status with server side.
+
+ :param context: context object for RPC call
+ :param routers: Router objects which is created in this sync event
+
+ There could be many failure cases should be
+ considered including the followings.
+ 1) Agent class restarted
+ 2) Failure on process creation
+ 3) VpnService is deleted during agent down
+ 4) RPC failure
+
+ In order to handle, these failure cases,
+ This driver takes simple sync strategies.
+ """
+ vpnservices = self.agent_rpc.get_vpn_services_on_host(
+ context, self.host)
+ router_ids = [vpnservice['router_id'] for vpnservice in vpnservices]
+ # Ensure the ipsec process is enabled
+ for vpnservice in vpnservices:
+ process = self.ensure_process(vpnservice['router_id'],
+ vpnservice=vpnservice)
+ self._update_nat(vpnservice, self.agent.add_nat_rule)
+ process.update()
+
+ # Delete any IPSec processes that are
+ # associated with routers, but are not running the VPN service.
+ for router in routers:
+ #We are using router id as process_id
+ process_id = router['id']
+ if process_id not in router_ids:
+ process = self.ensure_process(process_id)
+ self.destroy_router(process_id)
+
+ # Delete any IPSec processes running
+ # VPN that do not have an associated router.
+ process_ids = [process_id
+ for process_id in self.processes
+ if process_id not in router_ids]
+ for process_id in process_ids:
+ self.destroy_router(process_id)
+ self.report_status(context)
+
+
+class OpenSwanDriver(IPsecDriver):
+ def create_process(self, process_id, vpnservice, namespace):
+ return OpenSwanProcess(
+ self.conf,
+ self.root_helper,
+ process_id,
+ vpnservice,
+ namespace)
--- /dev/null
+# Configuration for {{vpnservice.name}}
+config setup
+ nat_traversal=yes
+ listen={{vpnservice.external_ip}}
+conn %default
+ ikelifetime=480m
+ keylife=60m
+ keyingtries=%forever
+{% for ipsec_site_connection in vpnservice.ipsec_site_connections
+%}conn {{ipsec_site_connection.id}}
+ # NOTE: a default route is required for %defaultroute to work...
+ left={{vpnservice.external_ip}}
+ leftid={{vpnservice.external_ip}}
+ auto={{ipsec_site_connection.initiator}}
+ # NOTE:REQUIRED
+ # [subnet]
+ leftsubnet={{vpnservice.subnet.cidr}}
+ # leftsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
+ leftnexthop=%defaultroute
+ ######################
+ # ipsec_site_connections
+ ######################
+ # [peer_address]
+ right={{ipsec_site_connection.peer_address}}
+ # [peer_id]
+ rightid={{ipsec_site_connection.peer_id}}
+ # [peer_cidrs]
+ rightsubnets={ {{ipsec_site_connection['peer_cidrs']|join(' ')}} }
+ # rightsubnet=networkA/netmaskA, networkB/netmaskB (IKEv2 only)
+ rightnexthop=%defaultroute
+ # [mtu]
+ # Note It looks like not supported in the strongswan driver
+ # ignore it now
+ # [dpd_action]
+ dpdaction={{ipsec_site_connection.dpd_action}}
+ # [dpd_interval]
+ dpddelay={{ipsec_site_connection.dpd_interval}}
+ # [dpd_timeout]
+ dpdtimeout={{ipsec_site_connection.dpd_timeout}}
+ # [auth_mode]
+ authby=secret
+ ######################
+ # IKEPolicy params
+ ######################
+ #ike version
+ ikev2={{ipsec_site_connection.ikepolicy.ike_version}}
+ # [encryption_algorithm]-[auth_algorithm]-[pfs]
+ ike={{ipsec_site_connection.ikepolicy.encryption_algorithm}}-{{ipsec_site_connection.ikepolicy.auth_algorithm}};{{ipsec_site_connection.ikepolicy.pfs}}
+ # [lifetime_value]
+ ikelifetime={{ipsec_site_connection.ikepolicy.lifetime_value}}s
+ # NOTE: it looks lifetime_units=kilobytes can't be enforced (could be seconds, hours, days...)
+ ##########################
+ # IPsecPolicys params
+ ##########################
+ # [transform_protocol]
+ auth={{ipsec_site_connection.ipsecpolicy.transform_protocol}}
+ # [encryption_algorithm]-[auth_algorithm]-[pfs]
+ phase2alg={{ipsec_site_connection.ipsecpolicy.encryption_algorithm}}-{{ipsec_site_connection.ipsecpolicy.auth_algorithm}};{{ipsec_site_connection.ipsecpolicy.pfs}}
+ # [encapsulation_mode]
+ type={{ipsec_site_connection.ipsecpolicy.encapsulation_mode}}
+ # [lifetime_value]
+ lifetime={{ipsec_site_connection.ipsecpolicy.lifetime_value}}s
+ # lifebytes=100000 if lifetime_units=kilobytes (IKEv2 only)
+{% endfor %}
--- /dev/null
+# Configuration for {{vpnservice.name}} {% for ipsec_site_connection in vpnservice.ipsec_site_connections %}
+{{vpnservice.external_ip}} {{ipsec_site_connection.peer_id}} : PSK "{{ipsec_site_connection.psk}}"
+{% endfor %}
# @author: Swaminathan Vasudevan, Hewlett-Packard
from neutron.db.vpn import vpn_db
+from neutron.services.vpn.service_drivers import ipsec as ipsec_driver
class VPNPlugin(vpn_db.VPNPluginDb):
vpn_db.VPNPluginDb.
"""
supported_extension_aliases = ["vpnaas"]
+
+
+class VPNDriverPlugin(VPNPlugin, vpn_db.VPNPluginRpcDbMixin):
+ """VpnPlugin which supports VPN Service Drivers."""
+ #TODO(nati) handle ikepolicy and ipsecpolicy update usecase
+ def __init__(self):
+ super(VPNDriverPlugin, self).__init__()
+ self.ipsec_driver = ipsec_driver.IPsecVPNDriver(self)
+
+ def _get_driver_for_vpnservice(self, vpnservice):
+ return self.ipsec_driver
+
+ def _get_driver_for_ipsec_site_connection(self, context,
+ ipsec_site_connection):
+ #TODO(nati) get vpnservice when we support service type framework
+ vpnservice = None
+ return self._get_driver_for_vpnservice(vpnservice)
+
+ def create_ipsec_site_connection(self, context, ipsec_site_connection):
+ ipsec_site_connection = super(
+ VPNDriverPlugin, self).create_ipsec_site_connection(
+ context, ipsec_site_connection)
+ driver = self._get_driver_for_ipsec_site_connection(
+ context, ipsec_site_connection)
+ driver.create_ipsec_site_connection(context, ipsec_site_connection)
+ return ipsec_site_connection
+
+ def delete_ipsec_site_connection(self, context, ipsec_conn_id):
+ ipsec_site_connection = self.get_ipsec_site_connection(
+ context, ipsec_conn_id)
+ super(VPNDriverPlugin, self).delete_ipsec_site_connection(
+ context, ipsec_conn_id)
+ driver = self._get_driver_for_ipsec_site_connection(
+ context, ipsec_site_connection)
+ driver.delete_ipsec_site_connection(context, ipsec_site_connection)
+
+ def update_ipsec_site_connection(
+ self, context,
+ ipsec_conn_id, ipsec_site_connection):
+ old_ipsec_site_connection = self.get_ipsec_site_connection(
+ context, ipsec_conn_id)
+ ipsec_site_connection = super(
+ VPNDriverPlugin, self).update_ipsec_site_connection(
+ context,
+ ipsec_conn_id,
+ ipsec_site_connection)
+ driver = self._get_driver_for_ipsec_site_connection(
+ context, ipsec_site_connection)
+ driver.update_ipsec_site_connection(
+ context, old_ipsec_site_connection, ipsec_site_connection)
+ return ipsec_site_connection
+
+ def delete_vpnservice(self, context, vpnservice_id):
+ vpnservice = self._get_vpnservice(context, vpnservice_id)
+ super(VPNDriverPlugin, self).delete_vpnservice(context, vpnservice_id)
+ driver = self._get_driver_for_vpnservice(vpnservice)
+ driver.delete_vpnservice(context, vpnservice)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 abc
+
+
+class VpnDriver(object):
+ __metaclass__ = abc.ABCMeta
+
+ @property
+ def service_type(self):
+ pass
+
+ @abc.abstractmethod
+ def create_vpnservice(self, context, vpnservice):
+ pass
+
+ @abc.abstractmethod
+ def update_vpnservice(
+ self, context, old_vpnservice, vpnservice):
+ pass
+
+ @abc.abstractmethod
+ def delete_vpnservice(self, context, vpnservice):
+ pass
--- /dev/null
+# vim: tabstop=10 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 netaddr
+
+from neutron.common import rpc as n_rpc
+from neutron import manager
+from neutron.openstack.common import log as logging
+from neutron.openstack.common import rpc
+from neutron.openstack.common.rpc import proxy
+from neutron.services.vpn.common import topics
+from neutron.services.vpn import service_drivers
+
+
+LOG = logging.getLogger(__name__)
+
+IPSEC = 'ipsec'
+BASE_IPSEC_VERSION = '1.0'
+
+
+class IPsecVpnDriverCallBack(object):
+ """Callback for IPSecVpnDriver rpc."""
+
+ # history
+ # 1.0 Initial version
+
+ RPC_API_VERSION = BASE_IPSEC_VERSION
+
+ def __init__(self, driver):
+ self.driver = driver
+
+ def create_rpc_dispatcher(self):
+ return n_rpc.PluginRpcDispatcher([self])
+
+ def get_vpn_services_on_host(self, context, host=None):
+ """Retuns the vpnservices on the host."""
+ plugin = self.driver.service_plugin
+ vpnservices = plugin._get_agent_hosting_vpn_services(
+ context, host)
+ return [self.driver._make_vpnservice_dict(vpnservice)
+ for vpnservice in vpnservices]
+
+ def update_status(self, context, status):
+ """Update status of vpnservices."""
+ plugin = self.driver.service_plugin
+ plugin.update_status_by_agent(context, status)
+
+
+class IPsecVpnAgentApi(proxy.RpcProxy):
+ """Agent RPC API for IPsecVPNAgent."""
+
+ RPC_API_VERSION = BASE_IPSEC_VERSION
+
+ def _agent_notification(self, context, method, router_id,
+ version=None):
+ """Notify update for the agent.
+
+ This method will find where is the router, and
+ dispatch notification for the agent.
+ """
+ adminContext = context.is_admin and context or context.elevated()
+ plugin = manager.NeutronManager.get_plugin()
+ if not version:
+ version = self.RPC_API_VERSION
+ l3_agents = plugin.get_l3_agents_hosting_routers(
+ adminContext, [router_id],
+ admin_state_up=True,
+ active=True)
+ for l3_agent in l3_agents:
+ LOG.debug(_('Notify agent at %(topic)s.%(host)s the message '
+ '%(method)s'),
+ {'topic': topics.IPSEC_AGENT_TOPIC,
+ 'host': l3_agent.host,
+ 'method': method})
+ self.cast(
+ context, self.make_msg(method),
+ version=version,
+ topic='%s.%s' % (topics.IPSEC_AGENT_TOPIC, l3_agent.host))
+
+ def vpnservice_updated(self, context, router_id):
+ """Send update event of vpnservices."""
+ method = 'vpnservice_updated'
+ self._agent_notification(context, method, router_id)
+
+
+class IPsecVPNDriver(service_drivers.VpnDriver):
+ """VPN Service Driver class for IPsec."""
+
+ def __init__(self, service_plugin):
+ self.callbacks = IPsecVpnDriverCallBack(self)
+ self.service_plugin = service_plugin
+ self.conn = rpc.create_connection(new=True)
+ self.conn.create_consumer(
+ topics.IPSEC_DRIVER_TOPIC,
+ self.callbacks.create_rpc_dispatcher(),
+ fanout=False)
+ self.conn.consume_in_thread()
+ self.agent_rpc = IPsecVpnAgentApi(
+ topics.IPSEC_AGENT_TOPIC, BASE_IPSEC_VERSION)
+
+ @property
+ def service_type(self):
+ return IPSEC
+
+ def create_ipsec_site_connection(self, context, ipsec_site_connection):
+ vpnservice = self.service_plugin._get_vpnservice(
+ context, ipsec_site_connection['vpnservice_id'])
+ self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
+
+ def update_ipsec_site_connection(
+ self, context, old_ipsec_site_connection, ipsec_site_connection):
+ vpnservice = self.service_plugin._get_vpnservice(
+ context, ipsec_site_connection['vpnservice_id'])
+ self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
+
+ def delete_ipsec_site_connection(self, context, ipsec_site_connection):
+ vpnservice = self.service_plugin._get_vpnservice(
+ context, ipsec_site_connection['vpnservice_id'])
+ self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
+
+ def create_ikepolicy(self, context, ikepolicy):
+ pass
+
+ def delete_ikepolicy(self, context, ikepolicy):
+ pass
+
+ def update_ikepolicy(self, context, old_ikepolicy, ikepolicy):
+ pass
+
+ def create_ipsecpolicy(self, context, ipsecpolicy):
+ pass
+
+ def delete_ipsecpolicy(self, context, ipsecpolicy):
+ pass
+
+ def update_ipsecpolicy(self, context, old_ipsec_policy, ipsecpolicy):
+ pass
+
+ def create_vpnservice(self, context, vpnservice):
+ pass
+
+ def update_vpnservice(self, context, old_vpnservice, vpnservice):
+ self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
+
+ def delete_vpnservice(self, context, vpnservice):
+ self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'])
+
+ def _make_vpnservice_dict(self, vpnservice):
+ """Convert vpnservice information for vpn agent.
+
+ also converting parameter name for vpn agent driver
+ """
+ vpnservice_dict = dict(vpnservice)
+ vpnservice_dict['ipsec_site_connections'] = []
+ vpnservice_dict['subnet'] = dict(
+ vpnservice.subnet)
+ vpnservice_dict['external_ip'] = vpnservice.router.gw_port[
+ 'fixed_ips'][0]['ip_address']
+ for ipsec_site_connection in vpnservice.ipsec_site_connections:
+ ipsec_site_connection_dict = dict(ipsec_site_connection)
+ try:
+ netaddr.IPAddress(ipsec_site_connection['peer_id'])
+ except netaddr.core.AddrFormatError:
+ ipsec_site_connection['peer_id'] = (
+ '@' + ipsec_site_connection['peer_id'])
+ ipsec_site_connection_dict['ikepolicy'] = dict(
+ ipsec_site_connection.ikepolicy)
+ ipsec_site_connection_dict['ipsecpolicy'] = dict(
+ ipsec_site_connection.ipsecpolicy)
+ vpnservice_dict['ipsec_site_connections'].append(
+ ipsec_site_connection_dict)
+ peer_cidrs = [
+ peer_cidr.cidr
+ for peer_cidr in ipsec_site_connection.peer_cidrs]
+ ipsec_site_connection_dict['peer_cidrs'] = peer_cidrs
+ return vpnservice_dict
_plugin_name = ('neutron.plugins.metaplugin.'
'meta_neutron_plugin.MetaPluginV2')
- def setUp(self, plugin=None, ext_mgr=None):
+ def setUp(self, plugin=None, ext_mgr=None,
+ service_plugins=None):
# NOTE(salv-orlando): The plugin keyword argument is ignored,
# as this class will always invoke super with self._plugin_name.
# These keyword parameters ensure setUp methods always have the
setup_metaplugin_conf()
ext_mgr = ext_mgr or test_l3_plugin.L3TestExtensionManager()
super(MetaPluginV2DBTestCase, self).setUp(
- plugin=self._plugin_name, ext_mgr=ext_mgr)
+ plugin=self._plugin_name, ext_mgr=ext_mgr,
+ service_plugins=service_plugins)
class TestMetaBasicGet(test_plugin.TestBasicGet,
'', kwargs['tenant_id'])
return network_req.get_response(self.api)
- def setUp(self, plugin=None, ext_mgr=None):
+ def setUp(self,
+ plugin=PLUGIN_NAME,
+ ext_mgr=None,
+ service_plugins=None):
test_lib.test_config['config_files'] = [get_fake_conf('nvp.ini.test')]
# mock nvp api client
self.fc = fake_nvpapiclient.FakeClient(STUBS_PATH)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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.
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 mock
+
+from neutron.openstack.common import uuidutils
+from neutron.plugins.common import constants
+from neutron.services.vpn.device_drivers import ipsec as ipsec_driver
+from neutron.tests import base
+
+_uuid = uuidutils.generate_uuid
+FAKE_HOST = 'fake_host'
+FAKE_ROUTER_ID = _uuid()
+FAKE_VPN_SERVICE = {
+ 'id': _uuid(),
+ 'router_id': FAKE_ROUTER_ID,
+ 'admin_state_up': True,
+ 'status': constants.PENDING_CREATE,
+ 'subnet': {'cidr': '10.0.0.0/24'},
+ 'ipsec_site_connections': [
+ {'peer_cidrs': ['20.0.0.0/24',
+ '30.0.0.0/24']},
+ {'peer_cidrs': ['40.0.0.0/24',
+ '50.0.0.0/24']}]
+}
+
+
+class TestIPsecDeviceDriver(base.BaseTestCase):
+ def setUp(self, driver=ipsec_driver.OpenSwanDriver):
+ super(TestIPsecDeviceDriver, self).setUp()
+ self.addCleanup(mock.patch.stopall)
+
+ for klass in [
+ 'os.makedirs',
+ 'os.path.isdir',
+ 'neutron.agent.linux.utils.replace_file',
+ 'neutron.openstack.common.rpc.create_connection',
+ 'neutron.services.vpn.device_drivers.ipsec.'
+ 'OpenSwanProcess._gen_config_content',
+ 'shutil.rmtree',
+ ]:
+ mock.patch(klass).start()
+ self.execute = mock.patch(
+ 'neutron.agent.linux.utils.execute').start()
+ self.agent = mock.Mock()
+ self.driver = driver(
+ self.agent,
+ FAKE_HOST)
+ self.driver.agent_rpc = mock.Mock()
+
+ def test_vpnservice_updated(self):
+ with mock.patch.object(self.driver, 'sync') as sync:
+ context = mock.Mock()
+ self.driver.vpnservice_updated(context)
+ sync.assert_called_once_with(context, [])
+
+ def test_create_router(self):
+ process_id = _uuid()
+ process = mock.Mock()
+ process.vpnservice = FAKE_VPN_SERVICE
+ self.driver.processes = {
+ process_id: process}
+ self.driver.create_router(process_id)
+ process.enable.assert_called_once_with()
+
+ def test_destroy_router(self):
+ process_id = _uuid()
+ process = mock.Mock()
+ process.vpnservice = FAKE_VPN_SERVICE
+ self.driver.processes = {
+ process_id: process}
+ self.driver.destroy_router(process_id)
+ process.disable.assert_called_once_with()
+ self.assertNotIn(process_id, self.driver.processes)
+
+ def test_sync_added(self):
+ self.driver.agent_rpc.get_vpn_services_on_host.return_value = [
+ FAKE_VPN_SERVICE]
+ context = mock.Mock()
+ process = mock.Mock()
+ process.vpnservice = FAKE_VPN_SERVICE
+ process.connection_status = {}
+ process.status = constants.ACTIVE
+ process.updated_pending_status = True
+ self.driver.process_status_cache = {}
+ self.driver.processes = {
+ FAKE_ROUTER_ID: process}
+ self.driver.sync(context, [])
+ self.agent.assert_has_calls([
+ mock.call.add_nat_rule(
+ FAKE_ROUTER_ID,
+ 'POSTROUTING',
+ '-s 10.0.0.0/24 -d 20.0.0.0/24 -m policy '
+ '--dir out --pol ipsec -j ACCEPT ',
+ top=True),
+ mock.call.add_nat_rule(
+ FAKE_ROUTER_ID,
+ 'POSTROUTING',
+ '-s 10.0.0.0/24 -d 30.0.0.0/24 -m policy '
+ '--dir out --pol ipsec -j ACCEPT ',
+ top=True),
+ mock.call.add_nat_rule(
+ FAKE_ROUTER_ID,
+ 'POSTROUTING',
+ '-s 10.0.0.0/24 -d 40.0.0.0/24 -m policy '
+ '--dir out --pol ipsec -j ACCEPT ',
+ top=True),
+ mock.call.add_nat_rule(
+ FAKE_ROUTER_ID,
+ 'POSTROUTING',
+ '-s 10.0.0.0/24 -d 50.0.0.0/24 -m policy '
+ '--dir out --pol ipsec -j ACCEPT ',
+ top=True),
+ mock.call.iptables_apply(FAKE_ROUTER_ID)
+ ])
+ process.update.assert_called_once_with()
+ self.driver.agent_rpc.update_status.assert_called_once_with(
+ context,
+ [{'status': 'ACTIVE',
+ 'ipsec_site_connections': {},
+ 'updated_pending_status': True,
+ 'id': FAKE_VPN_SERVICE['id']}])
+
+ def test_sync_removed(self):
+ self.driver.agent_rpc.get_vpn_services_on_host.return_value = []
+ context = mock.Mock()
+ process_id = _uuid()
+ process = mock.Mock()
+ process.vpnservice = FAKE_VPN_SERVICE
+ self.driver.processes = {
+ process_id: process}
+ self.driver.sync(context, [])
+ process.disable.assert_called_once_with()
+ self.assertNotIn(process_id, self.driver.processes)
+
+ def test_sync_removed_router(self):
+ self.driver.agent_rpc.get_vpn_services_on_host.return_value = []
+ context = mock.Mock()
+ process_id = _uuid()
+ self.driver.sync(context, [{'id': process_id}])
+ self.assertNotIn(process_id, self.driver.processes)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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.
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 mock
+
+from neutron import context
+from neutron.openstack.common import uuidutils
+from neutron.services.vpn.service_drivers import ipsec as ipsec_driver
+from neutron.tests import base
+
+_uuid = uuidutils.generate_uuid
+
+FAKE_VPN_CONNECTION = {
+ 'vpnservice_id': _uuid()
+}
+FAKE_VPN_SERVICE = {
+ 'router_id': _uuid()
+}
+FAKE_HOST = 'fake_host'
+
+
+class TestIPsecDriver(base.BaseTestCase):
+ def setUp(self):
+ super(TestIPsecDriver, self).setUp()
+ self.addCleanup(mock.patch.stopall)
+ mock.patch('neutron.openstack.common.rpc.create_connection').start()
+
+ l3_agent = mock.Mock()
+ l3_agent.host = FAKE_HOST
+ plugin = mock.Mock()
+ plugin.get_l3_agents_hosting_routers.return_value = [l3_agent]
+ plugin_p = mock.patch('neutron.manager.NeutronManager.get_plugin')
+ get_plugin = plugin_p.start()
+ get_plugin.return_value = plugin
+
+ service_plugin = mock.Mock()
+ service_plugin._get_vpnservice.return_value = {
+ 'router_id': _uuid()
+ }
+ self.driver = ipsec_driver.IPsecVPNDriver(service_plugin)
+
+ def _test_update(self, func, args):
+ ctxt = context.Context('', 'somebody')
+ with mock.patch.object(self.driver.agent_rpc, 'cast') as cast:
+ func(ctxt, *args)
+ cast.assert_called_once_with(
+ ctxt,
+ {'args': {},
+ 'namespace': None,
+ 'method': 'vpnservice_updated'},
+ version='1.0',
+ topic='ipsec_agent.fake_host')
+
+ def test_create_ipsec_site_connection(self):
+ self._test_update(self.driver.create_ipsec_site_connection,
+ [FAKE_VPN_CONNECTION])
+
+ def test_update_ipsec_site_connection(self):
+ self._test_update(self.driver.update_ipsec_site_connection,
+ [FAKE_VPN_CONNECTION, FAKE_VPN_CONNECTION])
+
+ def test_delete_ipsec_site_connection(self):
+ self._test_update(self.driver.delete_ipsec_site_connection,
+ [FAKE_VPN_CONNECTION])
+
+ def test_update_vpnservice(self):
+ self._test_update(self.driver.update_vpnservice,
+ [FAKE_VPN_SERVICE, FAKE_VPN_SERVICE])
+
+ def test_delete_vpnservice(self):
+ self._test_update(self.driver.delete_vpnservice,
+ [FAKE_VPN_SERVICE])
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 mock
+from oslo.config import cfg
+
+from neutron.agent.common import config as agent_config
+from neutron.agent import l3_agent
+from neutron.agent.linux import interface
+from neutron.common import config as base_config
+from neutron.openstack.common import uuidutils
+from neutron.services.vpn import agent
+from neutron.services.vpn import device_drivers
+from neutron.tests import base
+
+_uuid = uuidutils.generate_uuid
+NOOP_DEVICE_CLASS = 'NoopDeviceDriver'
+NOOP_DEVICE = ('neutron.tests.unit.services.'
+ 'vpn.test_vpn_agent.%s' % NOOP_DEVICE_CLASS)
+
+
+class NoopDeviceDriver(device_drivers.DeviceDriver):
+ def sync(self, context, processes):
+ pass
+
+ def create_router(self, process_id):
+ pass
+
+ def destroy_router(self, process_id):
+ pass
+
+
+class TestVPNAgent(base.BaseTestCase):
+ def setUp(self):
+ super(TestVPNAgent, self).setUp()
+ self.addCleanup(mock.patch.stopall)
+ self.conf = cfg.CONF
+ self.conf.register_opts(base_config.core_opts)
+ self.conf.register_opts(l3_agent.L3NATAgent.OPTS)
+ self.conf.register_opts(interface.OPTS)
+ agent_config.register_agent_state_opts_helper(self.conf)
+ agent_config.register_root_helper(self.conf)
+
+ self.conf.set_override('interface_driver',
+ 'neutron.agent.linux.interface.NullDriver')
+ self.conf.set_override(
+ 'vpn_device_driver',
+ [NOOP_DEVICE],
+ 'vpnagent')
+
+ for clazz in [
+ 'neutron.agent.linux.ip_lib.device_exists',
+ 'neutron.agent.linux.ip_lib.IPWrapper',
+ 'neutron.agent.linux.interface.NullDriver',
+ 'neutron.agent.linux.utils.execute'
+ ]:
+ mock.patch(clazz).start()
+
+ l3pluginApi_cls = mock.patch(
+ 'neutron.agent.l3_agent.L3PluginApi').start()
+ self.plugin_api = mock.Mock()
+ l3pluginApi_cls.return_value = self.plugin_api
+
+ self.fake_host = 'fake_host'
+ self.agent = agent.VPNAgent(self.fake_host)
+
+ def test_setup_drivers(self):
+ self.assertEqual(1, len(self.agent.devices))
+ device = self.agent.devices[0]
+ self.assertEqual(
+ NOOP_DEVICE_CLASS,
+ device.__class__.__name__
+ )
+
+ def test_get_namespace(self):
+ router_id = _uuid()
+ ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
+ self.conf.use_namespaces, None)
+ self.agent.router_info = {router_id: ri}
+ namespace = self.agent.get_namespace(router_id)
+ self.assertTrue(namespace.endswith(router_id))
+ self.assertFalse(self.agent.get_namespace('fake_id'))
+
+ def test_add_nat_rule(self):
+ router_id = _uuid()
+ ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
+ self.conf.use_namespaces, None)
+ iptables = mock.Mock()
+ ri.iptables_manager.ipv4['nat'] = iptables
+ self.agent.router_info = {router_id: ri}
+ self.agent.add_nat_rule(router_id, 'fake_chain', 'fake_rule', True)
+ iptables.add_rule.assert_called_once_with(
+ 'fake_chain', 'fake_rule', top=True)
+
+ def test_add_nat_rule_with_no_router(self):
+ self.agent.router_info = {}
+ #Should do nothing
+ self.agent.add_nat_rule(
+ 'fake_router_id',
+ 'fake_chain',
+ 'fake_rule',
+ True)
+
+ def test_remove_rule(self):
+ router_id = _uuid()
+ ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
+ self.conf.use_namespaces, None)
+ iptables = mock.Mock()
+ ri.iptables_manager.ipv4['nat'] = iptables
+ self.agent.router_info = {router_id: ri}
+ self.agent.remove_nat_rule(router_id, 'fake_chain', 'fake_rule')
+ iptables.remove_rule.assert_called_once_with(
+ 'fake_chain', 'fake_rule')
+
+ def test_remove_rule_with_no_router(self):
+ self.agent.router_info = {}
+ #Should do nothing
+ self.agent.remove_nat_rule(
+ 'fake_router_id',
+ 'fake_chain',
+ 'fake_rule')
+
+ def test_iptables_apply(self):
+ router_id = _uuid()
+ ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
+ self.conf.use_namespaces, None)
+ iptables = mock.Mock()
+ ri.iptables_manager = iptables
+ self.agent.router_info = {router_id: ri}
+ self.agent.iptables_apply(router_id)
+ iptables.apply.assert_called_once_with()
+
+ def test_iptables_apply_with_no_router(self):
+ #Should do nothing
+ self.agent.router_info = {}
+ self.agent.iptables_apply('fake_router_id')
+
+ def test_router_added(self):
+ mock.patch(
+ 'neutron.agent.linux.iptables_manager.IptablesManager').start()
+ router_id = _uuid()
+ router = {'id': router_id}
+ device = mock.Mock()
+ self.agent.devices = [device]
+ self.agent._router_added(router_id, router)
+ device.create_router.assert_called_once_with(router_id)
+
+ def test_router_removed(self):
+ self.plugin_api.get_external_network_id.return_value = None
+ mock.patch(
+ 'neutron.agent.linux.iptables_manager.IptablesManager').start()
+ router_id = _uuid()
+ ri = l3_agent.RouterInfo(router_id, self.conf.root_helper,
+ self.conf.use_namespaces, None)
+ ri.router = {
+ 'id': _uuid(),
+ 'admin_state_up': True,
+ 'routes': [],
+ 'external_gateway_info': {}}
+ device = mock.Mock()
+ self.agent.router_info = {router_id: ri}
+ self.agent.devices = [device]
+ self.agent._router_removed(router_id)
+ device.destroy_router.assert_called_once_with(router_id)
+
+ def test_process_routers(self):
+ self.plugin_api.get_external_network_id.return_value = None
+ routers = [
+ {'id': _uuid(),
+ 'admin_state_up': True,
+ 'routes': [],
+ 'external_gateway_info': {}}]
+
+ device = mock.Mock()
+ self.agent.devices = [device]
+ self.agent._process_routers(routers, False)
+ device.sync.assert_called_once_with(mock.ANY, routers)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2013, Nachi Ueno, NTT I3, 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 contextlib
+
+import mock
+
+from neutron.common import constants
+from neutron import context
+from neutron import manager
+from neutron.plugins.common import constants as p_constants
+from neutron.services.vpn.service_drivers import ipsec as ipsec_driver
+from neutron.tests.unit.db.vpn import test_db_vpnaas
+from neutron.tests.unit.openvswitch import test_agent_scheduler
+from neutron.tests.unit import test_agent_ext_plugin
+
+FAKE_HOST = test_agent_ext_plugin.L3_HOSTA
+VPN_DRIVER_CLASS = 'neutron.services.vpn.plugin.VPNDriverPlugin'
+
+
+class TestVPNDriverPlugin(test_db_vpnaas.TestVpnaas,
+ test_agent_scheduler.AgentSchedulerTestMixIn,
+ test_agent_ext_plugin.AgentDBTestMixIn):
+ def setUp(self):
+ self.addCleanup(mock.patch.stopall)
+ self.adminContext = context.get_admin_context()
+ driver_cls_p = mock.patch(
+ 'neutron.services.vpn.'
+ 'service_drivers.ipsec.IPsecVPNDriver')
+ driver_cls = driver_cls_p.start()
+ self.driver = mock.Mock()
+ self.driver.service_type = ipsec_driver.IPSEC
+ driver_cls.return_value = self.driver
+ super(TestVPNDriverPlugin, self).setUp(
+ vpnaas_plugin=VPN_DRIVER_CLASS)
+
+ def test_create_ipsec_site_connection(self, **extras):
+ super(TestVPNDriverPlugin, self).test_create_ipsec_site_connection()
+ self.driver.create_ipsec_site_connection.assert_called_once_with(
+ mock.ANY, mock.ANY)
+ self.driver.delete_ipsec_site_connection.assert_called_once_with(
+ mock.ANY, mock.ANY)
+
+ def test_delete_vpnservice(self, **extras):
+ super(TestVPNDriverPlugin, self).test_delete_vpnservice()
+ self.driver.delete_vpnservice.assert_called_once_with(
+ mock.ANY, mock.ANY)
+
+ @contextlib.contextmanager
+ def vpnservice_set(self):
+ """Test case to create a ipsec_site_connection."""
+ vpnservice_name = "vpn1"
+ ipsec_site_connection_name = "ipsec_site_connection"
+ ikename = "ikepolicy1"
+ ipsecname = "ipsecpolicy1"
+ description = "my-vpn-connection"
+ keys = {'name': vpnservice_name,
+ 'description': "my-vpn-connection",
+ 'peer_address': '192.168.1.10',
+ 'peer_id': '192.168.1.10',
+ 'peer_cidrs': ['192.168.2.0/24', '192.168.3.0/24'],
+ 'initiator': 'bi-directional',
+ 'mtu': 1500,
+ 'dpd_action': 'hold',
+ 'dpd_interval': 40,
+ 'dpd_timeout': 120,
+ 'tenant_id': self._tenant_id,
+ 'psk': 'abcd',
+ 'status': 'PENDING_CREATE',
+ 'admin_state_up': True}
+ with self.ikepolicy(name=ikename) as ikepolicy:
+ with self.ipsecpolicy(name=ipsecname) as ipsecpolicy:
+ with self.subnet() as subnet:
+ with self.router() as router:
+ plugin = manager.NeutronManager.get_plugin()
+ agent = {'host': FAKE_HOST,
+ 'agent_type': constants.AGENT_TYPE_L3,
+ 'binary': 'fake-binary',
+ 'topic': 'fake-topic'}
+ plugin.create_or_update_agent(self.adminContext, agent)
+ plugin.schedule_router(
+ self.adminContext, router['router']['id'])
+ with self.vpnservice(name=vpnservice_name,
+ subnet=subnet,
+ router=router) as vpnservice1:
+ keys['ikepolicy_id'] = ikepolicy['ikepolicy']['id']
+ keys['ipsecpolicy_id'] = (
+ ipsecpolicy['ipsecpolicy']['id']
+ )
+ keys['vpnservice_id'] = (
+ vpnservice1['vpnservice']['id']
+ )
+ with self.ipsec_site_connection(
+ self.fmt,
+ ipsec_site_connection_name,
+ keys['peer_address'],
+ keys['peer_id'],
+ keys['peer_cidrs'],
+ keys['mtu'],
+ keys['psk'],
+ keys['initiator'],
+ keys['dpd_action'],
+ keys['dpd_interval'],
+ keys['dpd_timeout'],
+ vpnservice1,
+ ikepolicy,
+ ipsecpolicy,
+ keys['admin_state_up'],
+ description=description,
+ ):
+ yield vpnservice1['vpnservice']
+
+ def test_get_agent_hosting_vpn_services(self):
+ with self.vpnservice_set():
+ service_plugin = manager.NeutronManager.get_service_plugins()[
+ p_constants.VPN]
+ vpnservices = service_plugin._get_agent_hosting_vpn_services(
+ self.adminContext, FAKE_HOST)
+ vpnservices = vpnservices.all()
+ self.assertEqual(1, len(vpnservices))
+ vpnservice_db = vpnservices[0]
+ self.assertEqual(1, len(vpnservice_db.ipsec_site_connections))
+ ipsec_site_connection = vpnservice_db.ipsec_site_connections[0]
+ self.assertIsNotNone(
+ ipsec_site_connection['ikepolicy'])
+ self.assertIsNotNone(
+ ipsec_site_connection['ipsecpolicy'])
+
+ def test_update_status(self):
+ with self.vpnservice_set() as vpnservice:
+ self._register_agent_states()
+ service_plugin = manager.NeutronManager.get_service_plugins()[
+ p_constants.VPN]
+ service_plugin.update_status_by_agent(
+ self.adminContext,
+ [{'status': 'ACTIVE',
+ 'ipsec_site_connections': {},
+ 'updated_pending_status': True,
+ 'id': vpnservice['id']}])
+ vpnservices = service_plugin._get_agent_hosting_vpn_services(
+ self.adminContext, FAKE_HOST)
+ vpnservice_db = vpnservices[0]
+ self.assertEqual(p_constants.ACTIVE, vpnservice_db['status'])
class L3NatTestCaseBase(L3NatTestCaseMixin,
test_db_plugin.NeutronDbPluginV2TestCase):
- def setUp(self, plugin=None, ext_mgr=None):
+ def setUp(self, plugin=None, ext_mgr=None,
+ service_plugins=None):
test_config['plugin_name_v2'] = (
'neutron.tests.unit.test_l3_plugin.TestL3NatPlugin')
# for these tests we need to enable overlapping ips
cfg.CONF.set_default('allow_overlapping_ips', True)
ext_mgr = ext_mgr or L3TestExtensionManager()
super(L3NatTestCaseBase, self).setUp(
- plugin=plugin, ext_mgr=ext_mgr)
+ plugin=plugin, ext_mgr=ext_mgr,
+ service_plugins=service_plugins)
# Set to None to reload the drivers
notifier_api._drivers = None
requests>=1.1
iso8601>=0.1.4
jsonrpclib
+Jinja2
kombu>=2.4.8
netaddr
python-neutronclient>=2.2.3,<3
neutron-usage-audit = neutron.cmd.usage_audit:main
quantum-check-nvp-config = neutron.plugins.nicira.check_nvp_config:main
quantum-db-manage = neutron.db.migration.cli:main
+ neutron-vpn-agent = neutron.services.vpn.agent:main
quantum-debug = neutron.debug.shell:main
quantum-dhcp-agent = neutron.agent.dhcp_agent:main
quantum-hyperv-agent = neutron.plugins.hyperv.agent.hyperv_neutron_agent:main