From 872cab442690f7d2e0083c2f6323addeed208a95 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 26 Jun 2014 16:11:04 +0900 Subject: [PATCH] remove unnecessary neutron files under neutron/services/ Change-Id: I795afc55b72306507ec6717838150fc2fed7a15d --- neutron/services/firewall/__init__.py | 16 - neutron/services/firewall/agents/__init__.py | 16 - .../firewall/agents/firewall_agent_api.py | 85 -- .../firewall/agents/l3reference/__init__.py | 16 - .../agents/l3reference/firewall_l3_agent.py | 295 ----- .../firewall/agents/varmour/__init__.py | 16 - .../firewall/agents/varmour/varmour_api.py | 147 --- .../firewall/agents/varmour/varmour_router.py | 351 ------ .../firewall/agents/varmour/varmour_utils.py | 74 -- neutron/services/firewall/drivers/__init__.py | 16 - .../services/firewall/drivers/fwaas_base.py | 100 -- .../firewall/drivers/linux/__init__.py | 16 - .../firewall/drivers/linux/iptables_fwaas.py | 275 ----- .../firewall/drivers/varmour/__init__.py | 16 - .../firewall/drivers/varmour/varmour_fwaas.py | 207 ---- neutron/services/firewall/fwaas_plugin.py | 299 ----- neutron/services/l3_router/README | 30 - neutron/services/l3_router/__init__.py | 16 - neutron/services/l3_router/l3_apic.py | 135 -- .../services/l3_router/l3_router_plugin.py | 98 -- neutron/services/loadbalancer/__init__.py | 16 - .../services/loadbalancer/agent/__init__.py | 0 neutron/services/loadbalancer/agent/agent.py | 72 -- .../services/loadbalancer/agent/agent_api.py | 100 -- .../loadbalancer/agent/agent_device_driver.py | 98 -- .../loadbalancer/agent/agent_manager.py | 338 ----- .../services/loadbalancer/agent_scheduler.py | 130 -- neutron/services/loadbalancer/constants.py | 47 - .../services/loadbalancer/drivers/__init__.py | 17 - .../loadbalancer/drivers/abstract_driver.py | 130 -- .../loadbalancer/drivers/common/__init__.py | 0 .../drivers/common/agent_driver_base.py | 445 ------- .../loadbalancer/drivers/embrane/README | 9 - .../loadbalancer/drivers/embrane/__init__.py | 0 .../drivers/embrane/agent/__init__.py | 0 .../drivers/embrane/agent/dispatcher.py | 108 -- .../drivers/embrane/agent/lb_operations.py | 179 --- .../loadbalancer/drivers/embrane/config.py | 53 - .../loadbalancer/drivers/embrane/constants.py | 74 -- .../loadbalancer/drivers/embrane/db.py | 56 - .../loadbalancer/drivers/embrane/driver.py | 342 ----- .../loadbalancer/drivers/embrane/models.py | 30 - .../loadbalancer/drivers/embrane/poller.py | 71 -- .../loadbalancer/drivers/haproxy/__init__.py | 17 - .../loadbalancer/drivers/haproxy/cfg.py | 238 ---- .../drivers/haproxy/namespace_driver.py | 396 ------ .../drivers/haproxy/plugin_driver.py | 23 - .../drivers/netscaler/__init__.py | 0 .../drivers/netscaler/ncc_client.py | 182 --- .../drivers/netscaler/netscaler_driver.py | 489 -------- .../loadbalancer/drivers/radware/__init__.py | 17 - .../loadbalancer/drivers/radware/driver.py | 1097 ----------------- .../drivers/radware/exceptions.py | 44 - neutron/services/loadbalancer/plugin.py | 326 ----- neutron/services/metering/__init__.py | 15 - neutron/services/metering/agents/__init__.py | 15 - .../metering/agents/metering_agent.py | 297 ----- neutron/services/metering/drivers/__init__.py | 15 - .../metering/drivers/abstract_driver.py | 51 - .../metering/drivers/iptables/__init__.py | 15 - .../drivers/iptables/iptables_driver.py | 284 ----- .../metering/drivers/noop/__init__.py | 15 - .../metering/drivers/noop/noop_driver.py | 45 - neutron/services/metering/metering_plugin.py | 74 -- neutron/services/provider_configuration.py | 162 --- neutron/services/vpn/__init__.py | 18 - neutron/services/vpn/agent.py | 148 --- neutron/services/vpn/common/__init__.py | 16 - neutron/services/vpn/common/topics.py | 22 - .../services/vpn/device_drivers/__init__.py | 38 - .../device_drivers/cisco_csr_rest_client.py | 258 ---- .../vpn/device_drivers/cisco_ipsec.py | 858 ------------- neutron/services/vpn/device_drivers/ipsec.py | 713 ----------- .../template/openswan/ipsec.conf.template | 64 - .../template/openswan/ipsec.secret.template | 3 - neutron/services/vpn/plugin.py | 107 -- .../services/vpn/service_drivers/__init__.py | 92 -- .../vpn/service_drivers/cisco_csr_db.py | 239 ---- .../vpn/service_drivers/cisco_ipsec.py | 245 ---- neutron/services/vpn/service_drivers/ipsec.py | 156 --- 80 files changed, 11333 deletions(-) delete mode 100644 neutron/services/firewall/__init__.py delete mode 100644 neutron/services/firewall/agents/__init__.py delete mode 100644 neutron/services/firewall/agents/firewall_agent_api.py delete mode 100644 neutron/services/firewall/agents/l3reference/__init__.py delete mode 100644 neutron/services/firewall/agents/l3reference/firewall_l3_agent.py delete mode 100755 neutron/services/firewall/agents/varmour/__init__.py delete mode 100755 neutron/services/firewall/agents/varmour/varmour_api.py delete mode 100755 neutron/services/firewall/agents/varmour/varmour_router.py delete mode 100755 neutron/services/firewall/agents/varmour/varmour_utils.py delete mode 100644 neutron/services/firewall/drivers/__init__.py delete mode 100644 neutron/services/firewall/drivers/fwaas_base.py delete mode 100644 neutron/services/firewall/drivers/linux/__init__.py delete mode 100644 neutron/services/firewall/drivers/linux/iptables_fwaas.py delete mode 100755 neutron/services/firewall/drivers/varmour/__init__.py delete mode 100755 neutron/services/firewall/drivers/varmour/varmour_fwaas.py delete mode 100644 neutron/services/firewall/fwaas_plugin.py delete mode 100644 neutron/services/l3_router/README delete mode 100644 neutron/services/l3_router/__init__.py delete mode 100644 neutron/services/l3_router/l3_apic.py delete mode 100644 neutron/services/l3_router/l3_router_plugin.py delete mode 100644 neutron/services/loadbalancer/__init__.py delete mode 100644 neutron/services/loadbalancer/agent/__init__.py delete mode 100644 neutron/services/loadbalancer/agent/agent.py delete mode 100644 neutron/services/loadbalancer/agent/agent_api.py delete mode 100644 neutron/services/loadbalancer/agent/agent_device_driver.py delete mode 100644 neutron/services/loadbalancer/agent/agent_manager.py delete mode 100644 neutron/services/loadbalancer/agent_scheduler.py delete mode 100644 neutron/services/loadbalancer/constants.py delete mode 100644 neutron/services/loadbalancer/drivers/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/abstract_driver.py delete mode 100644 neutron/services/loadbalancer/drivers/common/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/common/agent_driver_base.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/README delete mode 100644 neutron/services/loadbalancer/drivers/embrane/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/agent/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/agent/dispatcher.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/agent/lb_operations.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/config.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/constants.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/db.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/driver.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/models.py delete mode 100644 neutron/services/loadbalancer/drivers/embrane/poller.py delete mode 100644 neutron/services/loadbalancer/drivers/haproxy/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/haproxy/cfg.py delete mode 100644 neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py delete mode 100644 neutron/services/loadbalancer/drivers/haproxy/plugin_driver.py delete mode 100644 neutron/services/loadbalancer/drivers/netscaler/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/netscaler/ncc_client.py delete mode 100644 neutron/services/loadbalancer/drivers/netscaler/netscaler_driver.py delete mode 100644 neutron/services/loadbalancer/drivers/radware/__init__.py delete mode 100644 neutron/services/loadbalancer/drivers/radware/driver.py delete mode 100644 neutron/services/loadbalancer/drivers/radware/exceptions.py delete mode 100644 neutron/services/loadbalancer/plugin.py delete mode 100644 neutron/services/metering/__init__.py delete mode 100644 neutron/services/metering/agents/__init__.py delete mode 100644 neutron/services/metering/agents/metering_agent.py delete mode 100644 neutron/services/metering/drivers/__init__.py delete mode 100644 neutron/services/metering/drivers/abstract_driver.py delete mode 100644 neutron/services/metering/drivers/iptables/__init__.py delete mode 100644 neutron/services/metering/drivers/iptables/iptables_driver.py delete mode 100644 neutron/services/metering/drivers/noop/__init__.py delete mode 100644 neutron/services/metering/drivers/noop/noop_driver.py delete mode 100644 neutron/services/metering/metering_plugin.py delete mode 100644 neutron/services/provider_configuration.py delete mode 100644 neutron/services/vpn/__init__.py delete mode 100644 neutron/services/vpn/agent.py delete mode 100644 neutron/services/vpn/common/__init__.py delete mode 100644 neutron/services/vpn/common/topics.py delete mode 100644 neutron/services/vpn/device_drivers/__init__.py delete mode 100644 neutron/services/vpn/device_drivers/cisco_csr_rest_client.py delete mode 100644 neutron/services/vpn/device_drivers/cisco_ipsec.py delete mode 100644 neutron/services/vpn/device_drivers/ipsec.py delete mode 100644 neutron/services/vpn/device_drivers/template/openswan/ipsec.conf.template delete mode 100644 neutron/services/vpn/device_drivers/template/openswan/ipsec.secret.template delete mode 100644 neutron/services/vpn/plugin.py delete mode 100644 neutron/services/vpn/service_drivers/__init__.py delete mode 100644 neutron/services/vpn/service_drivers/cisco_csr_db.py delete mode 100644 neutron/services/vpn/service_drivers/cisco_ipsec.py delete mode 100644 neutron/services/vpn/service_drivers/ipsec.py diff --git a/neutron/services/firewall/__init__.py b/neutron/services/firewall/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/firewall/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/agents/__init__.py b/neutron/services/firewall/agents/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/firewall/agents/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/agents/firewall_agent_api.py b/neutron/services/firewall/agents/firewall_agent_api.py deleted file mode 100644 index aad828d41..000000000 --- a/neutron/services/firewall/agents/firewall_agent_api.py +++ /dev/null @@ -1,85 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2013 OpenStack Foundation -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. -# @author: Sridar Kandaswamy, skandasw@cisco.com, Cisco Systems, Inc. -# @author: Dan Florea, dflorea@cisco.com, Cisco Systems, Inc. - -from oslo.config import cfg - -from neutron.common import rpc_compat -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - -FWaaSOpts = [ - cfg.StrOpt( - 'driver', - default='', - help=_("Name of the FWaaS Driver")), - cfg.BoolOpt( - 'enabled', - default=False, - help=_("Enable FWaaS")), -] -cfg.CONF.register_opts(FWaaSOpts, 'fwaas') - - -class FWaaSPluginApiMixin(rpc_compat.RpcProxy): - """Agent side of the FWaaS agent to FWaaS Plugin RPC API.""" - - RPC_API_VERSION = '1.0' - - def __init__(self, topic, host): - super(FWaaSPluginApiMixin, - self).__init__(topic=topic, - default_version=self.RPC_API_VERSION) - self.host = host - - def set_firewall_status(self, context, firewall_id, status): - """Make a RPC to set the status of a firewall.""" - return self.call(context, - self.make_msg('set_firewall_status', host=self.host, - firewall_id=firewall_id, status=status), - topic=self.topic) - - def firewall_deleted(self, context, firewall_id): - """Make a RPC to indicate that the firewall resources are deleted.""" - return self.call(context, - self.make_msg('firewall_deleted', host=self.host, - firewall_id=firewall_id), - topic=self.topic) - - -class FWaaSAgentRpcCallbackMixin(object): - """Mixin for FWaaS agent Implementations.""" - - def __init__(self, host): - - super(FWaaSAgentRpcCallbackMixin, self).__init__(host) - - def create_firewall(self, context, firewall, host): - """Handle RPC cast from plugin to create a firewall.""" - pass - - def update_firewall(self, context, firewall, host): - """Handle RPC cast from plugin to update a firewall.""" - pass - - def delete_firewall(self, context, firewall, host): - """Handle RPC cast from plugin to delete a firewall.""" - pass diff --git a/neutron/services/firewall/agents/l3reference/__init__.py b/neutron/services/firewall/agents/l3reference/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/firewall/agents/l3reference/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/agents/l3reference/firewall_l3_agent.py b/neutron/services/firewall/agents/l3reference/firewall_l3_agent.py deleted file mode 100644 index 865051835..000000000 --- a/neutron/services/firewall/agents/l3reference/firewall_l3_agent.py +++ /dev/null @@ -1,295 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright (c) 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. -# @author: Sridar Kandaswamy, skandasw@cisco.com, Cisco Systems, Inc. -# @author: Dan Florea, dflorea@cisco.com, Cisco Systems, Inc. - -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent.linux import ip_lib -from neutron.common import topics -from neutron import context -from neutron.extensions import firewall as fw_ext -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.firewall.agents import firewall_agent_api as api - -LOG = logging.getLogger(__name__) - - -class FWaaSL3PluginApi(api.FWaaSPluginApiMixin): - """Agent side of the FWaaS agent to FWaaS Plugin RPC API.""" - - def __init__(self, topic, host): - super(FWaaSL3PluginApi, self).__init__(topic, host) - - def get_firewalls_for_tenant(self, context, **kwargs): - """Get the Firewalls with rules from the Plugin to send to driver.""" - LOG.debug(_("Retrieve Firewall with rules from Plugin")) - - return self.call(context, - self.make_msg('get_firewalls_for_tenant', - host=self.host), - topic=self.topic) - - def get_tenants_with_firewalls(self, context, **kwargs): - """Get all Tenants that have Firewalls configured from plugin.""" - LOG.debug(_("Retrieve Tenants with Firewalls configured from Plugin")) - - return self.call(context, - self.make_msg('get_tenants_with_firewalls', - host=self.host), - topic=self.topic) - - -class FWaaSL3AgentRpcCallback(api.FWaaSAgentRpcCallbackMixin): - """FWaaS Agent support to be used by Neutron L3 agent.""" - - def __init__(self, conf): - LOG.debug(_("Initializing firewall agent")) - self.conf = conf - fwaas_driver_class_path = cfg.CONF.fwaas.driver - self.fwaas_enabled = cfg.CONF.fwaas.enabled - if self.fwaas_enabled: - try: - self.fwaas_driver = importutils.import_object( - fwaas_driver_class_path) - LOG.debug(_("FWaaS Driver Loaded: '%s'"), - fwaas_driver_class_path) - except ImportError: - msg = _('Error importing FWaaS device driver: %s') - raise ImportError(msg % fwaas_driver_class_path) - self.services_sync = False - self.root_helper = config.get_root_helper(conf) - # setup RPC to msg fwaas plugin - self.fwplugin_rpc = FWaaSL3PluginApi(topics.FIREWALL_PLUGIN, - conf.host) - super(FWaaSL3AgentRpcCallback, self).__init__(host=conf.host) - - def _get_router_info_list_for_tenant(self, routers, tenant_id): - """Returns the list of router info objects on which to apply the fw.""" - root_ip = ip_lib.IPWrapper(self.root_helper) - # Get the routers for the tenant - router_ids = [ - router['id'] - for router in routers - if router['tenant_id'] == tenant_id] - local_ns_list = root_ip.get_namespaces( - self.root_helper) if self.conf.use_namespaces else [] - - router_info_list = [] - # Pick up namespaces for Tenant Routers - for rid in router_ids: - # for routers without an interface - get_routers returns - # the router - but this is not yet populated in router_info - if rid not in self.router_info: - continue - if self.router_info[rid].use_namespaces: - router_ns = self.router_info[rid].ns_name - if router_ns in local_ns_list: - router_info_list.append(self.router_info[rid]) - else: - router_info_list.append(self.router_info[rid]) - return router_info_list - - def _invoke_driver_for_plugin_api(self, context, fw, func_name): - """Invoke driver method for plugin API and provide status back.""" - LOG.debug(_("%(func_name)s from agent for fw: %(fwid)s"), - {'func_name': func_name, 'fwid': fw['id']}) - try: - routers = self.plugin_rpc.get_routers(context) - router_info_list = self._get_router_info_list_for_tenant( - routers, - fw['tenant_id']) - if not router_info_list: - LOG.debug(_('No Routers on tenant: %s'), fw['tenant_id']) - # fw was created before any routers were added, and if a - # delete is sent then we need to ack so that plugin can - # cleanup. - if func_name == 'delete_firewall': - self.fwplugin_rpc.firewall_deleted(context, fw['id']) - return - LOG.debug(_("Apply fw on Router List: '%s'"), - [ri.router['id'] for ri in router_info_list]) - # call into the driver - try: - self.fwaas_driver.__getattribute__(func_name)( - router_info_list, - fw) - if fw['admin_state_up']: - status = constants.ACTIVE - else: - status = constants.DOWN - except fw_ext.FirewallInternalDriverError: - LOG.error(_("Firewall Driver Error for %(func_name)s " - "for fw: %(fwid)s"), - {'func_name': func_name, 'fwid': fw['id']}) - status = constants.ERROR - # delete needs different handling - if func_name == 'delete_firewall': - if status in [constants.ACTIVE, constants.DOWN]: - self.fwplugin_rpc.firewall_deleted(context, fw['id']) - else: - self.fwplugin_rpc.set_firewall_status( - context, - fw['id'], - status) - except Exception: - LOG.exception( - _("FWaaS RPC failure in %(func_name)s for fw: %(fwid)s"), - {'func_name': func_name, 'fwid': fw['id']}) - self.services_sync = True - return - - def _invoke_driver_for_sync_from_plugin(self, ctx, router_info_list, fw): - """Invoke the delete driver method for status of PENDING_DELETE and - update method for all other status to (re)apply on driver which is - Idempotent. - """ - if fw['status'] == constants.PENDING_DELETE: - try: - self.fwaas_driver.delete_firewall(router_info_list, fw) - self.fwplugin_rpc.firewall_deleted( - ctx, - fw['id']) - except fw_ext.FirewallInternalDriverError: - LOG.error(_("Firewall Driver Error on fw state %(fwmsg)s " - "for fw: %(fwid)s"), - {'fwmsg': fw['status'], 'fwid': fw['id']}) - self.fwplugin_rpc.set_firewall_status( - ctx, - fw['id'], - constants.ERROR) - else: - # PENDING_UPDATE, PENDING_CREATE, ... - try: - self.fwaas_driver.update_firewall(router_info_list, fw) - if fw['admin_state_up']: - status = constants.ACTIVE - else: - status = constants.DOWN - except fw_ext.FirewallInternalDriverError: - LOG.error(_("Firewall Driver Error on fw state %(fwmsg)s " - "for fw: %(fwid)s"), - {'fwmsg': fw['status'], 'fwid': fw['id']}) - status = constants.ERROR - - self.fwplugin_rpc.set_firewall_status( - ctx, - fw['id'], - status) - - def _process_router_add(self, ri): - """On router add, get fw with rules from plugin and update driver.""" - LOG.debug(_("Process router add, router_id: '%s'"), ri.router['id']) - routers = [] - routers.append(ri.router) - router_info_list = self._get_router_info_list_for_tenant( - routers, - ri.router['tenant_id']) - if router_info_list: - # Get the firewall with rules - # for the tenant the router is on. - ctx = context.Context('', ri.router['tenant_id']) - fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx) - LOG.debug(_("Process router add, fw_list: '%s'"), - [fw['id'] for fw in fw_list]) - for fw in fw_list: - self._invoke_driver_for_sync_from_plugin( - ctx, - router_info_list, - fw) - - def process_router_add(self, ri): - """On router add, get fw with rules from plugin and update driver.""" - # avoid msg to plugin when fwaas is not configured - if not self.fwaas_enabled: - return - try: - self._process_router_add(ri) - except Exception: - LOG.exception( - _("FWaaS RPC info call failed for '%s'."), - ri.router['id']) - self.services_sync = True - - def process_services_sync(self, ctx): - """On RPC issues sync with plugin and apply the sync data.""" - # avoid msg to plugin when fwaas is not configured - if not self.fwaas_enabled: - return - try: - # get all routers - routers = self.plugin_rpc.get_routers(ctx) - # get the list of tenants with firewalls configured - # from the plugin - tenant_ids = self.fwplugin_rpc.get_tenants_with_firewalls(ctx) - LOG.debug(_("Tenants with Firewalls: '%s'"), tenant_ids) - for tenant_id in tenant_ids: - ctx = context.Context('', tenant_id) - fw_list = self.fwplugin_rpc.get_firewalls_for_tenant(ctx) - if fw_list: - # if fw present on tenant - router_info_list = self._get_router_info_list_for_tenant( - routers, - tenant_id) - if router_info_list: - LOG.debug(_("Router List: '%s'"), - [ri.router['id'] for ri in router_info_list]) - LOG.debug(_("fw_list: '%s'"), - [fw['id'] for fw in fw_list]) - # apply sync data on fw for this tenant - for fw in fw_list: - # fw, routers present on this host for tenant - # install - LOG.debug(_("Apply fw on Router List: '%s'"), - [ri.router['id'] - for ri in router_info_list]) - # no need to apply sync data for ACTIVE fw - if fw['status'] != constants.ACTIVE: - self._invoke_driver_for_sync_from_plugin( - ctx, - router_info_list, - fw) - self.services_sync = False - except Exception: - LOG.exception(_("Failed fwaas process services sync")) - self.services_sync = True - - def create_firewall(self, context, firewall, host): - """Handle Rpc from plugin to create a firewall.""" - return self._invoke_driver_for_plugin_api( - context, - firewall, - 'create_firewall') - - def update_firewall(self, context, firewall, host): - """Handle Rpc from plugin to update a firewall.""" - return self._invoke_driver_for_plugin_api( - context, - firewall, - 'update_firewall') - - def delete_firewall(self, context, firewall, host): - """Handle Rpc from plugin to delete a firewall.""" - return self._invoke_driver_for_plugin_api( - context, - firewall, - 'delete_firewall') diff --git a/neutron/services/firewall/agents/varmour/__init__.py b/neutron/services/firewall/agents/varmour/__init__.py deleted file mode 100755 index 5e8da711f..000000000 --- a/neutron/services/firewall/agents/varmour/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/agents/varmour/varmour_api.py b/neutron/services/firewall/agents/varmour/varmour_api.py deleted file mode 100755 index 86cb46fac..000000000 --- a/neutron/services/firewall/agents/varmour/varmour_api.py +++ /dev/null @@ -1,147 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 vArmour Networks 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. -# -# @author: Gary Duan, gduan@varmour.com, vArmour Networks - -import base64 - -import httplib2 -from oslo.config import cfg - -from neutron.openstack.common import jsonutils as json -from neutron.openstack.common import log as logging -from neutron.services.firewall.agents.varmour import varmour_utils as va_utils - -OPTS = [ - cfg.StrOpt('director', default='localhost', - help=_("vArmour director ip")), - cfg.StrOpt('director_port', default='443', - help=_("vArmour director port")), - cfg.StrOpt('username', default='varmour', - help=_("vArmour director username")), - cfg.StrOpt('password', default='varmour', secret=True, - help=_("vArmour director password")), ] - -cfg.CONF.register_opts(OPTS, "vArmour") - -LOG = logging.getLogger(__name__) - -REST_URL_PREFIX = '/api/v1.0' - - -class vArmourAPIException(Exception): - message = _("An unknown exception.") - - def __init__(self, **kwargs): - try: - self.err = self.message % kwargs - - except Exception: - self.err = self.message - - def __str__(self): - return self.err - - -class AuthenticationFailure(vArmourAPIException): - message = _("Invalid login credential.") - - -class vArmourRestAPI(object): - - def __init__(self): - LOG.debug(_('vArmourRestAPI: started')) - self.user = cfg.CONF.vArmour.username - self.passwd = cfg.CONF.vArmour.password - self.server = cfg.CONF.vArmour.director - self.port = cfg.CONF.vArmour.director_port - self.timeout = 3 - self.key = '' - - def auth(self): - headers = {} - enc = base64.b64encode(self.user + ':' + self.passwd) - headers['Authorization'] = 'Basic ' + enc - resp = self.rest_api('POST', va_utils.REST_URL_AUTH, None, headers) - if resp and resp['status'] == 200: - self.key = resp['body']['auth'] - return True - else: - raise AuthenticationFailure() - - def commit(self): - self.rest_api('POST', va_utils.REST_URL_COMMIT) - - def rest_api(self, method, url, body=None, headers=None): - url = REST_URL_PREFIX + url - if body: - body_data = json.dumps(body) - else: - body_data = '' - if not headers: - headers = {} - enc = base64.b64encode('%s:%s' % (self.user, self.key)) - headers['Authorization'] = 'Basic ' + enc - - LOG.debug(_("vArmourRestAPI: %(server)s %(port)s"), - {'server': self.server, 'port': self.port}) - - try: - action = "https://" + self.server + ":" + self.port + url - - LOG.debug(_("vArmourRestAPI Sending: " - "%(method)s %(action)s %(headers)s %(body_data)s"), - {'method': method, 'action': action, - 'headers': headers, 'body_data': body_data}) - - h = httplib2.Http(timeout=3, - disable_ssl_certificate_validation=True) - resp, resp_str = h.request(action, method, - body=body_data, - headers=headers) - - LOG.debug(_("vArmourRestAPI Response: %(status)s %(resp_str)s"), - {'status': resp.status, 'resp_str': resp_str}) - - if resp.status == 200: - return {'status': resp.status, - 'reason': resp.reason, - 'body': json.loads(resp_str)} - except Exception: - LOG.error(_('vArmourRestAPI: Could not establish HTTP connection')) - - def del_cfg_objs(self, url, prefix): - resp = self.rest_api('GET', url) - if resp and resp['status'] == 200: - olist = resp['body']['response'] - if not olist: - return - - for o in olist: - if o.startswith(prefix): - self.rest_api('DELETE', url + '/"name:%s"' % o) - self.commit() - - def count_cfg_objs(self, url, prefix): - count = 0 - resp = self.rest_api('GET', url) - if resp and resp['status'] == 200: - for o in resp['body']['response']: - if o.startswith(prefix): - count += 1 - - return count diff --git a/neutron/services/firewall/agents/varmour/varmour_router.py b/neutron/services/firewall/agents/varmour/varmour_router.py deleted file mode 100755 index 952a452b1..000000000 --- a/neutron/services/firewall/agents/varmour/varmour_router.py +++ /dev/null @@ -1,351 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 vArmour Networks 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. -# -# @author: Gary Duan, vArmour Networks Inc. -# - -import sys - -import eventlet -eventlet.monkey_patch() - -import netaddr -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent import l3_agent -from neutron.agent.linux import external_process -from neutron.agent.linux import interface -from neutron.agent.linux import ip_lib -from neutron.common import config as common_config -from neutron.common import constants as l3_constants -from neutron.common import topics -from neutron.openstack.common import log as logging -from neutron.openstack.common import service -from neutron import service as neutron_service -from neutron.services.firewall.agents.l3reference import firewall_l3_agent -from neutron.services.firewall.agents.varmour import varmour_api -from neutron.services.firewall.agents.varmour import varmour_utils as va_utils - - -LOG = logging.getLogger(__name__) - - -class vArmourL3NATAgent(l3_agent.L3NATAgent, - firewall_l3_agent.FWaaSL3AgentRpcCallback): - def __init__(self, host, conf=None): - LOG.debug(_('vArmourL3NATAgent: __init__')) - self.rest = varmour_api.vArmourRestAPI() - super(vArmourL3NATAgent, self).__init__(host, conf) - - def _destroy_router_namespaces(self, only_router_id=None): - return - - def _destroy_router_namespace(self, namespace): - return - - def _create_router_namespace(self, ri): - return - - def _router_added(self, router_id, router): - LOG.debug(_("_router_added: %s"), router_id) - ri = l3_agent.RouterInfo(router_id, self.root_helper, - self.conf.use_namespaces, router) - self.router_info[router_id] = ri - super(vArmourL3NATAgent, self).process_router_add(ri) - - def _router_removed(self, router_id): - LOG.debug(_("_router_removed: %s"), router_id) - - ri = self.router_info[router_id] - if ri: - ri.router['gw_port'] = None - ri.router[l3_constants.INTERFACE_KEY] = [] - ri.router[l3_constants.FLOATINGIP_KEY] = [] - self.process_router(ri) - - name = va_utils.get_snat_rule_name(ri) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_NAT_RULE, name) - - name = va_utils.get_dnat_rule_name(ri) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_NAT_RULE, name) - - name = va_utils.get_trusted_zone_name(ri) - self._va_unset_zone_interfaces(name, True) - - name = va_utils.get_untrusted_zone_name(ri) - self._va_unset_zone_interfaces(name, True) - - del self.router_info[router_id] - - def _spawn_metadata_proxy(self, router_id, ns_name): - return - - def _destroy_metadata_proxy(self, router_id, ns_name): - return - - def _set_subnet_info(self, port): - ips = port['fixed_ips'] - if not ips: - raise Exception(_("Router port %s has no IP address") % port['id']) - return - if len(ips) > 1: - LOG.warn(_("Ignoring multiple IPs on router port %s"), port['id']) - prefixlen = netaddr.IPNetwork(port['subnet']['cidr']).prefixlen - port['ip_cidr'] = "%s/%s" % (ips[0]['ip_address'], prefixlen) - - def _va_unset_zone_interfaces(self, zone_name, remove_zone=False): - # return True if zone exists; otherwise, return False - LOG.debug(_("_va_unset_zone_interfaces: %s"), zone_name) - resp = self.rest.rest_api('GET', va_utils.REST_URL_CONF_ZONE) - if resp and resp['status'] == 200: - zlist = resp['body']['response'] - for zn in zlist: - if zn == zone_name: - commit = False - - if 'interface' in zlist[zn]: - for intf in zlist[zn]['interface']: - self.rest.rest_api('DELETE', - va_utils.REST_URL_CONF + - va_utils.REST_ZONE_NAME % zn + - va_utils.REST_INTF_NAME % intf) - commit = True - if remove_zone: - self.rest.rest_api('DELETE', - va_utils.REST_URL_CONF + - va_utils.REST_ZONE_NAME % zn) - commit = True - - if commit: - self.rest.commit() - - return True - - return False - - def _va_pif_2_lif(self, pif): - return pif + '.0' - - def _va_set_interface_ip(self, pif, cidr): - LOG.debug(_("_va_set_interface_ip: %(pif)s %(cidr)s"), - {'pif': pif, 'cidr': cidr}) - - lif = self._va_pif_2_lif(pif) - obj = va_utils.REST_INTF_NAME % pif + va_utils.REST_LOGIC_NAME % lif - body = { - 'name': lif, - 'family': 'ipv4', - 'address': cidr - } - self.rest.rest_api('PUT', va_utils.REST_URL_CONF + obj, body) - - def _va_get_port_name(self, port_list, name): - if name: - for p in port_list: - if p['VM name'] == name: - return p['name'] - - def _va_config_trusted_zone(self, ri, plist): - zone = va_utils.get_trusted_zone_name(ri) - LOG.debug(_("_va_config_trusted_zone: %s"), zone) - - body = { - 'name': zone, - 'type': 'L3', - 'interface': [] - } - - if not self._va_unset_zone_interfaces(zone): - # if zone doesn't exist, create it - self.rest.rest_api('POST', va_utils.REST_URL_CONF_ZONE, body) - self.rest.commit() - - # add new internal ports to trusted zone - for p in ri.internal_ports: - if p['admin_state_up']: - dev = self.get_internal_device_name(p['id']) - pif = self._va_get_port_name(plist, dev) - if pif: - lif = self._va_pif_2_lif(pif) - if lif not in body['interface']: - body['interface'].append(lif) - - self._va_set_interface_ip(pif, p['ip_cidr']) - - if body['interface']: - self.rest.rest_api('PUT', va_utils.REST_URL_CONF_ZONE, body) - self.rest.commit() - - def _va_config_untrusted_zone(self, ri, plist): - zone = va_utils.get_untrusted_zone_name(ri) - LOG.debug(_("_va_config_untrusted_zone: %s"), zone) - - body = { - 'name': zone, - 'type': 'L3', - 'interface': [] - } - - if not self._va_unset_zone_interfaces(zone): - # if zone doesn't exist, create it - self.rest.rest_api('POST', va_utils.REST_URL_CONF_ZONE, body) - self.rest.commit() - - # add new gateway ports to untrusted zone - if ri.ex_gw_port: - LOG.debug(_("_va_config_untrusted_zone: gw=%r"), ri.ex_gw_port) - dev = self.get_external_device_name(ri.ex_gw_port['id']) - pif = self._va_get_port_name(plist, dev) - if pif: - lif = self._va_pif_2_lif(pif) - - self._va_set_interface_ip(pif, ri.ex_gw_port['ip_cidr']) - - body['interface'].append(lif) - self.rest.rest_api('PUT', va_utils.REST_URL_CONF_ZONE, body) - self.rest.commit() - - def _va_config_router_snat_rules(self, ri, plist): - LOG.debug(_('_va_config_router_snat_rules: %s'), ri.router['id']) - - prefix = va_utils.get_snat_rule_name(ri) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_NAT_RULE, prefix) - - if not ri.enable_snat: - return - - for idx, p in enumerate(ri.internal_ports): - if p['admin_state_up']: - dev = self.get_internal_device_name(p['id']) - pif = self._va_get_port_name(plist, dev) - if pif: - net = netaddr.IPNetwork(p['ip_cidr']) - body = { - 'name': '%s_%d' % (prefix, idx), - 'ingress-context-type': 'interface', - 'ingress-index': self._va_pif_2_lif(pif), - 'source-address': [ - [str(netaddr.IPAddress(net.first + 2)), - str(netaddr.IPAddress(net.last - 1))] - ], - 'flag': 'interface translate-source' - } - self.rest.rest_api('POST', - va_utils.REST_URL_CONF_NAT_RULE, - body) - - if ri.internal_ports: - self.rest.commit() - - def _va_config_floating_ips(self, ri): - LOG.debug(_('_va_config_floating_ips: %s'), ri.router['id']) - - prefix = va_utils.get_dnat_rule_name(ri) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_NAT_RULE, prefix) - - # add new dnat rules - for idx, fip in enumerate(ri.floating_ips): - body = { - 'name': '%s_%d' % (prefix, idx), - 'ingress-context-type': 'zone', - 'ingress-index': va_utils.get_untrusted_zone_name(ri), - 'destination-address': [[fip['floating_ip_address'], - fip['floating_ip_address']]], - 'static': [fip['fixed_ip_address'], fip['fixed_ip_address']], - 'flag': 'translate-destination' - } - self.rest.rest_api('POST', va_utils.REST_URL_CONF_NAT_RULE, body) - - if ri.floating_ips: - self.rest.commit() - - def process_router(self, ri): - LOG.debug(_("process_router: %s"), ri.router['id']) - super(vArmourL3NATAgent, self).process_router(ri) - - self.rest.auth() - - # read internal port name and configuration port name map - resp = self.rest.rest_api('GET', va_utils.REST_URL_INTF_MAP) - if resp and resp['status'] == 200: - try: - plist = resp['body']['response'] - except ValueError: - LOG.warn(_("Unable to parse interface mapping.")) - return - else: - LOG.warn(_("Unable to read interface mapping.")) - return - - if ri.ex_gw_port: - self._set_subnet_info(ri.ex_gw_port) - self._va_config_trusted_zone(ri, plist) - self._va_config_untrusted_zone(ri, plist) - self._va_config_router_snat_rules(ri, plist) - self._va_config_floating_ips(ri) - - def _handle_router_snat_rules(self, ri, ex_gw_port, internal_cidrs, - interface_name, action): - return - - def _send_gratuitous_arp_packet(self, ri, interface_name, ip_address): - return - - def external_gateway_added(self, ri, ex_gw_port, - interface_name, internal_cidrs): - LOG.debug(_("external_gateway_added: %s"), ri.router['id']) - - if not ip_lib.device_exists(interface_name, - root_helper=self.root_helper, - namespace=ri.ns_name): - self.driver.plug(ex_gw_port['network_id'], - ex_gw_port['id'], interface_name, - ex_gw_port['mac_address'], - bridge=self.conf.external_network_bridge, - namespace=ri.ns_name, - prefix=l3_agent.EXTERNAL_DEV_PREFIX) - self.driver.init_l3(interface_name, [ex_gw_port['ip_cidr']], - namespace=ri.ns_name) - - def _update_routing_table(self, ri, operation, route): - return - - -class vArmourL3NATAgentWithStateReport(vArmourL3NATAgent, - l3_agent.L3NATAgentWithStateReport): - pass - - -def main(): - conf = cfg.CONF - conf.register_opts(vArmourL3NATAgent.OPTS) - config.register_interface_driver_opts_helper(conf) - config.register_use_namespaces_opts_helper(conf) - config.register_agent_state_opts_helper(conf) - config.register_root_helper(conf) - conf.register_opts(interface.OPTS) - conf.register_opts(external_process.OPTS) - common_config.init(sys.argv[1:]) - config.setup_logging(conf) - server = neutron_service.Service.create( - binary='neutron-l3-agent', - topic=topics.L3_AGENT, - report_interval=cfg.CONF.AGENT.report_interval, - manager='neutron.services.firewall.agents.varmour.varmour_router.' - 'vArmourL3NATAgentWithStateReport') - service.launch(server).wait() diff --git a/neutron/services/firewall/agents/varmour/varmour_utils.py b/neutron/services/firewall/agents/varmour/varmour_utils.py deleted file mode 100755 index 7290cb6e6..000000000 --- a/neutron/services/firewall/agents/varmour/varmour_utils.py +++ /dev/null @@ -1,74 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 vArmour Networks 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. -# -# @author: Gary Duan, gduan@varmour.com, vArmour Networks - -ROUTER_OBJ_PREFIX = 'r-' -OBJ_PREFIX_LEN = 8 -TRUST_ZONE = '_z_trust' -UNTRUST_ZONE = '_z_untrust' -SNAT_RULE = '_snat' -DNAT_RULE = '_dnat' -ROUTER_POLICY = '_p' - -REST_URL_CONF = '/config' -REST_URL_AUTH = '/auth' -REST_URL_COMMIT = '/commit' -REST_URL_INTF_MAP = '/operation/interface/mapping' - -REST_URL_CONF_NAT_RULE = REST_URL_CONF + '/nat/rule' -REST_URL_CONF_ZONE = REST_URL_CONF + '/zone' -REST_URL_CONF_POLICY = REST_URL_CONF + '/policy' -REST_URL_CONF_ADDR = REST_URL_CONF + '/address' -REST_URL_CONF_SERVICE = REST_URL_CONF + '/service' - -REST_ZONE_NAME = '/zone/"name:%s"' -REST_INTF_NAME = '/interface/"name:%s"' -REST_LOGIC_NAME = '/logical/"name:%s"' -REST_SERVICE_NAME = '/service/"name:%s"/rule' - - -def get_router_object_prefix(ri): - return ROUTER_OBJ_PREFIX + ri.router['id'][:OBJ_PREFIX_LEN] - - -def get_firewall_object_prefix(ri, fw): - return get_router_object_prefix(ri) + '-' + fw['id'][:OBJ_PREFIX_LEN] - - -def get_trusted_zone_name(ri): - return get_router_object_prefix(ri) + TRUST_ZONE - - -def get_untrusted_zone_name(ri): - return get_router_object_prefix(ri) + UNTRUST_ZONE - - -def get_snat_rule_name(ri): - return get_router_object_prefix(ri) + SNAT_RULE - - -def get_dnat_rule_name(ri): - return get_router_object_prefix(ri) + DNAT_RULE - - -def get_router_policy_name(ri): - return get_router_object_prefix(ri) + ROUTER_POLICY - - -def get_firewall_policy_name(ri, fw, rule): - return get_firewall_object_prefix(ri, fw) + rule['id'][:OBJ_PREFIX_LEN] diff --git a/neutron/services/firewall/drivers/__init__.py b/neutron/services/firewall/drivers/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/firewall/drivers/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/drivers/fwaas_base.py b/neutron/services/firewall/drivers/fwaas_base.py deleted file mode 100644 index 0e3fb25a3..000000000 --- a/neutron/services/firewall/drivers/fwaas_base.py +++ /dev/null @@ -1,100 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Dell 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. -# -# @author: Rajesh Mohan, Rajesh_Mohan3@Dell.com, DELL Inc. - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class FwaasDriverBase(object): - """Firewall as a Service Driver base class. - - Using FwaasDriver Class, an instance of L3 perimeter Firewall - can be created. The firewall co-exists with the L3 agent. - - One instance is created for each tenant. One firewall policy - is associated with each tenant (in the Havana release). - - The Firewall can be visualized as having two zones (in Havana - release), trusted and untrusted. - - All the 'internal' interfaces of Neutron Router is treated as trusted. The - interface connected to 'external network' is treated as untrusted. - - The policy is applied on traffic ingressing/egressing interfaces on - the trusted zone. This implies that policy will be applied for traffic - passing from - - trusted to untrusted zones - - untrusted to trusted zones - - trusted to trusted zones - - Policy WILL NOT be applied for traffic from untrusted to untrusted zones. - This is not a problem in Havana release as there is only one interface - connected to external network. - - Since the policy is applied on the internal interfaces, the traffic - will be not be NATed to floating IP. For incoming traffic, the - traffic will get NATed to internal IP address before it hits - the firewall rules. So, while writing the rules, care should be - taken if using rules based on floating IP. - - The firewall rule addition/deletion/insertion/update are done by the - management console. When the policy is sent to the driver, the complete - policy is sent and the whole policy has to be applied atomically. The - firewall rules will not get updated individually. This is to avoid problems - related to out-of-order notifications or inconsistent behaviour by partial - application of rules. - """ - - @abc.abstractmethod - def create_firewall(self, apply_list, firewall): - """Create the Firewall with default (drop all) policy. - - The default policy will be applied on all the interfaces of - trusted zone. - """ - pass - - @abc.abstractmethod - def delete_firewall(self, apply_list, firewall): - """Delete firewall. - - Removes all policies created by this instance and frees up - all the resources. - """ - pass - - @abc.abstractmethod - def update_firewall(self, apply_list, firewall): - """Apply the policy on all trusted interfaces. - - Remove previous policy and apply the new policy on all trusted - interfaces. - """ - pass - - @abc.abstractmethod - def apply_default_policy(self, apply_list, firewall): - """Apply the default policy on all trusted interfaces. - - Remove current policy and apply the default policy on all trusted - interfaces. - """ - pass diff --git a/neutron/services/firewall/drivers/linux/__init__.py b/neutron/services/firewall/drivers/linux/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/firewall/drivers/linux/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/drivers/linux/iptables_fwaas.py b/neutron/services/firewall/drivers/linux/iptables_fwaas.py deleted file mode 100644 index 00a3ed792..000000000 --- a/neutron/services/firewall/drivers/linux/iptables_fwaas.py +++ /dev/null @@ -1,275 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Dell 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. -# -# @author: Rajesh Mohan, Rajesh_Mohan3@Dell.com, DELL Inc. - -from neutron.agent.linux import iptables_manager -from neutron.extensions import firewall as fw_ext -from neutron.openstack.common import log as logging -from neutron.services.firewall.drivers import fwaas_base - -LOG = logging.getLogger(__name__) -FWAAS_DRIVER_NAME = 'Fwaas iptables driver' -FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy' -INGRESS_DIRECTION = 'ingress' -EGRESS_DIRECTION = 'egress' -CHAIN_NAME_PREFIX = {INGRESS_DIRECTION: 'i', - EGRESS_DIRECTION: 'o'} - -""" Firewall rules are applied on internal-interfaces of Neutron router. - The packets ingressing tenant's network will be on the output - direction on internal-interfaces. -""" -IPTABLES_DIR = {INGRESS_DIRECTION: '-o', - EGRESS_DIRECTION: '-i'} -IPV4 = 'ipv4' -IPV6 = 'ipv6' -IP_VER_TAG = {IPV4: 'v4', - IPV6: 'v6'} - - -class IptablesFwaasDriver(fwaas_base.FwaasDriverBase): - """IPTables driver for Firewall As A Service.""" - - def __init__(self): - LOG.debug(_("Initializing fwaas iptables driver")) - - def create_firewall(self, apply_list, firewall): - LOG.debug(_('Creating firewall %(fw_id)s for tenant %(tid)s)'), - {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) - try: - if firewall['admin_state_up']: - self._setup_firewall(apply_list, firewall) - else: - self.apply_default_policy(apply_list, firewall) - except (LookupError, RuntimeError): - # catch known library exceptions and raise Fwaas generic exception - LOG.exception(_("Failed to create firewall: %s"), firewall['id']) - raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) - - def delete_firewall(self, apply_list, firewall): - LOG.debug(_('Deleting firewall %(fw_id)s for tenant %(tid)s)'), - {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) - fwid = firewall['id'] - try: - for router_info in apply_list: - ipt_mgr = router_info.iptables_manager - self._remove_chains(fwid, ipt_mgr) - self._remove_default_chains(ipt_mgr) - # apply the changes immediately (no defer in firewall path) - ipt_mgr.defer_apply_off() - except (LookupError, RuntimeError): - # catch known library exceptions and raise Fwaas generic exception - LOG.exception(_("Failed to delete firewall: %s"), fwid) - raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) - - def update_firewall(self, apply_list, firewall): - LOG.debug(_('Updating firewall %(fw_id)s for tenant %(tid)s)'), - {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) - try: - if firewall['admin_state_up']: - self._setup_firewall(apply_list, firewall) - else: - self.apply_default_policy(apply_list, firewall) - except (LookupError, RuntimeError): - # catch known library exceptions and raise Fwaas generic exception - LOG.exception(_("Failed to update firewall: %s"), firewall['id']) - raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) - - def apply_default_policy(self, apply_list, firewall): - LOG.debug(_('Applying firewall %(fw_id)s for tenant %(tid)s)'), - {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) - fwid = firewall['id'] - try: - for router_info in apply_list: - ipt_mgr = router_info.iptables_manager - - # the following only updates local memory; no hole in FW - self._remove_chains(fwid, ipt_mgr) - self._remove_default_chains(ipt_mgr) - - # create default 'DROP ALL' policy chain - self._add_default_policy_chain_v4v6(ipt_mgr) - self._enable_policy_chain(fwid, ipt_mgr) - - # apply the changes immediately (no defer in firewall path) - ipt_mgr.defer_apply_off() - except (LookupError, RuntimeError): - # catch known library exceptions and raise Fwaas generic exception - LOG.exception(_("Failed to apply default policy on firewall: %s"), - fwid) - raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) - - def _setup_firewall(self, apply_list, firewall): - fwid = firewall['id'] - for router_info in apply_list: - ipt_mgr = router_info.iptables_manager - - # the following only updates local memory; no hole in FW - self._remove_chains(fwid, ipt_mgr) - self._remove_default_chains(ipt_mgr) - - # create default 'DROP ALL' policy chain - self._add_default_policy_chain_v4v6(ipt_mgr) - #create chain based on configured policy - self._setup_chains(firewall, ipt_mgr) - - # apply the changes immediately (no defer in firewall path) - ipt_mgr.defer_apply_off() - - def _get_chain_name(self, fwid, ver, direction): - return '%s%s%s' % (CHAIN_NAME_PREFIX[direction], - IP_VER_TAG[ver], - fwid) - - def _setup_chains(self, firewall, ipt_mgr): - """Create Fwaas chain using the rules in the policy - """ - fw_rules_list = firewall['firewall_rule_list'] - fwid = firewall['id'] - - #default rules for invalid packets and established sessions - invalid_rule = self._drop_invalid_packets_rule() - est_rule = self._allow_established_rule() - - for ver in [IPV4, IPV6]: - if ver == IPV4: - table = ipt_mgr.ipv4['filter'] - else: - table = ipt_mgr.ipv6['filter'] - ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION) - ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION) - for name in [ichain_name, ochain_name]: - table.add_chain(name) - table.add_rule(name, invalid_rule) - table.add_rule(name, est_rule) - - for rule in fw_rules_list: - if not rule['enabled']: - continue - iptbl_rule = self._convert_fwaas_to_iptables_rule(rule) - if rule['ip_version'] == 4: - ver = IPV4 - table = ipt_mgr.ipv4['filter'] - else: - ver = IPV6 - table = ipt_mgr.ipv6['filter'] - ichain_name = self._get_chain_name(fwid, ver, INGRESS_DIRECTION) - ochain_name = self._get_chain_name(fwid, ver, EGRESS_DIRECTION) - table.add_rule(ichain_name, iptbl_rule) - table.add_rule(ochain_name, iptbl_rule) - self._enable_policy_chain(fwid, ipt_mgr) - - def _remove_default_chains(self, nsid): - """Remove fwaas default policy chain.""" - self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid) - self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid) - - def _remove_chains(self, fwid, ipt_mgr): - """Remove fwaas policy chain.""" - for ver in [IPV4, IPV6]: - for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]: - chain_name = self._get_chain_name(fwid, ver, direction) - self._remove_chain_by_name(ver, chain_name, ipt_mgr) - - def _add_default_policy_chain_v4v6(self, ipt_mgr): - ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN) - ipt_mgr.ipv4['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP') - ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN) - ipt_mgr.ipv6['filter'].add_rule(FWAAS_DEFAULT_CHAIN, '-j DROP') - - def _remove_chain_by_name(self, ver, chain_name, ipt_mgr): - if ver == IPV4: - ipt_mgr.ipv4['filter'].ensure_remove_chain(chain_name) - else: - ipt_mgr.ipv6['filter'].ensure_remove_chain(chain_name) - - def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules): - if ver == IPV4: - table = ipt_mgr.ipv4['filter'] - else: - table = ipt_mgr.ipv6['filter'] - for rule in rules: - table.add_rule(chain_name, rule) - - def _enable_policy_chain(self, fwid, ipt_mgr): - bname = iptables_manager.binary_name - - for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']), - (IPV6, ipt_mgr.ipv6['filter'])]: - for direction in [INGRESS_DIRECTION, EGRESS_DIRECTION]: - chain_name = self._get_chain_name(fwid, ver, direction) - chain_name = iptables_manager.get_chain_name(chain_name) - if chain_name in tbl.chains: - jump_rule = ['%s qr-+ -j %s-%s' % (IPTABLES_DIR[direction], - bname, chain_name)] - self._add_rules_to_chain(ipt_mgr, ver, 'FORWARD', - jump_rule) - - #jump to DROP_ALL policy - chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN) - jump_rule = ['-o qr-+ -j %s-%s' % (bname, chain_name)] - self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule) - self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule) - - #jump to DROP_ALL policy - chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN) - jump_rule = ['-i qr-+ -j %s-%s' % (bname, chain_name)] - self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule) - self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule) - - def _convert_fwaas_to_iptables_rule(self, rule): - action = rule.get('action') == 'allow' and 'ACCEPT' or 'DROP' - args = [self._protocol_arg(rule.get('protocol')), - self._port_arg('dport', - rule.get('protocol'), - rule.get('destination_port')), - self._port_arg('sport', - rule.get('protocol'), - rule.get('source_port')), - self._ip_prefix_arg('s', rule.get('source_ip_address')), - self._ip_prefix_arg('d', rule.get('destination_ip_address')), - self._action_arg(action)] - - iptables_rule = ' '.join(args) - return iptables_rule - - def _drop_invalid_packets_rule(self): - return '-m state --state INVALID -j DROP' - - def _allow_established_rule(self): - return '-m state --state ESTABLISHED,RELATED -j ACCEPT' - - def _action_arg(self, action): - if action: - return '-j %s' % action - return '' - - def _protocol_arg(self, protocol): - if protocol: - return '-p %s' % protocol - return '' - - def _port_arg(self, direction, protocol, port): - if not (protocol in ['udp', 'tcp'] and port): - return '' - return '--%s %s' % (direction, port) - - def _ip_prefix_arg(self, direction, ip_prefix): - if ip_prefix: - return '-%s %s' % (direction, ip_prefix) - return '' diff --git a/neutron/services/firewall/drivers/varmour/__init__.py b/neutron/services/firewall/drivers/varmour/__init__.py deleted file mode 100755 index 5e8da711f..000000000 --- a/neutron/services/firewall/drivers/varmour/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/firewall/drivers/varmour/varmour_fwaas.py b/neutron/services/firewall/drivers/varmour/varmour_fwaas.py deleted file mode 100755 index bcdf2f909..000000000 --- a/neutron/services/firewall/drivers/varmour/varmour_fwaas.py +++ /dev/null @@ -1,207 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 vArmour Networks 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. -# -# @author: Gary Duan, gduan@varmour.com, vArmour Networks - -from neutron.openstack.common import log as logging -from neutron.services.firewall.agents.varmour import varmour_api -from neutron.services.firewall.agents.varmour import varmour_utils as va_utils -from neutron.services.firewall.drivers import fwaas_base - -LOG = logging.getLogger(__name__) - - -class vArmourFwaasDriver(fwaas_base.FwaasDriverBase): - def __init__(self): - LOG.debug(_("Initializing fwaas vArmour driver")) - - self.rest = varmour_api.vArmourRestAPI() - - def create_firewall(self, apply_list, firewall): - LOG.debug(_('create_firewall (%s)'), firewall['id']) - - return self.update_firewall(apply_list, firewall) - - def update_firewall(self, apply_list, firewall): - LOG.debug(_("update_firewall (%s)"), firewall['id']) - - if firewall['admin_state_up']: - return self._update_firewall(apply_list, firewall) - else: - return self.apply_default_policy(apply_list, firewall) - - def delete_firewall(self, apply_list, firewall): - LOG.debug(_("delete_firewall (%s)"), firewall['id']) - - return self.apply_default_policy(apply_list, firewall) - - def apply_default_policy(self, apply_list, firewall): - LOG.debug(_("apply_default_policy (%s)"), firewall['id']) - - self.rest.auth() - - for ri in apply_list: - self._clear_policy(ri, firewall) - - return True - - def _update_firewall(self, apply_list, firewall): - LOG.debug(_("Updating firewall (%s)"), firewall['id']) - - self.rest.auth() - - for ri in apply_list: - self._clear_policy(ri, firewall) - self._setup_policy(ri, firewall) - - return True - - def _setup_policy(self, ri, fw): - # create zones no matter if they exist. Interfaces are added by router - body = { - 'type': 'L3', - 'interface': [] - } - - body['name'] = va_utils.get_trusted_zone_name(ri) - self.rest.rest_api('POST', va_utils.REST_URL_CONF_ZONE, body) - body['name'] = va_utils.get_untrusted_zone_name(ri) - self.rest.rest_api('POST', va_utils.REST_URL_CONF_ZONE, body) - self.rest.commit() - - servs = dict() - addrs = dict() - for rule in fw['firewall_rule_list']: - if not rule['enabled']: - continue - - if rule['ip_version'] == 4: - service = self._make_service(ri, fw, rule, servs) - s_addr = self._make_address(ri, fw, rule, addrs, True) - d_addr = self._make_address(ri, fw, rule, addrs, False) - - policy = va_utils.get_firewall_policy_name(ri, fw, rule) - z0 = va_utils.get_trusted_zone_name(ri) - z1 = va_utils.get_untrusted_zone_name(ri) - body = self._make_policy(policy + '_0', rule, - z0, z0, s_addr, d_addr, service) - self.rest.rest_api('POST', va_utils.REST_URL_CONF_POLICY, body) - body = self._make_policy(policy + '_1', rule, - z0, z1, s_addr, d_addr, service) - self.rest.rest_api('POST', va_utils.REST_URL_CONF_POLICY, body) - body = self._make_policy(policy + '_2', rule, - z1, z0, s_addr, d_addr, service) - self.rest.rest_api('POST', va_utils.REST_URL_CONF_POLICY, body) - - self.rest.commit() - else: - LOG.warn(_("Unsupported IP version rule.")) - - def _clear_policy(self, ri, fw): - prefix = va_utils.get_firewall_object_prefix(ri, fw) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_POLICY, prefix) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_ADDR, prefix) - self.rest.del_cfg_objs(va_utils.REST_URL_CONF_SERVICE, prefix) - - def _make_service(self, ri, fw, rule, servs): - prefix = va_utils.get_firewall_object_prefix(ri, fw) - - if rule.get('protocol'): - key = rule.get('protocol') - if rule.get('source_port'): - key += '-' + rule.get('source_port') - if rule.get('destination_port'): - key += '-' + rule.get('destination_port') - else: - return - - if key in servs: - name = '%s_%d' % (prefix, servs[key]) - else: - # create new service object with index - idx = len(servs) - servs[key] = idx - name = '%s_%d' % (prefix, idx) - - body = {'name': name} - self.rest.rest_api('POST', - va_utils.REST_URL_CONF_SERVICE, - body) - body = self._make_service_rule(rule) - self.rest.rest_api('POST', - va_utils.REST_URL_CONF + - va_utils.REST_SERVICE_NAME % name, - body) - self.rest.commit() - - return name - - def _make_service_rule(self, rule): - body = { - 'name': '1', - 'protocol': rule.get('protocol') - } - if 'source_port' in rule: - body['source-start'] = rule['source_port'] - body['source-end'] = rule['source_port'] - if 'destination_port' in rule: - body['dest-start'] = rule['destination_port'] - body['dest-end'] = rule['destination_port'] - - return body - - def _make_address(self, ri, fw, rule, addrs, is_src): - prefix = va_utils.get_firewall_object_prefix(ri, fw) - - if is_src: - key = rule.get('source_ip_address') - else: - key = rule.get('destination_ip_address') - - if not key: - return - - if key in addrs: - name = '%s_%d' % (prefix, addrs[key]) - else: - # create new address object with idx - idx = len(addrs) - addrs[key] = idx - name = '%s_%d' % (prefix, idx) - - body = { - 'name': name, - 'type': 'ipv4', - 'ipv4': key - } - self.rest.rest_api('POST', va_utils.REST_URL_CONF_ADDR, body) - self.rest.commit() - - return name - - def _make_policy(self, name, rule, zone0, zone1, s_addr, d_addr, service): - body = { - 'name': name, - 'action': 'permit' if rule.get('action') == 'allow' else 'deny', - 'from': zone0, - 'to': zone1, - 'match-source-address': [s_addr or 'Any'], - 'match-dest-address': [d_addr or 'Any'], - 'match-service': [service or 'Any'] - } - - return body diff --git a/neutron/services/firewall/fwaas_plugin.py b/neutron/services/firewall/fwaas_plugin.py deleted file mode 100644 index f0eaf1b2c..000000000 --- a/neutron/services/firewall/fwaas_plugin.py +++ /dev/null @@ -1,299 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Big Switch Networks, 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. -# -# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com, Big Switch Networks, Inc. - -from oslo.config import cfg - -from neutron.common import exceptions as n_exception -from neutron.common import rpc_compat -from neutron.common import topics -from neutron import context as neutron_context -from neutron.db import api as qdbapi -from neutron.db.firewall import firewall_db -from neutron.extensions import firewall as fw_ext -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as const - - -LOG = logging.getLogger(__name__) - - -class FirewallCallbacks(rpc_compat.RpcCallback): - RPC_API_VERSION = '1.0' - - def __init__(self, plugin): - super(FirewallCallbacks, self).__init__() - self.plugin = plugin - - def set_firewall_status(self, context, firewall_id, status, **kwargs): - """Agent uses this to set a firewall's status.""" - LOG.debug(_("set_firewall_status() called")) - with context.session.begin(subtransactions=True): - fw_db = self.plugin._get_firewall(context, firewall_id) - # ignore changing status if firewall expects to be deleted - # That case means that while some pending operation has been - # performed on the backend, neutron server received delete request - # and changed firewall status to const.PENDING_DELETE - if fw_db.status == const.PENDING_DELETE: - LOG.debug(_("Firewall %(fw_id)s in PENDING_DELETE state, " - "not changing to %(status)s"), - {'fw_id': firewall_id, 'status': status}) - return False - #TODO(xuhanp): Remove INACTIVE status and use DOWN to - # be consistent with other network resources - if status in (const.ACTIVE, const.INACTIVE, const.DOWN): - fw_db.status = status - return True - else: - fw_db.status = const.ERROR - return False - - def firewall_deleted(self, context, firewall_id, **kwargs): - """Agent uses this to indicate firewall is deleted.""" - LOG.debug(_("firewall_deleted() called")) - with context.session.begin(subtransactions=True): - fw_db = self.plugin._get_firewall(context, firewall_id) - # allow to delete firewalls in ERROR state - if fw_db.status in (const.PENDING_DELETE, const.ERROR): - self.plugin.delete_db_firewall_object(context, firewall_id) - return True - else: - LOG.warn(_('Firewall %(fw)s unexpectedly deleted by agent, ' - 'status was %(status)s'), - {'fw': firewall_id, 'status': fw_db.status}) - fw_db.status = const.ERROR - return False - - def get_firewalls_for_tenant(self, context, **kwargs): - """Agent uses this to get all firewalls and rules for a tenant.""" - LOG.debug(_("get_firewalls_for_tenant() called")) - fw_list = [ - self.plugin._make_firewall_dict_with_rules(context, fw['id']) - for fw in self.plugin.get_firewalls(context) - ] - return fw_list - - def get_firewalls_for_tenant_without_rules(self, context, **kwargs): - """Agent uses this to get all firewalls for a tenant.""" - LOG.debug(_("get_firewalls_for_tenant_without_rules() called")) - fw_list = [fw for fw in self.plugin.get_firewalls(context)] - return fw_list - - def get_tenants_with_firewalls(self, context, **kwargs): - """Agent uses this to get all tenants that have firewalls.""" - LOG.debug(_("get_tenants_with_firewalls() called")) - ctx = neutron_context.get_admin_context() - fw_list = self.plugin.get_firewalls(ctx) - fw_tenant_list = list(set(fw['tenant_id'] for fw in fw_list)) - return fw_tenant_list - - -class FirewallAgentApi(rpc_compat.RpcProxy): - """Plugin side of plugin to agent RPC API.""" - - API_VERSION = '1.0' - - def __init__(self, topic, host): - super(FirewallAgentApi, self).__init__(topic, self.API_VERSION) - self.host = host - - def create_firewall(self, context, firewall): - return self.fanout_cast( - context, - self.make_msg('create_firewall', firewall=firewall, - host=self.host), - topic=self.topic - ) - - def update_firewall(self, context, firewall): - return self.fanout_cast( - context, - self.make_msg('update_firewall', firewall=firewall, - host=self.host), - topic=self.topic - ) - - def delete_firewall(self, context, firewall): - return self.fanout_cast( - context, - self.make_msg('delete_firewall', firewall=firewall, - host=self.host), - topic=self.topic - ) - - -class FirewallCountExceeded(n_exception.Conflict): - - """Reference implementation specific exception for firewall count. - - Only one firewall is supported per tenant. When a second - firewall is tried to be created, this exception will be raised. - """ - message = _("Exceeded allowed count of firewalls for tenant " - "%(tenant_id)s. Only one firewall is supported per tenant.") - - -class FirewallPlugin(firewall_db.Firewall_db_mixin): - - """Implementation of the Neutron Firewall Service Plugin. - - This class manages the workflow of FWaaS request/response. - Most DB related works are implemented in class - firewall_db.Firewall_db_mixin. - """ - supported_extension_aliases = ["fwaas"] - - def __init__(self): - """Do the initialization for the firewall service plugin here.""" - qdbapi.register_models() - - self.endpoints = [FirewallCallbacks(self)] - - self.conn = rpc_compat.create_connection(new=True) - self.conn.create_consumer( - topics.FIREWALL_PLUGIN, self.endpoints, fanout=False) - self.conn.consume_in_threads() - - self.agent_rpc = FirewallAgentApi( - topics.L3_AGENT, - cfg.CONF.host - ) - - def _make_firewall_dict_with_rules(self, context, firewall_id): - firewall = self.get_firewall(context, firewall_id) - fw_policy_id = firewall['firewall_policy_id'] - if fw_policy_id: - fw_policy = self.get_firewall_policy(context, fw_policy_id) - fw_rules_list = [self.get_firewall_rule( - context, rule_id) for rule_id in fw_policy['firewall_rules']] - firewall['firewall_rule_list'] = fw_rules_list - else: - firewall['firewall_rule_list'] = [] - # FIXME(Sumit): If the size of the firewall object we are creating - # here exceeds the largest message size supported by rabbit/qpid - # then we will have a problem. - return firewall - - def _rpc_update_firewall(self, context, firewall_id): - status_update = {"firewall": {"status": const.PENDING_UPDATE}} - fw = super(FirewallPlugin, self).update_firewall(context, firewall_id, - status_update) - if fw: - fw_with_rules = ( - self._make_firewall_dict_with_rules(context, - firewall_id)) - self.agent_rpc.update_firewall(context, fw_with_rules) - - def _rpc_update_firewall_policy(self, context, firewall_policy_id): - firewall_policy = self.get_firewall_policy(context, firewall_policy_id) - if firewall_policy: - for firewall_id in firewall_policy['firewall_list']: - self._rpc_update_firewall(context, firewall_id) - - def _ensure_update_firewall(self, context, firewall_id): - fwall = self.get_firewall(context, firewall_id) - if fwall['status'] in [const.PENDING_CREATE, - const.PENDING_UPDATE, - const.PENDING_DELETE]: - raise fw_ext.FirewallInPendingState(firewall_id=firewall_id, - pending_state=fwall['status']) - - def _ensure_update_firewall_policy(self, context, firewall_policy_id): - firewall_policy = self.get_firewall_policy(context, firewall_policy_id) - if firewall_policy and 'firewall_list' in firewall_policy: - for firewall_id in firewall_policy['firewall_list']: - self._ensure_update_firewall(context, firewall_id) - - def _ensure_update_firewall_rule(self, context, firewall_rule_id): - fw_rule = self.get_firewall_rule(context, firewall_rule_id) - if 'firewall_policy_id' in fw_rule and fw_rule['firewall_policy_id']: - self._ensure_update_firewall_policy(context, - fw_rule['firewall_policy_id']) - - def create_firewall(self, context, firewall): - LOG.debug(_("create_firewall() called")) - tenant_id = self._get_tenant_id_for_create(context, - firewall['firewall']) - fw_count = self.get_firewalls_count(context, - filters={'tenant_id': [tenant_id]}) - if fw_count: - raise FirewallCountExceeded(tenant_id=tenant_id) - firewall['firewall']['status'] = const.PENDING_CREATE - fw = super(FirewallPlugin, self).create_firewall(context, firewall) - fw_with_rules = ( - self._make_firewall_dict_with_rules(context, fw['id'])) - self.agent_rpc.create_firewall(context, fw_with_rules) - return fw - - def update_firewall(self, context, id, firewall): - LOG.debug(_("update_firewall() called")) - self._ensure_update_firewall(context, id) - firewall['firewall']['status'] = const.PENDING_UPDATE - fw = super(FirewallPlugin, self).update_firewall(context, id, firewall) - fw_with_rules = ( - self._make_firewall_dict_with_rules(context, fw['id'])) - self.agent_rpc.update_firewall(context, fw_with_rules) - return fw - - def delete_db_firewall_object(self, context, id): - firewall = self.get_firewall(context, id) - if firewall['status'] in [const.PENDING_DELETE]: - super(FirewallPlugin, self).delete_firewall(context, id) - - def delete_firewall(self, context, id): - LOG.debug(_("delete_firewall() called")) - status_update = {"firewall": {"status": const.PENDING_DELETE}} - fw = super(FirewallPlugin, self).update_firewall(context, id, - status_update) - fw_with_rules = ( - self._make_firewall_dict_with_rules(context, fw['id'])) - self.agent_rpc.delete_firewall(context, fw_with_rules) - - def update_firewall_policy(self, context, id, firewall_policy): - LOG.debug(_("update_firewall_policy() called")) - self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPlugin, - self).update_firewall_policy(context, id, firewall_policy) - self._rpc_update_firewall_policy(context, id) - return fwp - - def update_firewall_rule(self, context, id, firewall_rule): - LOG.debug(_("update_firewall_rule() called")) - self._ensure_update_firewall_rule(context, id) - fwr = super(FirewallPlugin, - self).update_firewall_rule(context, id, firewall_rule) - firewall_policy_id = fwr['firewall_policy_id'] - if firewall_policy_id: - self._rpc_update_firewall_policy(context, firewall_policy_id) - return fwr - - def insert_rule(self, context, id, rule_info): - LOG.debug(_("insert_rule() called")) - self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPlugin, - self).insert_rule(context, id, rule_info) - self._rpc_update_firewall_policy(context, id) - return fwp - - def remove_rule(self, context, id, rule_info): - LOG.debug(_("remove_rule() called")) - self._ensure_update_firewall_policy(context, id) - fwp = super(FirewallPlugin, - self).remove_rule(context, id, rule_info) - self._rpc_update_firewall_policy(context, id) - return fwp diff --git a/neutron/services/l3_router/README b/neutron/services/l3_router/README deleted file mode 100644 index f6ca35bed..000000000 --- a/neutron/services/l3_router/README +++ /dev/null @@ -1,30 +0,0 @@ -This service plugin implements the L3 routing functionality (resources router -and floatingip) that in earlier releases before Havana was provided by core -plugins (openvswitch, linuxbridge, ... etc). - -Core plugins can now choose not to implement L3 routing functionality and -instead delegate that to the L3 routing service plugin. - -The required changes to a core plugin are in that case: -- Do not inherit 'l3_db.L3_NAT_db_mixin' (or its descendants like extraroute) - anymore. -- Remove "router" from 'supported_extension_aliases'. -- Modify any 'self' references to members in L3_NAT_db_mixin to instead use - 'manager.NeutronManager.get_service_plugins().get(constants.L3_ROUTER_NAT)' - For example, - self.prevent_l3_port_deletion(...) - becomes something like - plugin = manager.NeutronManager.get_service_plugins().get( - constants.L3_ROUTER_NAT) - if plugin: - plugin.prevent_l3_port_deletion(...) - -If the core plugin has relied on the L3Agent the following must also be changed: -- Do not inherit 'l3_rpc_base.L3RpcCallbackMixin' in any '*RpcCallbacks' class. -- Do not be a consumer of the topics.L3PLUGIN topic for RPC. - -To use the L3 routing service plugin, add -'neutron.services.l3_router.l3_router_plugin.L3RouterPlugin' -to 'service_plugins' in '/etc/neutron/neutron.conf'. -That is, -service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin diff --git a/neutron/services/l3_router/__init__.py b/neutron/services/l3_router/__init__.py deleted file mode 100644 index 7506a2914..000000000 --- a/neutron/services/l3_router/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/l3_router/l3_apic.py b/neutron/services/l3_router/l3_apic.py deleted file mode 100644 index 02198e8dc..000000000 --- a/neutron/services/l3_router/l3_apic.py +++ /dev/null @@ -1,135 +0,0 @@ -# Copyright (c) 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. -# -# @author: Arvind Somya (asomya@cisco.com), Cisco Systems Inc. - -from neutron.db import api as qdbapi -from neutron.db import db_base_plugin_v2 -from neutron.db import extraroute_db -from neutron.db import l3_gwmode_db -from neutron.db import model_base -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.plugins.ml2.drivers.cisco.apic import apic_manager - -LOG = logging.getLogger(__name__) - - -class ApicL3ServicePlugin(db_base_plugin_v2.NeutronDbPluginV2, - db_base_plugin_v2.CommonDbMixin, - extraroute_db.ExtraRoute_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin): - """Implementation of the APIC L3 Router Service Plugin. - - This class implements a L3 service plugin that provides - internal gateway functionality for the Cisco APIC (Application - Policy Infrastructure Controller). - """ - supported_extension_aliases = ["router", "ext-gw-mode", "extraroute"] - - def __init__(self): - super(ApicL3ServicePlugin, self).__init__() - qdbapi.register_models(base=model_base.BASEV2) - self.manager = apic_manager.APICManager() - - @staticmethod - def get_plugin_type(): - return constants.L3_ROUTER_NAT - - @staticmethod - def get_plugin_description(): - """Returns string description of the plugin.""" - return _("L3 Router Service Plugin for basic L3 using the APIC") - - def _add_epg_to_contract(self, tenant_id, epg, contract): - """Add an End Point Group(EPG) to a contract as provider/consumer.""" - if self.manager.db.get_provider_contract(): - # Set this network's EPG as a consumer - self.manager.set_contract_for_epg(tenant_id, epg.epg_id, - contract.contract_id) - else: - # Set this network's EPG as a provider - self.manager.set_contract_for_epg(tenant_id, epg.epg_id, - contract.contract_id, - provider=True) - - def add_router_interface(self, context, router_id, interface_info): - """Attach a subnet to a router.""" - tenant_id = context.tenant_id - subnet_id = interface_info['subnet_id'] - LOG.debug("Attaching subnet %(subnet_id)s to " - "router %(router_id)s" % {'subnet_id': subnet_id, - 'router_id': router_id}) - - # Get network for this subnet - subnet = self.get_subnet(context, subnet_id) - network_id = subnet['network_id'] - net_name = self.get_network(context, network_id)['name'] - - # Setup tenant filters and contracts - contract = self.manager.create_tenant_contract(tenant_id) - - # Check for a provider EPG - epg = self.manager.ensure_epg_created_for_network(tenant_id, - network_id, - net_name) - self._add_epg_to_contract(tenant_id, epg, contract) - - # Create DB port - try: - return super(ApicL3ServicePlugin, self).add_router_interface( - context, router_id, interface_info) - except Exception: - LOG.error(_("Error attaching subnet %(subnet_id)s to " - "router %(router_id)s") % {'subnet_id': subnet_id, - 'router_id': router_id}) - with excutils.save_and_reraise_exception(): - self.manager.delete_contract_for_epg(tenant_id, epg.epg_id, - contract.contract_id, - provider=epg.provider) - - def remove_router_interface(self, context, router_id, interface_info): - """Detach a subnet from a router.""" - tenant_id = context.tenant_id - subnet_id = interface_info['subnet_id'] - LOG.debug("Detaching subnet %(subnet_id)s from " - "router %(router_id)s" % {'subnet_id': subnet_id, - 'router_id': router_id}) - - # Get network for this subnet - subnet = self.get_subnet(context, subnet_id) - network_id = subnet['network_id'] - network = self.get_network(context, network_id) - - contract = self.manager.create_tenant_contract(tenant_id) - - epg = self.manager.ensure_epg_created_for_network(tenant_id, - network_id, - network['name']) - # Delete contract for this epg - self.manager.delete_contract_for_epg(tenant_id, epg.epg_id, - contract.contract_id, - provider=epg.provider) - - try: - return super(ApicL3ServicePlugin, self).remove_router_interface( - context, router_id, interface_info) - except Exception: - LOG.error(_("Error detaching subnet %(subnet_id)s from " - "router %(router_id)s") % {'subnet_id': subnet_id, - 'router_id': router_id}) - with excutils.save_and_reraise_exception(): - self._add_epg_to_contract(tenant_id, epg, contract) diff --git a/neutron/services/l3_router/l3_router_plugin.py b/neutron/services/l3_router/l3_router_plugin.py deleted file mode 100644 index bd1378bc8..000000000 --- a/neutron/services/l3_router/l3_router_plugin.py +++ /dev/null @@ -1,98 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. -# -# @author: Bob Melander, Cisco Systems, Inc. - -from oslo.config import cfg - -from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api -from neutron.common import constants as q_const -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db import api as qdbapi -from neutron.db import db_base_plugin_v2 -from neutron.db import extraroute_db -from neutron.db import l3_agentschedulers_db -from neutron.db import l3_gwmode_db -from neutron.db import l3_rpc_base -from neutron.db import model_base -from neutron.openstack.common import importutils -from neutron.plugins.common import constants - - -class L3RouterPluginRpcCallbacks(rpc_compat.RpcCallback, - l3_rpc_base.L3RpcCallbackMixin): - - RPC_API_VERSION = '1.1' - - -class L3RouterPlugin(db_base_plugin_v2.CommonDbMixin, - extraroute_db.ExtraRoute_db_mixin, - l3_gwmode_db.L3_NAT_db_mixin, - l3_agentschedulers_db.L3AgentSchedulerDbMixin): - - """Implementation of the Neutron L3 Router Service Plugin. - - This class implements a L3 service plugin that provides - router and floatingip resources and manages associated - request/response. - All DB related work is implemented in classes - l3_db.L3_NAT_db_mixin and extraroute_db.ExtraRoute_db_mixin. - """ - supported_extension_aliases = ["router", "ext-gw-mode", - "extraroute", "l3_agent_scheduler"] - - def __init__(self): - qdbapi.register_models(base=model_base.BASEV2) - self.setup_rpc() - self.router_scheduler = importutils.import_object( - cfg.CONF.router_scheduler_driver) - - def setup_rpc(self): - # RPC support - self.topic = topics.L3PLUGIN - self.conn = rpc_compat.create_connection(new=True) - self.agent_notifiers.update( - {q_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) - self.endpoints = [L3RouterPluginRpcCallbacks()] - self.conn.create_consumer(self.topic, self.endpoints, - fanout=False) - self.conn.consume_in_threads() - - def get_plugin_type(self): - return constants.L3_ROUTER_NAT - - def get_plugin_description(self): - """returns string description of the plugin.""" - return ("L3 Router Service Plugin for basic L3 forwarding" - " between (L2) Neutron networks and access to external" - " networks via a NAT gateway.") - - def create_floatingip(self, context, floatingip): - """Create floating IP. - - :param context: Neutron request context - :param floatingip: data fo the floating IP being created - :returns: A floating IP object on success - - AS the l3 router plugin aysnchrounously creates floating IPs - leveraging tehe l3 agent, the initial status fro the floating - IP object will be DOWN. - """ - return super(L3RouterPlugin, self).create_floatingip( - context, floatingip, - initial_status=q_const.FLOATINGIP_STATUS_DOWN) diff --git a/neutron/services/loadbalancer/__init__.py b/neutron/services/loadbalancer/__init__.py deleted file mode 100644 index 5e8da711f..000000000 --- a/neutron/services/loadbalancer/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/loadbalancer/agent/__init__.py b/neutron/services/loadbalancer/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/services/loadbalancer/agent/agent.py b/neutron/services/loadbalancer/agent/agent.py deleted file mode 100644 index 84de61b13..000000000 --- a/neutron/services/loadbalancer/agent/agent.py +++ /dev/null @@ -1,72 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost - -import sys - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent.linux import interface -from neutron.common import config as common_config -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.openstack.common import service -from neutron.services.loadbalancer.agent import agent_manager as manager - -OPTS = [ - cfg.IntOpt( - 'periodic_interval', - default=10, - help=_('Seconds between periodic task runs') - ) -] - - -class LbaasAgentService(rpc_compat.Service): - def start(self): - super(LbaasAgentService, self).start() - self.tg.add_timer( - cfg.CONF.periodic_interval, - self.manager.run_periodic_tasks, - None, - None - ) - - -def main(): - cfg.CONF.register_opts(OPTS) - cfg.CONF.register_opts(manager.OPTS) - # import interface options just in case the driver uses namespaces - cfg.CONF.register_opts(interface.OPTS) - config.register_interface_driver_opts_helper(cfg.CONF) - config.register_agent_state_opts_helper(cfg.CONF) - config.register_root_helper(cfg.CONF) - - common_config.init(sys.argv[1:]) - config.setup_logging(cfg.CONF) - - mgr = manager.LbaasAgentManager(cfg.CONF) - svc = LbaasAgentService( - host=cfg.CONF.host, - topic=topics.LOADBALANCER_AGENT, - manager=mgr - ) - service.launch(svc).wait() diff --git a/neutron/services/loadbalancer/agent/agent_api.py b/neutron/services/loadbalancer/agent/agent_api.py deleted file mode 100644 index 5c1e2b936..000000000 --- a/neutron/services/loadbalancer/agent/agent_api.py +++ /dev/null @@ -1,100 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost - -from neutron.common import rpc_compat - - -class LbaasAgentApi(rpc_compat.RpcProxy): - """Agent side of the Agent to Plugin RPC API.""" - - API_VERSION = '2.0' - # history - # 1.0 Initial version - # 2.0 Generic API for agent based drivers - # - get_logical_device() handling changed on plugin side; - # - pool_deployed() and update_status() methods added; - - def __init__(self, topic, context, host): - super(LbaasAgentApi, self).__init__(topic, self.API_VERSION) - self.context = context - self.host = host - - def get_ready_devices(self): - return self.call( - self.context, - self.make_msg('get_ready_devices', host=self.host), - topic=self.topic - ) - - def pool_destroyed(self, pool_id): - return self.call( - self.context, - self.make_msg('pool_destroyed', pool_id=pool_id), - topic=self.topic - ) - - def pool_deployed(self, pool_id): - return self.call( - self.context, - self.make_msg('pool_deployed', pool_id=pool_id), - topic=self.topic - ) - - def get_logical_device(self, pool_id): - return self.call( - self.context, - self.make_msg( - 'get_logical_device', - pool_id=pool_id - ), - topic=self.topic - ) - - def update_status(self, obj_type, obj_id, status): - return self.call( - self.context, - self.make_msg('update_status', obj_type=obj_type, obj_id=obj_id, - status=status), - topic=self.topic - ) - - def plug_vip_port(self, port_id): - return self.call( - self.context, - self.make_msg('plug_vip_port', port_id=port_id, host=self.host), - topic=self.topic - ) - - def unplug_vip_port(self, port_id): - return self.call( - self.context, - self.make_msg('unplug_vip_port', port_id=port_id, host=self.host), - topic=self.topic - ) - - def update_pool_stats(self, pool_id, stats): - return self.call( - self.context, - self.make_msg( - 'update_pool_stats', - pool_id=pool_id, - stats=stats, - host=self.host - ), - topic=self.topic - ) diff --git a/neutron/services/loadbalancer/agent/agent_device_driver.py b/neutron/services/loadbalancer/agent/agent_device_driver.py deleted file mode 100644 index 5c555b1b8..000000000 --- a/neutron/services/loadbalancer/agent/agent_device_driver.py +++ /dev/null @@ -1,98 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 OpenStack Foundation. All rights reserved -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class AgentDeviceDriver(object): - """Abstract device driver that defines the API required by LBaaS agent.""" - - @abc.abstractmethod - def get_name(cls): - """Returns unique name across all LBaaS device drivers.""" - pass - - @abc.abstractmethod - def deploy_instance(self, logical_config): - """Fully deploys a loadbalancer instance from a given config.""" - pass - - @abc.abstractmethod - def undeploy_instance(self, pool_id): - """Fully undeploys the loadbalancer instance.""" - pass - - @abc.abstractmethod - def get_stats(self, pool_id): - pass - - def remove_orphans(self, known_pool_ids): - # Not all drivers will support this - raise NotImplementedError() - - @abc.abstractmethod - def create_vip(self, vip): - pass - - @abc.abstractmethod - def update_vip(self, old_vip, vip): - pass - - @abc.abstractmethod - def delete_vip(self, vip): - pass - - @abc.abstractmethod - def create_pool(self, pool): - pass - - @abc.abstractmethod - def update_pool(self, old_pool, pool): - pass - - @abc.abstractmethod - def delete_pool(self, pool): - pass - - @abc.abstractmethod - def create_member(self, member): - pass - - @abc.abstractmethod - def update_member(self, old_member, member): - pass - - @abc.abstractmethod - def delete_member(self, member): - pass - - @abc.abstractmethod - def create_pool_health_monitor(self, health_monitor, pool_id): - pass - - @abc.abstractmethod - def update_pool_health_monitor(self, - old_health_monitor, - health_monitor, - pool_id): - pass - - @abc.abstractmethod - def delete_pool_health_monitor(self, health_monitor, pool_id): - pass diff --git a/neutron/services/loadbalancer/agent/agent_manager.py b/neutron/services/loadbalancer/agent/agent_manager.py deleted file mode 100644 index 1bbb53cb0..000000000 --- a/neutron/services/loadbalancer/agent/agent_manager.py +++ /dev/null @@ -1,338 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost - -from oslo.config import cfg - -from neutron.agent import rpc as agent_rpc -from neutron.common import constants as n_const -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron import context -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.openstack.common import periodic_task -from neutron.plugins.common import constants -from neutron.services.loadbalancer.agent import agent_api - -LOG = logging.getLogger(__name__) - -OPTS = [ - cfg.MultiStrOpt( - 'device_driver', - default=['neutron.services.loadbalancer.drivers' - '.haproxy.namespace_driver.HaproxyNSDriver'], - help=_('Drivers used to manage loadbalancing devices'), - ), -] - - -class DeviceNotFoundOnAgent(n_exc.NotFound): - msg = _('Unknown device with pool_id %(pool_id)s') - - -class LbaasAgentManager(rpc_compat.RpcCallback, periodic_task.PeriodicTasks): - - RPC_API_VERSION = '2.0' - # history - # 1.0 Initial version - # 1.1 Support agent_updated call - # 2.0 Generic API for agent based drivers - # - modify/reload/destroy_pool methods were removed; - # - added methods to handle create/update/delete for every lbaas - # object individually; - - def __init__(self, conf): - super(LbaasAgentManager, self).__init__() - self.conf = conf - self.context = context.get_admin_context_without_session() - self.plugin_rpc = agent_api.LbaasAgentApi( - topics.LOADBALANCER_PLUGIN, - self.context, - self.conf.host - ) - self._load_drivers() - - self.agent_state = { - 'binary': 'neutron-lbaas-agent', - 'host': conf.host, - 'topic': topics.LOADBALANCER_AGENT, - 'configurations': {'device_drivers': self.device_drivers.keys()}, - 'agent_type': n_const.AGENT_TYPE_LOADBALANCER, - 'start_flag': True} - self.admin_state_up = True - - self._setup_state_rpc() - self.needs_resync = False - # pool_id->device_driver_name mapping used to store known instances - self.instance_mapping = {} - - def _load_drivers(self): - self.device_drivers = {} - for driver in self.conf.device_driver: - try: - driver_inst = importutils.import_object( - driver, - self.conf, - self.plugin_rpc - ) - except ImportError: - msg = _('Error importing loadbalancer device driver: %s') - raise SystemExit(msg % driver) - - driver_name = driver_inst.get_name() - if driver_name not in self.device_drivers: - self.device_drivers[driver_name] = driver_inst - else: - msg = _('Multiple device drivers with the same name found: %s') - raise SystemExit(msg % driver_name) - - def _setup_state_rpc(self): - self.state_rpc = agent_rpc.PluginReportStateAPI( - topics.LOADBALANCER_PLUGIN) - report_interval = self.conf.AGENT.report_interval - if report_interval: - heartbeat = loopingcall.FixedIntervalLoopingCall( - self._report_state) - heartbeat.start(interval=report_interval) - - def _report_state(self): - try: - instance_count = len(self.instance_mapping) - self.agent_state['configurations']['instances'] = instance_count - self.state_rpc.report_state(self.context, - self.agent_state) - self.agent_state.pop('start_flag', None) - except Exception: - LOG.exception(_("Failed reporting state!")) - - def initialize_service_hook(self, started_by): - self.sync_state() - - @periodic_task.periodic_task - def periodic_resync(self, context): - if self.needs_resync: - self.needs_resync = False - self.sync_state() - - @periodic_task.periodic_task(spacing=6) - def collect_stats(self, context): - for pool_id, driver_name in self.instance_mapping.items(): - driver = self.device_drivers[driver_name] - try: - stats = driver.get_stats(pool_id) - if stats: - self.plugin_rpc.update_pool_stats(pool_id, stats) - except Exception: - LOG.exception(_('Error updating statistics on pool %s'), - pool_id) - self.needs_resync = True - - def sync_state(self): - known_instances = set(self.instance_mapping.keys()) - try: - ready_instances = set(self.plugin_rpc.get_ready_devices()) - - for deleted_id in known_instances - ready_instances: - self._destroy_pool(deleted_id) - - for pool_id in ready_instances: - self._reload_pool(pool_id) - - except Exception: - LOG.exception(_('Unable to retrieve ready devices')) - self.needs_resync = True - - self.remove_orphans() - - def _get_driver(self, pool_id): - if pool_id not in self.instance_mapping: - raise DeviceNotFoundOnAgent(pool_id=pool_id) - - driver_name = self.instance_mapping[pool_id] - return self.device_drivers[driver_name] - - def _reload_pool(self, pool_id): - try: - logical_config = self.plugin_rpc.get_logical_device(pool_id) - driver_name = logical_config['driver'] - if driver_name not in self.device_drivers: - LOG.error(_('No device driver ' - 'on agent: %s.'), driver_name) - self.plugin_rpc.update_status( - 'pool', pool_id, constants.ERROR) - return - - self.device_drivers[driver_name].deploy_instance(logical_config) - self.instance_mapping[pool_id] = driver_name - self.plugin_rpc.pool_deployed(pool_id) - except Exception: - LOG.exception(_('Unable to deploy instance for pool: %s'), pool_id) - self.needs_resync = True - - def _destroy_pool(self, pool_id): - driver = self._get_driver(pool_id) - try: - driver.undeploy_instance(pool_id) - del self.instance_mapping[pool_id] - self.plugin_rpc.pool_destroyed(pool_id) - except Exception: - LOG.exception(_('Unable to destroy device for pool: %s'), pool_id) - self.needs_resync = True - - def remove_orphans(self): - for driver_name in self.device_drivers: - pool_ids = [pool_id for pool_id in self.instance_mapping - if self.instance_mapping[pool_id] == driver_name] - try: - self.device_drivers[driver_name].remove_orphans(pool_ids) - except NotImplementedError: - pass # Not all drivers will support this - - def _handle_failed_driver_call(self, operation, obj_type, obj_id, driver): - LOG.exception(_('%(operation)s %(obj)s %(id)s failed on device driver ' - '%(driver)s'), - {'operation': operation.capitalize(), 'obj': obj_type, - 'id': obj_id, 'driver': driver}) - self.plugin_rpc.update_status(obj_type, obj_id, constants.ERROR) - - def create_vip(self, context, vip): - driver = self._get_driver(vip['pool_id']) - try: - driver.create_vip(vip) - except Exception: - self._handle_failed_driver_call('create', 'vip', vip['id'], - driver.get_name()) - else: - self.plugin_rpc.update_status('vip', vip['id'], constants.ACTIVE) - - def update_vip(self, context, old_vip, vip): - driver = self._get_driver(vip['pool_id']) - try: - driver.update_vip(old_vip, vip) - except Exception: - self._handle_failed_driver_call('update', 'vip', vip['id'], - driver.get_name()) - else: - self.plugin_rpc.update_status('vip', vip['id'], constants.ACTIVE) - - def delete_vip(self, context, vip): - driver = self._get_driver(vip['pool_id']) - driver.delete_vip(vip) - - def create_pool(self, context, pool, driver_name): - if driver_name not in self.device_drivers: - LOG.error(_('No device driver on agent: %s.'), driver_name) - self.plugin_rpc.update_status('pool', pool['id'], constants.ERROR) - return - - driver = self.device_drivers[driver_name] - try: - driver.create_pool(pool) - except Exception: - self._handle_failed_driver_call('create', 'pool', pool['id'], - driver.get_name()) - else: - self.instance_mapping[pool['id']] = driver_name - self.plugin_rpc.update_status('pool', pool['id'], constants.ACTIVE) - - def update_pool(self, context, old_pool, pool): - driver = self._get_driver(pool['id']) - try: - driver.update_pool(old_pool, pool) - except Exception: - self._handle_failed_driver_call('update', 'pool', pool['id'], - driver.get_name()) - else: - self.plugin_rpc.update_status('pool', pool['id'], constants.ACTIVE) - - def delete_pool(self, context, pool): - driver = self._get_driver(pool['id']) - driver.delete_pool(pool) - del self.instance_mapping[pool['id']] - - def create_member(self, context, member): - driver = self._get_driver(member['pool_id']) - try: - driver.create_member(member) - except Exception: - self._handle_failed_driver_call('create', 'member', member['id'], - driver.get_name()) - else: - self.plugin_rpc.update_status('member', member['id'], - constants.ACTIVE) - - def update_member(self, context, old_member, member): - driver = self._get_driver(member['pool_id']) - try: - driver.update_member(old_member, member) - except Exception: - self._handle_failed_driver_call('update', 'member', member['id'], - driver.get_name()) - else: - self.plugin_rpc.update_status('member', member['id'], - constants.ACTIVE) - - def delete_member(self, context, member): - driver = self._get_driver(member['pool_id']) - driver.delete_member(member) - - def create_pool_health_monitor(self, context, health_monitor, pool_id): - driver = self._get_driver(pool_id) - assoc_id = {'pool_id': pool_id, 'monitor_id': health_monitor['id']} - try: - driver.create_pool_health_monitor(health_monitor, pool_id) - except Exception: - self._handle_failed_driver_call( - 'create', 'health_monitor', assoc_id, driver.get_name()) - else: - self.plugin_rpc.update_status( - 'health_monitor', assoc_id, constants.ACTIVE) - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, pool_id): - driver = self._get_driver(pool_id) - assoc_id = {'pool_id': pool_id, 'monitor_id': health_monitor['id']} - try: - driver.update_pool_health_monitor(old_health_monitor, - health_monitor, - pool_id) - except Exception: - self._handle_failed_driver_call( - 'update', 'health_monitor', assoc_id, driver.get_name()) - else: - self.plugin_rpc.update_status( - 'health_monitor', assoc_id, constants.ACTIVE) - - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - driver = self._get_driver(pool_id) - driver.delete_pool_health_monitor(health_monitor, pool_id) - - def agent_updated(self, context, payload): - """Handle the agent_updated notification event.""" - if payload['admin_state_up'] != self.admin_state_up: - self.admin_state_up = payload['admin_state_up'] - if self.admin_state_up: - self.needs_resync = True - else: - for pool_id in self.instance_mapping.keys(): - LOG.info(_("Destroying pool %s due to agent disabling"), - pool_id) - self._destroy_pool(pool_id) - LOG.info(_("Agent_updated by server side %s!"), payload) diff --git a/neutron/services/loadbalancer/agent_scheduler.py b/neutron/services/loadbalancer/agent_scheduler.py deleted file mode 100644 index a17e528d1..000000000 --- a/neutron/services/loadbalancer/agent_scheduler.py +++ /dev/null @@ -1,130 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -import random - -import sqlalchemy as sa -from sqlalchemy import orm -from sqlalchemy.orm import joinedload - -from neutron.common import constants -from neutron.db import agents_db -from neutron.db import agentschedulers_db -from neutron.db import model_base -from neutron.extensions import lbaas_agentscheduler -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - - -class PoolLoadbalancerAgentBinding(model_base.BASEV2): - """Represents binding between neutron loadbalancer pools and agents.""" - - pool_id = sa.Column(sa.String(36), - sa.ForeignKey("pools.id", ondelete='CASCADE'), - primary_key=True) - agent = orm.relation(agents_db.Agent) - agent_id = sa.Column(sa.String(36), sa.ForeignKey("agents.id", - ondelete='CASCADE'), - nullable=False) - - -class LbaasAgentSchedulerDbMixin(agentschedulers_db.AgentSchedulerDbMixin, - lbaas_agentscheduler - .LbaasAgentSchedulerPluginBase): - - def get_lbaas_agent_hosting_pool(self, context, pool_id, active=None): - query = context.session.query(PoolLoadbalancerAgentBinding) - query = query.options(joinedload('agent')) - binding = query.get(pool_id) - - if (binding and self.is_eligible_agent( - active, binding.agent)): - return {'agent': self._make_agent_dict(binding.agent)} - - def get_lbaas_agents(self, context, active=None, filters=None): - query = context.session.query(agents_db.Agent) - query = query.filter_by(agent_type=constants.AGENT_TYPE_LOADBALANCER) - if active is not None: - query = query.filter_by(admin_state_up=active) - if filters: - for key, value in filters.iteritems(): - column = getattr(agents_db.Agent, key, None) - if column: - query = query.filter(column.in_(value)) - - return [agent - for agent in query - if self.is_eligible_agent(active, agent)] - - def list_pools_on_lbaas_agent(self, context, id): - query = context.session.query(PoolLoadbalancerAgentBinding.pool_id) - query = query.filter_by(agent_id=id) - pool_ids = [item[0] for item in query] - if pool_ids: - return {'pools': self.get_pools(context, filters={'id': pool_ids})} - else: - return {'pools': []} - - def get_lbaas_agent_candidates(self, device_driver, active_agents): - candidates = [] - for agent in active_agents: - agent_conf = self.get_configuration_dict(agent) - if device_driver in agent_conf['device_drivers']: - candidates.append(agent) - return candidates - - -class ChanceScheduler(object): - """Allocate a loadbalancer agent for a vip in a random way.""" - - def schedule(self, plugin, context, pool, device_driver): - """Schedule the pool to an active loadbalancer agent if there - is no enabled agent hosting it. - """ - with context.session.begin(subtransactions=True): - lbaas_agent = plugin.get_lbaas_agent_hosting_pool( - context, pool['id']) - if lbaas_agent: - LOG.debug(_('Pool %(pool_id)s has already been hosted' - ' by lbaas agent %(agent_id)s'), - {'pool_id': pool['id'], - 'agent_id': lbaas_agent['id']}) - return - - active_agents = plugin.get_lbaas_agents(context, active=True) - if not active_agents: - LOG.warn(_('No active lbaas agents for pool %s'), pool['id']) - return - - candidates = plugin.get_lbaas_agent_candidates(device_driver, - active_agents) - if not candidates: - LOG.warn(_('No lbaas agent supporting device driver %s'), - device_driver) - return - - chosen_agent = random.choice(candidates) - binding = PoolLoadbalancerAgentBinding() - binding.agent = chosen_agent - binding.pool_id = pool['id'] - context.session.add(binding) - LOG.debug(_('Pool %(pool_id)s is scheduled to ' - 'lbaas agent %(agent_id)s'), - {'pool_id': pool['id'], - 'agent_id': chosen_agent['id']}) - return chosen_agent diff --git a/neutron/services/loadbalancer/constants.py b/neutron/services/loadbalancer/constants.py deleted file mode 100644 index 6125b9b10..000000000 --- a/neutron/services/loadbalancer/constants.py +++ /dev/null @@ -1,47 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2013 Mirantis, 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. - -LB_METHOD_ROUND_ROBIN = 'ROUND_ROBIN' -LB_METHOD_LEAST_CONNECTIONS = 'LEAST_CONNECTIONS' -LB_METHOD_SOURCE_IP = 'SOURCE_IP' - -PROTOCOL_TCP = 'TCP' -PROTOCOL_HTTP = 'HTTP' -PROTOCOL_HTTPS = 'HTTPS' - -HEALTH_MONITOR_PING = 'PING' -HEALTH_MONITOR_TCP = 'TCP' -HEALTH_MONITOR_HTTP = 'HTTP' -HEALTH_MONITOR_HTTPS = 'HTTPS' - -SESSION_PERSISTENCE_SOURCE_IP = 'SOURCE_IP' -SESSION_PERSISTENCE_HTTP_COOKIE = 'HTTP_COOKIE' -SESSION_PERSISTENCE_APP_COOKIE = 'APP_COOKIE' - -STATS_ACTIVE_CONNECTIONS = 'active_connections' -STATS_MAX_CONNECTIONS = 'max_connections' -STATS_TOTAL_CONNECTIONS = 'total_connections' -STATS_CURRENT_SESSIONS = 'current_sessions' -STATS_MAX_SESSIONS = 'max_sessions' -STATS_TOTAL_SESSIONS = 'total_sessions' -STATS_IN_BYTES = 'bytes_in' -STATS_OUT_BYTES = 'bytes_out' -STATS_CONNECTION_ERRORS = 'connection_errors' -STATS_RESPONSE_ERRORS = 'response_errors' -STATS_STATUS = 'status' -STATS_HEALTH = 'health' -STATS_FAILED_CHECKS = 'failed_checks' diff --git a/neutron/services/loadbalancer/drivers/__init__.py b/neutron/services/loadbalancer/drivers/__init__.py deleted file mode 100644 index ce18bf6d6..000000000 --- a/neutron/services/loadbalancer/drivers/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost diff --git a/neutron/services/loadbalancer/drivers/abstract_driver.py b/neutron/services/loadbalancer/drivers/abstract_driver.py deleted file mode 100644 index 55d2c6d62..000000000 --- a/neutron/services/loadbalancer/drivers/abstract_driver.py +++ /dev/null @@ -1,130 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Radware LTD. -# -# 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. -# -# @author: Avishay Balderman, Radware - -import abc - -import six - - -@six.add_metaclass(abc.ABCMeta) -class LoadBalancerAbstractDriver(object): - """Abstract lbaas driver that expose ~same API as lbaas plugin. - - The configuration elements (Vip,Member,etc) are the dicts that - are returned to the tenant. - Get operations are not part of the API - it will be handled - by the lbaas plugin. - """ - - @abc.abstractmethod - def create_vip(self, context, vip): - """A real driver would invoke a call to his backend - and set the Vip status to ACTIVE/ERROR according - to the backend call result - self.plugin.update_status(context, Vip, vip["id"], - constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def update_vip(self, context, old_vip, vip): - """Driver may call the code below in order to update the status. - self.plugin.update_status(context, Vip, id, constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def delete_vip(self, context, vip): - """A real driver would invoke a call to his backend - and try to delete the Vip. - if the deletion was successful, delete the record from the database. - if the deletion has failed, set the Vip status to ERROR. - """ - pass - - @abc.abstractmethod - def create_pool(self, context, pool): - """Driver may call the code below in order to update the status. - self.plugin.update_status(context, Pool, pool["id"], - constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def update_pool(self, context, old_pool, pool): - """Driver may call the code below in order to update the status. - self.plugin.update_status(context, - Pool, - pool["id"], constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def delete_pool(self, context, pool): - """Driver can call the code below in order to delete the pool. - self.plugin._delete_db_pool(context, pool["id"]) - or set the status to ERROR if deletion failed - """ - pass - - @abc.abstractmethod - def stats(self, context, pool_id): - pass - - @abc.abstractmethod - def create_member(self, context, member): - """Driver may call the code below in order to update the status. - self.plugin.update_status(context, Member, member["id"], - constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def update_member(self, context, old_member, member): - """Driver may call the code below in order to update the status. - self.plugin.update_status(context, Member, - member["id"], constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def delete_member(self, context, member): - pass - - @abc.abstractmethod - def update_pool_health_monitor(self, context, - old_health_monitor, - health_monitor, - pool_id): - pass - - @abc.abstractmethod - def create_pool_health_monitor(self, context, - health_monitor, - pool_id): - """Driver may call the code below in order to update the status. - self.plugin.update_pool_health_monitor(context, - health_monitor["id"], - pool_id, - constants.ACTIVE) - """ - pass - - @abc.abstractmethod - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - pass diff --git a/neutron/services/loadbalancer/drivers/common/__init__.py b/neutron/services/loadbalancer/drivers/common/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/services/loadbalancer/drivers/common/agent_driver_base.py b/neutron/services/loadbalancer/drivers/common/agent_driver_base.py deleted file mode 100644 index 5849c9597..000000000 --- a/neutron/services/loadbalancer/drivers/common/agent_driver_base.py +++ /dev/null @@ -1,445 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost - -import uuid - -from oslo.config import cfg - -from neutron.common import constants as q_const -from neutron.common import exceptions as n_exc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db import agents_db -from neutron.db.loadbalancer import loadbalancer_db -from neutron.extensions import lbaas_agentscheduler -from neutron.extensions import portbindings -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.loadbalancer.drivers import abstract_driver - -LOG = logging.getLogger(__name__) - -AGENT_SCHEDULER_OPTS = [ - cfg.StrOpt('loadbalancer_pool_scheduler_driver', - default='neutron.services.loadbalancer.agent_scheduler' - '.ChanceScheduler', - help=_('Driver to use for scheduling ' - 'pool to a default loadbalancer agent')), -] - -cfg.CONF.register_opts(AGENT_SCHEDULER_OPTS) - - -class DriverNotSpecified(n_exc.NeutronException): - message = _("Device driver for agent should be specified " - "in plugin driver.") - - -class LoadBalancerCallbacks(rpc_compat.RpcCallback): - - RPC_API_VERSION = '2.0' - # history - # 1.0 Initial version - # 2.0 Generic API for agent based drivers - # - get_logical_device() handling changed; - # - pool_deployed() and update_status() methods added; - - def __init__(self, plugin): - super(LoadBalancerCallbacks, self).__init__() - self.plugin = plugin - - def get_ready_devices(self, context, host=None): - with context.session.begin(subtransactions=True): - agents = self.plugin.get_lbaas_agents(context, - filters={'host': [host]}) - if not agents: - return [] - elif len(agents) > 1: - LOG.warning(_('Multiple lbaas agents found on host %s'), host) - pools = self.plugin.list_pools_on_lbaas_agent(context, - agents[0].id) - pool_ids = [pool['id'] for pool in pools['pools']] - - qry = context.session.query(loadbalancer_db.Pool.id) - qry = qry.filter(loadbalancer_db.Pool.id.in_(pool_ids)) - qry = qry.filter( - loadbalancer_db.Pool.status.in_( - constants.ACTIVE_PENDING_STATUSES)) - up = True # makes pep8 and sqlalchemy happy - qry = qry.filter(loadbalancer_db.Pool.admin_state_up == up) - return [id for id, in qry] - - def get_logical_device(self, context, pool_id=None): - with context.session.begin(subtransactions=True): - qry = context.session.query(loadbalancer_db.Pool) - qry = qry.filter_by(id=pool_id) - pool = qry.one() - retval = {} - retval['pool'] = self.plugin._make_pool_dict(pool) - - if pool.vip: - retval['vip'] = self.plugin._make_vip_dict(pool.vip) - retval['vip']['port'] = ( - self.plugin._core_plugin._make_port_dict(pool.vip.port) - ) - for fixed_ip in retval['vip']['port']['fixed_ips']: - fixed_ip['subnet'] = ( - self.plugin._core_plugin.get_subnet( - context, - fixed_ip['subnet_id'] - ) - ) - retval['members'] = [ - self.plugin._make_member_dict(m) - for m in pool.members if ( - m.status in constants.ACTIVE_PENDING_STATUSES or - m.status == constants.INACTIVE) - ] - retval['healthmonitors'] = [ - self.plugin._make_health_monitor_dict(hm.healthmonitor) - for hm in pool.monitors - if hm.status in constants.ACTIVE_PENDING_STATUSES - ] - retval['driver'] = ( - self.plugin.drivers[pool.provider.provider_name].device_driver) - - return retval - - def pool_deployed(self, context, pool_id): - with context.session.begin(subtransactions=True): - qry = context.session.query(loadbalancer_db.Pool) - qry = qry.filter_by(id=pool_id) - pool = qry.one() - - # set all resources to active - if pool.status in constants.ACTIVE_PENDING_STATUSES: - pool.status = constants.ACTIVE - - if (pool.vip and pool.vip.status in - constants.ACTIVE_PENDING_STATUSES): - pool.vip.status = constants.ACTIVE - - for m in pool.members: - if m.status in constants.ACTIVE_PENDING_STATUSES: - m.status = constants.ACTIVE - - for hm in pool.monitors: - if hm.status in constants.ACTIVE_PENDING_STATUSES: - hm.status = constants.ACTIVE - - def update_status(self, context, obj_type, obj_id, status): - model_mapping = { - 'pool': loadbalancer_db.Pool, - 'vip': loadbalancer_db.Vip, - 'member': loadbalancer_db.Member, - 'health_monitor': loadbalancer_db.PoolMonitorAssociation - } - if obj_type not in model_mapping: - raise n_exc.Invalid(_('Unknown object type: %s') % obj_type) - try: - if obj_type == 'health_monitor': - self.plugin.update_pool_health_monitor( - context, obj_id['monitor_id'], obj_id['pool_id'], status) - else: - self.plugin.update_status( - context, model_mapping[obj_type], obj_id, status) - except n_exc.NotFound: - # update_status may come from agent on an object which was - # already deleted from db with other request - LOG.warning(_('Cannot update status: %(obj_type)s %(obj_id)s ' - 'not found in the DB, it was probably deleted ' - 'concurrently'), - {'obj_type': obj_type, 'obj_id': obj_id}) - - def pool_destroyed(self, context, pool_id=None): - """Agent confirmation hook that a pool has been destroyed. - - This method exists for subclasses to change the deletion - behavior. - """ - pass - - def plug_vip_port(self, context, port_id=None, host=None): - if not port_id: - return - - try: - port = self.plugin._core_plugin.get_port( - context, - port_id - ) - except n_exc.PortNotFound: - msg = _('Unable to find port %s to plug.') - LOG.debug(msg, port_id) - return - - port['admin_state_up'] = True - port['device_owner'] = 'neutron:' + constants.LOADBALANCER - port['device_id'] = str(uuid.uuid5(uuid.NAMESPACE_DNS, str(host))) - port[portbindings.HOST_ID] = host - self.plugin._core_plugin.update_port( - context, - port_id, - {'port': port} - ) - - def unplug_vip_port(self, context, port_id=None, host=None): - if not port_id: - return - - try: - port = self.plugin._core_plugin.get_port( - context, - port_id - ) - except n_exc.PortNotFound: - msg = _('Unable to find port %s to unplug. This can occur when ' - 'the Vip has been deleted first.') - LOG.debug(msg, port_id) - return - - port['admin_state_up'] = False - port['device_owner'] = '' - port['device_id'] = '' - - try: - self.plugin._core_plugin.update_port( - context, - port_id, - {'port': port} - ) - - except n_exc.PortNotFound: - msg = _('Unable to find port %s to unplug. This can occur when ' - 'the Vip has been deleted first.') - LOG.debug(msg, port_id) - - def update_pool_stats(self, context, pool_id=None, stats=None, host=None): - self.plugin.update_pool_stats(context, pool_id, data=stats) - - -class LoadBalancerAgentApi(rpc_compat.RpcProxy): - """Plugin side of plugin to agent RPC API.""" - - BASE_RPC_API_VERSION = '2.0' - # history - # 1.0 Initial version - # 1.1 Support agent_updated call - # 2.0 Generic API for agent based drivers - # - modify/reload/destroy_pool methods were removed; - # - added methods to handle create/update/delete for every lbaas - # object individually; - - def __init__(self, topic): - super(LoadBalancerAgentApi, self).__init__( - topic, default_version=self.BASE_RPC_API_VERSION) - - def _cast(self, context, method_name, method_args, host, version=None): - return self.cast( - context, - self.make_msg(method_name, **method_args), - topic='%s.%s' % (self.topic, host), - version=version - ) - - def create_vip(self, context, vip, host): - return self._cast(context, 'create_vip', {'vip': vip}, host) - - def update_vip(self, context, old_vip, vip, host): - return self._cast(context, 'update_vip', - {'old_vip': old_vip, 'vip': vip}, host) - - def delete_vip(self, context, vip, host): - return self._cast(context, 'delete_vip', {'vip': vip}, host) - - def create_pool(self, context, pool, host, driver_name): - return self._cast(context, 'create_pool', - {'pool': pool, 'driver_name': driver_name}, host) - - def update_pool(self, context, old_pool, pool, host): - return self._cast(context, 'update_pool', - {'old_pool': old_pool, 'pool': pool}, host) - - def delete_pool(self, context, pool, host): - return self._cast(context, 'delete_pool', {'pool': pool}, host) - - def create_member(self, context, member, host): - return self._cast(context, 'create_member', {'member': member}, host) - - def update_member(self, context, old_member, member, host): - return self._cast(context, 'update_member', - {'old_member': old_member, 'member': member}, host) - - def delete_member(self, context, member, host): - return self._cast(context, 'delete_member', {'member': member}, host) - - def create_pool_health_monitor(self, context, health_monitor, pool_id, - host): - return self._cast(context, 'create_pool_health_monitor', - {'health_monitor': health_monitor, - 'pool_id': pool_id}, host) - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, pool_id, host): - return self._cast(context, 'update_pool_health_monitor', - {'old_health_monitor': old_health_monitor, - 'health_monitor': health_monitor, - 'pool_id': pool_id}, host) - - def delete_pool_health_monitor(self, context, health_monitor, pool_id, - host): - return self._cast(context, 'delete_pool_health_monitor', - {'health_monitor': health_monitor, - 'pool_id': pool_id}, host) - - def agent_updated(self, context, admin_state_up, host): - return self._cast(context, 'agent_updated', - {'payload': {'admin_state_up': admin_state_up}}, - host) - - -class AgentDriverBase(abstract_driver.LoadBalancerAbstractDriver): - - # name of device driver that should be used by the agent; - # vendor specific plugin drivers must override it; - device_driver = None - - def __init__(self, plugin): - if not self.device_driver: - raise DriverNotSpecified() - - self.agent_rpc = LoadBalancerAgentApi(topics.LOADBALANCER_AGENT) - - self.plugin = plugin - self._set_callbacks_on_plugin() - self.plugin.agent_notifiers.update( - {q_const.AGENT_TYPE_LOADBALANCER: self.agent_rpc}) - - self.pool_scheduler = importutils.import_object( - cfg.CONF.loadbalancer_pool_scheduler_driver) - - def _set_callbacks_on_plugin(self): - # other agent based plugin driver might already set callbacks on plugin - if hasattr(self.plugin, 'agent_callbacks'): - return - - self.plugin.agent_endpoints = [ - LoadBalancerCallbacks(self.plugin), - agents_db.AgentExtRpcCallback(self.plugin) - ] - self.plugin.conn = rpc_compat.create_connection(new=True) - self.plugin.conn.create_consumer( - topics.LOADBALANCER_PLUGIN, - self.plugin.agent_endpoints, - fanout=False) - self.plugin.conn.consume_in_threads() - - def get_pool_agent(self, context, pool_id): - agent = self.plugin.get_lbaas_agent_hosting_pool(context, pool_id) - if not agent: - raise lbaas_agentscheduler.NoActiveLbaasAgent(pool_id=pool_id) - return agent['agent'] - - def create_vip(self, context, vip): - agent = self.get_pool_agent(context, vip['pool_id']) - self.agent_rpc.create_vip(context, vip, agent['host']) - - def update_vip(self, context, old_vip, vip): - agent = self.get_pool_agent(context, vip['pool_id']) - if vip['status'] in constants.ACTIVE_PENDING_STATUSES: - self.agent_rpc.update_vip(context, old_vip, vip, agent['host']) - else: - self.agent_rpc.delete_vip(context, vip, agent['host']) - - def delete_vip(self, context, vip): - self.plugin._delete_db_vip(context, vip['id']) - agent = self.get_pool_agent(context, vip['pool_id']) - self.agent_rpc.delete_vip(context, vip, agent['host']) - - def create_pool(self, context, pool): - agent = self.pool_scheduler.schedule(self.plugin, context, pool, - self.device_driver) - if not agent: - raise lbaas_agentscheduler.NoEligibleLbaasAgent(pool_id=pool['id']) - self.agent_rpc.create_pool(context, pool, agent['host'], - self.device_driver) - - def update_pool(self, context, old_pool, pool): - agent = self.get_pool_agent(context, pool['id']) - if pool['status'] in constants.ACTIVE_PENDING_STATUSES: - self.agent_rpc.update_pool(context, old_pool, pool, - agent['host']) - else: - self.agent_rpc.delete_pool(context, pool, agent['host']) - - def delete_pool(self, context, pool): - # get agent first to know host as binding will be deleted - # after pool is deleted from db - agent = self.plugin.get_lbaas_agent_hosting_pool(context, pool['id']) - self.plugin._delete_db_pool(context, pool['id']) - if agent: - self.agent_rpc.delete_pool(context, pool, agent['agent']['host']) - - def create_member(self, context, member): - agent = self.get_pool_agent(context, member['pool_id']) - self.agent_rpc.create_member(context, member, agent['host']) - - def update_member(self, context, old_member, member): - agent = self.get_pool_agent(context, member['pool_id']) - # member may change pool id - if member['pool_id'] != old_member['pool_id']: - old_pool_agent = self.plugin.get_lbaas_agent_hosting_pool( - context, old_member['pool_id']) - if old_pool_agent: - self.agent_rpc.delete_member(context, old_member, - old_pool_agent['agent']['host']) - self.agent_rpc.create_member(context, member, agent['host']) - else: - self.agent_rpc.update_member(context, old_member, member, - agent['host']) - - def delete_member(self, context, member): - self.plugin._delete_db_member(context, member['id']) - agent = self.get_pool_agent(context, member['pool_id']) - self.agent_rpc.delete_member(context, member, agent['host']) - - def create_pool_health_monitor(self, context, healthmon, pool_id): - # healthmon is not used here - agent = self.get_pool_agent(context, pool_id) - self.agent_rpc.create_pool_health_monitor(context, healthmon, - pool_id, agent['host']) - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, pool_id): - agent = self.get_pool_agent(context, pool_id) - self.agent_rpc.update_pool_health_monitor(context, old_health_monitor, - health_monitor, pool_id, - agent['host']) - - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - self.plugin._delete_db_pool_health_monitor( - context, health_monitor['id'], pool_id - ) - - agent = self.get_pool_agent(context, pool_id) - self.agent_rpc.delete_pool_health_monitor(context, health_monitor, - pool_id, agent['host']) - - def stats(self, context, pool_id): - pass diff --git a/neutron/services/loadbalancer/drivers/embrane/README b/neutron/services/loadbalancer/drivers/embrane/README deleted file mode 100644 index 22326b0b9..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/README +++ /dev/null @@ -1,9 +0,0 @@ -Embrane LBaaS Driver - -This DRIVER interfaces OpenStack Neutron with Embrane's heleos platform, -Load Balancing appliances for cloud environments. - -L2 connectivity is leveraged by one of the supported existing plugins. - -For more details on use, configuration and implementation please refer to: -https://wiki.openstack.org/wiki/Neutron/LBaaS/EmbraneDriver \ No newline at end of file diff --git a/neutron/services/loadbalancer/drivers/embrane/__init__.py b/neutron/services/loadbalancer/drivers/embrane/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/services/loadbalancer/drivers/embrane/agent/__init__.py b/neutron/services/loadbalancer/drivers/embrane/agent/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/services/loadbalancer/drivers/embrane/agent/dispatcher.py b/neutron/services/loadbalancer/drivers/embrane/agent/dispatcher.py deleted file mode 100644 index 7ba000599..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/agent/dispatcher.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -from eventlet import greenthread -from eventlet import queue -from heleosapi import exceptions as h_exc - -from neutron.openstack.common import log as logging -from neutron.plugins.embrane.common import contexts as ctx -from neutron.services.loadbalancer.drivers.embrane.agent import lb_operations -from neutron.services.loadbalancer.drivers.embrane import constants as econ - -LOG = logging.getLogger(__name__) - - -class Dispatcher(object): - def __init__(self, driver, async=True): - self._async = async - self._driver = driver - self.sync_items = dict() - self.handlers = lb_operations.handlers - - def dispatch_lb(self, d_context, *args, **kwargs): - item = d_context.item - event = d_context.event - n_context = d_context.n_context - chain = d_context.chain - - item_id = item["id"] - if event in self.handlers: - for f in self.handlers[event]: - first_run = False - if item_id not in self.sync_items: - self.sync_items[item_id] = [queue.Queue()] - first_run = True - self.sync_items[item_id][0].put( - ctx.OperationContext(event, n_context, item, chain, f, - args, kwargs)) - if first_run: - t = greenthread.spawn(self._consume_lb, - item_id, - self.sync_items[item_id][0], - self._driver, - self._async) - self.sync_items[item_id].append(t) - if not self._async: - t = self.sync_items[item_id][1] - t.wait() - - def _consume_lb(self, sync_item, sync_queue, driver, a_sync): - current_state = None - while True: - try: - if current_state == econ.DELETED: - del self.sync_items[sync_item] - return - try: - operation_context = sync_queue.get( - block=a_sync, - timeout=econ.QUEUE_TIMEOUT) - except queue.Empty: - del self.sync_items[sync_item] - return - - (operation_context.chain and - operation_context.chain.execute_all()) - - transient_state = None - try: - transient_state = operation_context.function( - driver, operation_context.n_context, - operation_context.item, *operation_context.args, - **operation_context.kwargs) - except (h_exc.PendingDva, h_exc.DvaNotFound, - h_exc.BrokenInterface, h_exc.DvaCreationFailed, - h_exc.BrokenDva, h_exc.ConfigurationFailed) as ex: - LOG.warning(econ.error_map[type(ex)], ex.message) - except h_exc.DvaDeleteFailed as ex: - LOG.warning(econ.error_map[type(ex)], ex.message) - transient_state = econ.DELETED - finally: - # if the returned transient state is None, no operations - # are required on the DVA status - if transient_state == econ.DELETED: - current_state = driver._delete_vip( - operation_context.n_context, - operation_context.item) - # Error state cannot be reverted - else: - driver._update_vip_graph_state( - operation_context.n_context, - operation_context.item) - except Exception: - LOG.exception(_('Unhandled exception occurred')) diff --git a/neutron/services/loadbalancer/drivers/embrane/agent/lb_operations.py b/neutron/services/loadbalancer/drivers/embrane/agent/lb_operations.py deleted file mode 100644 index f31caf777..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/agent/lb_operations.py +++ /dev/null @@ -1,179 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -import functools - -from heleosapi import exceptions as h_exc - -from neutron.openstack.common import log as logging -from neutron.services.loadbalancer import constants as lcon -from neutron.services.loadbalancer.drivers.embrane import constants as econ - -LOG = logging.getLogger(__name__) -handlers = {} - - -def handler(event, handler): - def wrap(f): - if event not in handler.keys(): - handler[event] = [f] - else: - handler[event].append(f) - - @functools.wraps(f) - def wrapped_f(*args, **kwargs): - return f(*args, **kwargs) - - return wrapped_f - - return wrap - - -@handler(econ.Events.CREATE_VIP, handlers) -def _provision_load_balancer(driver, context, vip, flavor, - vip_utif_info, vip_ip_allocation_info, - pool_utif_info=None, - pool_ip_allocation_info=None, - pool=None, members=None, - monitors=None): - api = driver._heleos_api - tenant_id = context.tenant_id - admin_state = vip["admin_state_up"] - # Architectural configuration - api.create_load_balancer(tenant_id=tenant_id, - router_id=vip["id"], - name=vip["name"], - flavor=flavor, - up=False) - api.grow_interface(vip_utif_info, False, tenant_id, vip["id"]) - if pool: - api.grow_interface(pool_utif_info, False, tenant_id, - vip["id"]) - - # Logical configuration - api.allocate_address(vip["id"], True, vip_ip_allocation_info) - if pool: - api.allocate_address(vip["id"], True, pool_ip_allocation_info) - dva = api.configure_load_balancer(vip["id"], admin_state, - vip, pool, - monitors, members) - return api.extract_dva_state(dva) - - -@handler(econ.Events.UPDATE_VIP, handlers) -def _update_load_balancer(driver, context, vip, - old_pool_id=None, old_port_id=None, - removed_ip=None, pool_utif_info=None, - pool_ip_allocation_info=None, - new_pool=None, members=None, - monitors=None): - api = driver._heleos_api - tenant_id = context.tenant_id - admin_state = vip["admin_state_up"] - - if old_pool_id: - # Architectural Changes - api.de_allocate_address(vip['id'], False, old_port_id, removed_ip) - api.shrink_interface(tenant_id, vip["id"], False, old_port_id) - api.grow_interface(pool_utif_info, False, tenant_id, vip["id"]) - # Configuration Changes - api.allocate_address(vip["id"], True, pool_ip_allocation_info) - api.replace_pool(vip["id"], True, vip, old_pool_id, - new_pool, monitors, members) - - api.update_vservice(vip["id"], True, vip) - # Dva update - dva = api.update_dva(tenant_id, vip["id"], vip["name"], - admin_state, description=vip["description"]) - - return api.extract_dva_state(dva) - - -@handler(econ.Events.DELETE_VIP, handlers) -def _delete_load_balancer(driver, context, vip): - try: - driver._heleos_api.delete_dva(context.tenant_id, vip['id']) - except h_exc.DvaNotFound: - LOG.warning(_('The load balancer %s had no physical representation, ' - 'likely already deleted'), vip['id']) - return econ.DELETED - - -@handler(econ.Events.UPDATE_POOL, handlers) -def _update_server_pool(driver, context, vip, pool, - monitors=None): - api = driver._heleos_api - cookie = ((vip.get('session_persistence') or {}).get('type') == - lcon.SESSION_PERSISTENCE_HTTP_COOKIE) - return api.extract_dva_state(api.update_pool(vip['id'], - vip['admin_state_up'], - pool, cookie, monitors)) - - -@handler(econ.Events.ADD_OR_UPDATE_MEMBER, handlers) -def _add_or_update_pool_member(driver, context, vip, member, protocol): - api = driver._heleos_api - return api.extract_dva_state(api.update_backend_server( - vip['id'], vip['admin_state_up'], member, protocol)) - - -@handler(econ.Events.REMOVE_MEMBER, handlers) -def _remove_member_from_pool(driver, context, vip, member): - api = driver._heleos_api - return api.extract_dva_state(api.remove_pool_member(vip['id'], - vip['admin_state_up'], - member)) - - -@handler(econ.Events.DELETE_MEMBER, handlers) -def _delete_member(driver, context, vip, member): - with context.session.begin(subtransactions=True): - api = driver._heleos_api - dva = api.delete_backend_server(vip['id'], vip['admin_state_up'], - member) - driver._delete_member(context, member) - return api.extract_dva_state(dva) - - -@handler(econ.Events.ADD_POOL_HM, handlers) -def _create_pool_hm(driver, context, vip, hm, pool_id): - api = driver._heleos_api - return api.extract_dva_state(api.add_pool_monitor( - vip['id'], vip['admin_state_up'], hm, pool_id)) - - -@handler(econ.Events.UPDATE_POOL_HM, handlers) -def _update_pool_hm(driver, context, vip, hm, pool_id): - api = driver._heleos_api - return api.extract_dva_state(api.update_pool_monitor( - vip['id'], vip['admin_state_up'], hm, pool_id)) - - -@handler(econ.Events.DELETE_POOL_HM, handlers) -def _delete_pool_hm(driver, context, vip, hm, pool_id): - with context.session.begin(subtransactions=True): - api = driver._heleos_api - dva = api.add_pool_monitor(vip['id'], vip['admin_state_up'], - hm, pool_id) - driver._delete_pool_hm(context, hm, pool_id) - return api.extract_dva_state(dva) - - -@handler(econ.Events.POLL_GRAPH, handlers) -def _poll_graph(driver, context, vip): - api = driver._heleos_api - return api.extract_dva_state(api.get_dva(vip['id'])) diff --git a/neutron/services/loadbalancer/drivers/embrane/config.py b/neutron/services/loadbalancer/drivers/embrane/config.py deleted file mode 100644 index cac9a63d5..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/config.py +++ /dev/null @@ -1,53 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -from oslo.config import cfg - -# User may want to use LB service together with the L3 plugin, but using -# different resources. The service will inherit the configuration from the -# L3 heleos plugin if present and not overridden. -heleos_opts = [ - cfg.StrOpt('esm_mgmt', - help=_('ESM management root address')), - cfg.StrOpt('admin_username', - help=_('ESM admin username.')), - cfg.StrOpt('admin_password', - secret=True, - help=_('ESM admin password.')), - cfg.StrOpt('lb_image', - help=_('Load Balancer image id (Embrane LB)')), - cfg.StrOpt('inband_id', - help=_('In band Security Zone id for LBs')), - cfg.StrOpt('oob_id', - help=_('Out of band Security Zone id for LBs')), - cfg.StrOpt('mgmt_id', - help=_('Management Security Zone id for LBs')), - cfg.StrOpt('dummy_utif_id', - help=_('Dummy user traffic Security Zone id for LBs')), - cfg.StrOpt('resource_pool_id', - help=_('Shared resource pool id')), - cfg.StrOpt('lb_flavor', default="small", - help=_('choose LB image flavor to use, accepted values: small, ' - 'medium')), - cfg.IntOpt('sync_interval', default=60, - help=_('resource synchronization interval in seconds')), - cfg.BoolOpt('async_requests', - help=_('Define if the requests have ' - 'run asynchronously or not')), -] - -cfg.CONF.register_opts(heleos_opts, 'heleoslb') diff --git a/neutron/services/loadbalancer/drivers/embrane/constants.py b/neutron/services/loadbalancer/drivers/embrane/constants.py deleted file mode 100644 index 61e609598..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/constants.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -from heleosapi import constants as h_con -from heleosapi import exceptions as h_exc - -from neutron.plugins.common import constants as ccon - -DELETED = 'DELETED' # not visible status -QUEUE_TIMEOUT = 300 -BACK_SUB_LIMIT = 6 - - -class BackendActions: - UPDATE = 'update' - GROW = 'grow' - REMOVE = 'remove' - SHRINK = 'shrink' - - -class Events: - CREATE_VIP = 'create_vip' - UPDATE_VIP = 'update_vip' - DELETE_VIP = 'delete_vip' - UPDATE_POOL = 'update_pool' - UPDATE_MEMBER = 'update_member' - ADD_OR_UPDATE_MEMBER = 'add_or_update_member' - REMOVE_MEMBER = 'remove_member' - DELETE_MEMBER = 'delete_member' - POLL_GRAPH = 'poll_graph' - ADD_POOL_HM = "create_pool_hm" - UPDATE_POOL_HM = "update_pool_hm" - DELETE_POOL_HM = "delete_pool_hm" - - -_DVA_PENDING_ERROR_MSG = _('Dva is pending for the following reason: %s') -_DVA_NOT_FOUNT_ERROR_MSG = _('%s, ' - 'probably was cancelled through the heleos UI') -_DVA_BROKEN_ERROR_MSG = _('Dva seems to be broken for reason %s') -_DVA_CREATION_FAILED_ERROR_MSG = _('Dva creation failed reason %s') -_DVA_CREATION_PENDING_ERROR_MSG = _('Dva creation is in pending state ' - 'for reason %s') -_CFG_FAILED_ERROR_MSG = _('Dva configuration failed for reason %s') -_DVA_DEL_FAILED_ERROR_MSG = _('Failed to delete the backend ' - 'load balancer for reason %s. Please remove ' - 'it manually through the heleos UI') -NO_MEMBER_SUBNET_WARN = _('No subnet is associated to member %s (required ' - 'to identify the proper load balancer port)') - -error_map = {h_exc.PendingDva: _DVA_PENDING_ERROR_MSG, - h_exc.DvaNotFound: _DVA_NOT_FOUNT_ERROR_MSG, - h_exc.BrokenDva: _DVA_BROKEN_ERROR_MSG, - h_exc.DvaCreationFailed: _DVA_CREATION_FAILED_ERROR_MSG, - h_exc.DvaCreationPending: _DVA_CREATION_PENDING_ERROR_MSG, - h_exc.ConfigurationFailed: _CFG_FAILED_ERROR_MSG, - h_exc.DvaDeleteFailed: _DVA_DEL_FAILED_ERROR_MSG} - -state_map = {h_con.DvaState.POWER_ON: ccon.ACTIVE, - None: ccon.ERROR, - DELETED: DELETED} diff --git a/neutron/services/loadbalancer/drivers/embrane/db.py b/neutron/services/loadbalancer/drivers/embrane/db.py deleted file mode 100644 index 005fbe09a..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/db.py +++ /dev/null @@ -1,56 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -import neutron.db.api as db -from neutron.db import models_v2 as nmodel -from neutron.services.loadbalancer.drivers.embrane import models - - -def initialize(): - db.configure_db() - - -def add_pool_port(context, pool_id, port_id): - session = context.session - with session.begin(subtransactions=True): - pool_port = models.PoolPort() - pool_port.pool_id = pool_id - pool_port.port_id = port_id - session.add(pool_port) - - -def get_pool_port(context, pool_id): - return (context.session.query(models.PoolPort).filter_by(pool_id=pool_id). - first()) - - -def delete_pool_backend(context, pool_id): - session = context.session - backend = (session.query(models.PoolPort).filter_by( - pool_id=pool_id)) - for b in backend: - delete_pool_port(context, b) - - -def delete_pool_port(context, backend_port): - session = context.session - with session.begin(subtransactions=True): - port = (session.query(nmodel.Port).filter_by( - id=backend_port['port_id'])).first() - if port: - session.delete(backend_port) - session.delete(port) diff --git a/neutron/services/loadbalancer/drivers/embrane/driver.py b/neutron/services/loadbalancer/drivers/embrane/driver.py deleted file mode 100644 index 83497b32c..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/driver.py +++ /dev/null @@ -1,342 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -from heleosapi import backend_operations as h_op -from heleosapi import constants as h_con -from heleosapi import info as h_info -from oslo.config import cfg - -from neutron.api.v2 import attributes -from neutron.common import exceptions as n_exc -from neutron.db.loadbalancer import loadbalancer_db as ldb -from neutron.extensions import loadbalancer as lb_ext -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants as pcon -from neutron.plugins.embrane.common import contexts as embrane_ctx -from neutron.plugins.embrane.common import exceptions as h_exc -from neutron.plugins.embrane.common import utils -from neutron.services.loadbalancer import constants as lbcon -from neutron.services.loadbalancer.drivers import abstract_driver -from neutron.services.loadbalancer.drivers.embrane.agent import dispatcher -from neutron.services.loadbalancer.drivers.embrane import config # noqa -from neutron.services.loadbalancer.drivers.embrane import constants as econ -from neutron.services.loadbalancer.drivers.embrane import db as edb -from neutron.services.loadbalancer.drivers.embrane import poller - -LOG = logging.getLogger(__name__) -conf = cfg.CONF.heleoslb -confh = {} - -try: - confh = cfg.CONF.heleos -except cfg.NoSuchOptError: - pass - - -def get_conf(x): - try: - return conf.get(x) or confh.get(x) - except cfg.NoSuchOptError: - return - - -class EmbraneLbaas(abstract_driver.LoadBalancerAbstractDriver): - def __init__(self, plugin): - edb.initialize() - config_esm_mgmt = get_conf('esm_mgmt') - config_admin_username = get_conf('admin_username') - config_admin_password = get_conf('admin_password') - config_lb_image_id = get_conf('lb_image') - config_security_zones = {h_con.SzType.IB: get_conf('inband_id'), - h_con.SzType.OOB: get_conf('oob_id'), - h_con.SzType.MGMT: get_conf('mgmt_id'), - h_con.SzType.DUMMY: get_conf('dummy_utif_id')} - config_resource_pool = get_conf('resource_pool_id') - self._heleos_api = h_op.BackendOperations( - esm_mgmt=config_esm_mgmt, - admin_username=config_admin_username, - admin_password=config_admin_password, - lb_image_id=config_lb_image_id, - security_zones=config_security_zones, - resource_pool=config_resource_pool) - self._dispatcher = dispatcher.Dispatcher( - self, get_conf("async_requests")) - self.plugin = plugin - poll_interval = conf.get('sync_interval') - if poll_interval > 0: - self._loop_call = poller.Poller(self) - self._loop_call.start_polling(conf.get('sync_interval')) - self._flavor = get_conf('lb_flavor') - - def _validate_vip(self, vip): - if vip.get('connection_limit') and vip['connection_limit'] != -1: - raise h_exc.UnsupportedException( - err_msg=_('Connection limit is not supported by Embrane LB')) - persistance = vip.get('session_persistence') - if (persistance and persistance.get('type') == - lbcon.SESSION_PERSISTENCE_APP_COOKIE): - p_type = vip['session_persistence']['type'] - raise h_exc.UnsupportedException( - err_msg=_('Session persistence %s ' - 'not supported by Embrane LBaaS') % p_type) - - def _delete_vip(self, context, vip): - with context.session.begin(subtransactions=True): - self.plugin._delete_db_vip(context, vip['id']) - return econ.DELETED - - def _delete_member(self, context, member): - self.plugin._delete_db_member(context, member['id']) - - def _delete_pool_hm(self, context, health_monitor, pool_id): - self.plugin._delete_db_pool_health_monitor(context, - health_monitor['id'], - pool_id) - - def _update_vip_graph_state(self, context, vip): - self._heleos_api.update_vip_status(vip) - self.plugin.update_status(context, ldb.Vip, vip['id'], - vip['status']) - if vip['status'] != pcon.ERROR: - pool = self.plugin.get_pool(context, vip['pool_id']) - pool_members = pool['members'] - # Manages possible manual changes and monitor actions - self._heleos_api.update_pool_status(vip['id'], pool) - self._heleos_api.update_members_status(vip['id'], pool['id'], - pool_members) - self.plugin.update_status(context, ldb.Pool, pool['id'], - pool['status']) - for member in pool_members: - self.plugin.update_status(context, ldb.Member, - member['id'], member['status']) - - def _create_backend_port(self, context, db_pool): - try: - subnet = self.plugin._core_plugin.get_subnet(context, - db_pool["subnet_id"]) - except n_exc.SubnetNotFound: - LOG.warning(_("Subnet assigned to pool %s doesn't exist, " - "backend port can't be created"), db_pool['id']) - return - - fixed_ip = {'subnet_id': subnet['id'], - 'fixed_ips': attributes.ATTR_NOT_SPECIFIED} - - port_data = { - 'tenant_id': db_pool['tenant_id'], - 'name': 'pool-' + db_pool['id'], - 'network_id': subnet['network_id'], - 'mac_address': attributes.ATTR_NOT_SPECIFIED, - 'admin_state_up': False, - 'device_id': '', - 'device_owner': '', - 'fixed_ips': [fixed_ip] - } - - port = self.plugin._core_plugin.create_port(context, - {'port': port_data}) - return edb.add_pool_port(context, db_pool['id'], port['id']) - - def _retrieve_utif_info(self, context, neutron_port): - network = self.plugin._core_plugin.get_network( - context, neutron_port['network_id']) - result = h_info.UtifInfo(network.get('provider:segmentation_id'), - network['name'], - network['id'], - False, - network['tenant_id'], - neutron_port['id'], - neutron_port['mac_address'], - network.get('provider:network_type')) - return result - - def create_vip(self, context, vip): - self._validate_vip(vip) - db_vip = self.plugin.populate_vip_graph(context, vip) - vip_port = self.plugin._core_plugin._get_port(context, - db_vip['port_id']) - vip_utif_info = self._retrieve_utif_info(context, vip_port) - vip_ip_allocation_info = utils.retrieve_ip_allocation_info( - context, vip_port) - vip_ip_allocation_info.is_gw = True - db_pool = pool_utif_info = pool_ip_allocation_info = None - members = monitors = [] - if db_vip['pool_id']: - db_pool = self.plugin.get_pool( - context, db_vip['pool_id']) - pool_port = edb.get_pool_port(context, db_pool["id"]) - if pool_port: - db_port = self.plugin._core_plugin._get_port( - context, pool_port["port_id"]) - pool_utif_info = self._retrieve_utif_info(context, db_port) - pool_ip_allocation_info = utils.retrieve_ip_allocation_info( - context, db_port) - members = self.plugin.get_members( - context, filters={'id': db_pool['members']}) - monitors = self.plugin.get_members( - context, filters={'id': db_pool['health_monitors']}) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext(econ.Events.CREATE_VIP, - db_vip, context, None), - self._flavor, vip_utif_info, vip_ip_allocation_info, - pool_utif_info, pool_ip_allocation_info, db_pool, members, - monitors) - - def update_vip(self, context, old_vip, vip): - new_pool = old_port_id = removed_ip = None - new_pool_utif = new_pool_ip_allocation = None - old_pool = {} - members = monitors = [] - if old_vip['pool_id'] != vip['pool_id']: - new_pool = self.plugin.get_pool( - context, vip['pool_id']) - members = self.plugin.get_members( - context, filters={'id': new_pool['members']}) - monitors = self.plugin.get_members( - context, filters={'id': new_pool['health_monitors']}) - new_pool_port = edb.get_pool_port(context, new_pool["id"]) - if new_pool_port: - db_port = self.plugin._core_plugin._get_port( - context, new_pool_port["port_id"]) - new_pool_utif = self._retrieve_utif_info(context, db_port) - new_pool_ip_allocation = utils.retrieve_ip_allocation_info( - context, db_port) - old_pool = self.plugin.get_pool( - context, old_vip['pool_id']) - old_pool_port = edb.get_pool_port(context, old_pool["id"]) - if old_pool_port: - old_port = self.plugin._core_plugin._get_port( - context, old_pool_port['port_id']) - # remove that subnet ip - removed_ip = old_port['fixed_ips'][0]['ip_address'] - old_port_id = old_port['id'] - - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext(econ.Events.UPDATE_VIP, vip, - context, None), - old_pool.get('id'), old_port_id, removed_ip, new_pool_utif, - new_pool_ip_allocation, new_pool, members, monitors) - - def delete_vip(self, context, vip): - db_vip = self.plugin.populate_vip_graph(context, vip) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.DELETE_VIP, db_vip, context, None)) - - def create_pool(self, context, pool): - if pool['subnet_id']: - self._create_backend_port(context, pool) - - def update_pool(self, context, old_pool, pool): - with context.session.begin(subtransactions=True): - if old_pool['vip_id']: - try: - db_vip = self.plugin._get_resource( - context, ldb.Vip, old_pool['vip_id']) - except lb_ext.VipNotFound: - return - monitors = self.plugin.get_members( - context, filters={'id': old_pool['health_monitors']}) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext(econ.Events.UPDATE_POOL, - db_vip, context, None), - pool, monitors) - - def delete_pool(self, context, pool): - edb.delete_pool_backend(context, pool['id']) - self.plugin._delete_db_pool(context, pool['id']) - - def create_member(self, context, member): - db_pool = self.plugin.get_pool(context, member['pool_id']) - if db_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.ADD_OR_UPDATE_MEMBER, db_vip, context, None), - member, db_pool['protocol']) - - def update_member(self, context, old_member, member): - db_pool = self.plugin.get_pool(context, member['pool_id']) - if member['pool_id'] != old_member['pool_id']: - old_pool = self.plugin.get_pool(context, old_member['pool_id']) - if old_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - old_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.REMOVE_MEMBER, db_vip, context, None), - old_member) - if db_pool['vip_id']: - db_vip = self.plugin._get_resource( - context, ldb.Vip, db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.ADD_OR_UPDATE_MEMBER, db_vip, context, None), - member, db_pool['protocol']) - - def delete_member(self, context, member): - db_pool = self.plugin.get_pool(context, member['pool_id']) - if db_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.DELETE_MEMBER, db_vip, context, None), - member) - else: - self._delete_member(context, member) - - def stats(self, context, pool_id): - return {'bytes_in': 0, - 'bytes_out': 0, - 'active_connections': 0, - 'total_connections': 0} - - def create_pool_health_monitor(self, context, health_monitor, pool_id): - db_pool = self.plugin.get_pool(context, pool_id) - # API call only if vip exists - if db_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.ADD_POOL_HM, db_vip, context, None), - health_monitor, pool_id) - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, pool_id): - db_pool = self.plugin.get_pool(context, pool_id) - if db_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.UPDATE_POOL_HM, db_vip, context, None), - health_monitor, pool_id) - - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - db_pool = self.plugin.get_pool(context, pool_id) - if db_pool['vip_id']: - db_vip = self.plugin._get_resource(context, ldb.Vip, - db_pool['vip_id']) - self._dispatcher.dispatch_lb( - embrane_ctx.DispatcherContext( - econ.Events.DELETE_POOL_HM, db_vip, context, None), - health_monitor, pool_id) - else: - self._delete_pool_hm(context, health_monitor, pool_id) diff --git a/neutron/services/loadbalancer/drivers/embrane/models.py b/neutron/services/loadbalancer/drivers/embrane/models.py deleted file mode 100644 index 51adfcf24..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/models.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -import sqlalchemy as sql - -from neutron.db import model_base - - -class PoolPort(model_base.BASEV2): - """Represents the connection between pools and ports.""" - __tablename__ = 'embrane_pool_port' - - pool_id = sql.Column(sql.String(36), sql.ForeignKey('pools.id'), - primary_key=True) - port_id = sql.Column(sql.String(36), sql.ForeignKey('ports.id'), - nullable=False) diff --git a/neutron/services/loadbalancer/drivers/embrane/poller.py b/neutron/services/loadbalancer/drivers/embrane/poller.py deleted file mode 100644 index bf36079cf..000000000 --- a/neutron/services/loadbalancer/drivers/embrane/poller.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright 2014 Embrane, 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. -# -# @author: Ivar Lazzaro, Embrane, Inc. ivar@embrane.com - -from heleosapi import exceptions as h_exc - -from neutron import context -from neutron.db.loadbalancer import loadbalancer_db as ldb -from neutron.db import servicetype_db as sdb -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.plugins.common import constants as ccon -from neutron.plugins.embrane.common import contexts as embrane_ctx -from neutron.services.loadbalancer.drivers.embrane import constants as econ - -LOG = logging.getLogger(__name__) -skip_states = [ccon.PENDING_CREATE, - ccon.PENDING_DELETE, - ccon.PENDING_UPDATE, - ccon.ERROR] - - -class Poller(object): - def __init__(self, driver): - self.dispatcher = driver._dispatcher - service_type_manager = sdb.ServiceTypeManager.get_instance() - self.provider = (service_type_manager.get_service_providers( - None, filters={ - 'service_type': [ccon.LOADBALANCER], - 'driver': ['neutron.services.loadbalancer.drivers.' - 'embrane.driver.EmbraneLbaas']}))[0]['name'] - - def start_polling(self, interval): - loop_call = loopingcall.FixedIntervalLoopingCall(self._run) - loop_call.start(interval=interval) - return loop_call - - def _run(self): - ctx = context.get_admin_context() - try: - self.synchronize_vips(ctx) - except h_exc.PollingException as e: - LOG.exception(_('Unhandled exception occurred'), e) - - def synchronize_vips(self, ctx): - session = ctx.session - vips = session.query(ldb.Vip).join( - sdb.ProviderResourceAssociation, - sdb.ProviderResourceAssociation.resource_id == - ldb.Vip.pool_id).filter( - sdb.ProviderResourceAssociation.provider_name == self.provider) - # No need to check pending states - for vip in vips: - if vip['status'] not in skip_states: - self.dispatcher.dispatch_lb( - d_context=embrane_ctx.DispatcherContext( - econ.Events.POLL_GRAPH, vip, ctx, None), - args=()) diff --git a/neutron/services/loadbalancer/drivers/haproxy/__init__.py b/neutron/services/loadbalancer/drivers/haproxy/__init__.py deleted file mode 100644 index ce18bf6d6..000000000 --- a/neutron/services/loadbalancer/drivers/haproxy/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost diff --git a/neutron/services/loadbalancer/drivers/haproxy/cfg.py b/neutron/services/loadbalancer/drivers/haproxy/cfg.py deleted file mode 100644 index 815334f81..000000000 --- a/neutron/services/loadbalancer/drivers/haproxy/cfg.py +++ /dev/null @@ -1,238 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost - -import itertools -from six import moves - -from neutron.agent.linux import utils -from neutron.plugins.common import constants as qconstants -from neutron.services.loadbalancer import constants - - -PROTOCOL_MAP = { - constants.PROTOCOL_TCP: 'tcp', - constants.PROTOCOL_HTTP: 'http', - constants.PROTOCOL_HTTPS: 'tcp', -} - -BALANCE_MAP = { - constants.LB_METHOD_ROUND_ROBIN: 'roundrobin', - constants.LB_METHOD_LEAST_CONNECTIONS: 'leastconn', - constants.LB_METHOD_SOURCE_IP: 'source' -} - -STATS_MAP = { - constants.STATS_ACTIVE_CONNECTIONS: 'scur', - constants.STATS_MAX_CONNECTIONS: 'smax', - constants.STATS_CURRENT_SESSIONS: 'scur', - constants.STATS_MAX_SESSIONS: 'smax', - constants.STATS_TOTAL_CONNECTIONS: 'stot', - constants.STATS_TOTAL_SESSIONS: 'stot', - constants.STATS_IN_BYTES: 'bin', - constants.STATS_OUT_BYTES: 'bout', - constants.STATS_CONNECTION_ERRORS: 'econ', - constants.STATS_RESPONSE_ERRORS: 'eresp' -} - -ACTIVE_PENDING_STATUSES = qconstants.ACTIVE_PENDING_STATUSES -INACTIVE = qconstants.INACTIVE - - -def save_config(conf_path, logical_config, socket_path=None, - user_group='nogroup'): - """Convert a logical configuration to the HAProxy version.""" - data = [] - data.extend(_build_global(logical_config, socket_path=socket_path, - user_group=user_group)) - data.extend(_build_defaults(logical_config)) - data.extend(_build_frontend(logical_config)) - data.extend(_build_backend(logical_config)) - utils.replace_file(conf_path, '\n'.join(data)) - - -def _build_global(config, socket_path=None, user_group='nogroup'): - opts = [ - 'daemon', - 'user nobody', - 'group %s' % user_group, - 'log /dev/log local0', - 'log /dev/log local1 notice' - ] - - if socket_path: - opts.append('stats socket %s mode 0666 level user' % socket_path) - - return itertools.chain(['global'], ('\t' + o for o in opts)) - - -def _build_defaults(config): - opts = [ - 'log global', - 'retries 3', - 'option redispatch', - 'timeout connect 5000', - 'timeout client 50000', - 'timeout server 50000', - ] - - return itertools.chain(['defaults'], ('\t' + o for o in opts)) - - -def _build_frontend(config): - protocol = config['vip']['protocol'] - - opts = [ - 'option tcplog', - 'bind %s:%d' % ( - _get_first_ip_from_port(config['vip']['port']), - config['vip']['protocol_port'] - ), - 'mode %s' % PROTOCOL_MAP[protocol], - 'default_backend %s' % config['pool']['id'], - ] - - if config['vip']['connection_limit'] >= 0: - opts.append('maxconn %s' % config['vip']['connection_limit']) - - if protocol == constants.PROTOCOL_HTTP: - opts.append('option forwardfor') - - return itertools.chain( - ['frontend %s' % config['vip']['id']], - ('\t' + o for o in opts) - ) - - -def _build_backend(config): - protocol = config['pool']['protocol'] - lb_method = config['pool']['lb_method'] - - opts = [ - 'mode %s' % PROTOCOL_MAP[protocol], - 'balance %s' % BALANCE_MAP.get(lb_method, 'roundrobin') - ] - - if protocol == constants.PROTOCOL_HTTP: - opts.append('option forwardfor') - - # add the first health_monitor (if available) - server_addon, health_opts = _get_server_health_option(config) - opts.extend(health_opts) - - # add session persistence (if available) - persist_opts = _get_session_persistence(config) - opts.extend(persist_opts) - - # add the members - for member in config['members']: - if ((member['status'] in ACTIVE_PENDING_STATUSES or - member['status'] == INACTIVE) - and member['admin_state_up']): - server = (('server %(id)s %(address)s:%(protocol_port)s ' - 'weight %(weight)s') % member) + server_addon - if _has_http_cookie_persistence(config): - server += ' cookie %d' % config['members'].index(member) - opts.append(server) - - return itertools.chain( - ['backend %s' % config['pool']['id']], - ('\t' + o for o in opts) - ) - - -def _get_first_ip_from_port(port): - for fixed_ip in port['fixed_ips']: - return fixed_ip['ip_address'] - - -def _get_server_health_option(config): - """return the first active health option.""" - for monitor in config['healthmonitors']: - # not checking the status of healthmonitor for two reasons: - # 1) status field is absent in HealthMonitor model - # 2) only active HealthMonitors are fetched with - # LoadBalancerCallbacks.get_logical_device - if monitor['admin_state_up']: - break - else: - return '', [] - - server_addon = ' check inter %(delay)ds fall %(max_retries)d' % monitor - opts = [ - 'timeout check %ds' % monitor['timeout'] - ] - - if monitor['type'] in (constants.HEALTH_MONITOR_HTTP, - constants.HEALTH_MONITOR_HTTPS): - opts.append('option httpchk %(http_method)s %(url_path)s' % monitor) - opts.append( - 'http-check expect rstatus %s' % - '|'.join(_expand_expected_codes(monitor['expected_codes'])) - ) - - if monitor['type'] == constants.HEALTH_MONITOR_HTTPS: - opts.append('option ssl-hello-chk') - - return server_addon, opts - - -def _get_session_persistence(config): - persistence = config['vip'].get('session_persistence') - if not persistence: - return [] - - opts = [] - if persistence['type'] == constants.SESSION_PERSISTENCE_SOURCE_IP: - opts.append('stick-table type ip size 10k') - opts.append('stick on src') - elif (persistence['type'] == constants.SESSION_PERSISTENCE_HTTP_COOKIE and - config.get('members')): - opts.append('cookie SRV insert indirect nocache') - elif (persistence['type'] == constants.SESSION_PERSISTENCE_APP_COOKIE and - persistence.get('cookie_name')): - opts.append('appsession %s len 56 timeout 3h' % - persistence['cookie_name']) - - return opts - - -def _has_http_cookie_persistence(config): - return (config['vip'].get('session_persistence') and - config['vip']['session_persistence']['type'] == - constants.SESSION_PERSISTENCE_HTTP_COOKIE) - - -def _expand_expected_codes(codes): - """Expand the expected code string in set of codes. - - 200-204 -> 200, 201, 202, 204 - 200, 203 -> 200, 203 - """ - - retval = set() - for code in codes.replace(',', ' ').split(' '): - code = code.strip() - - if not code: - continue - elif '-' in code: - low, hi = code.split('-')[:2] - retval.update(str(i) for i in moves.xrange(int(low), int(hi) + 1)) - else: - retval.add(code) - return retval diff --git a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py b/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py deleted file mode 100644 index b15f7d864..000000000 --- a/neutron/services/loadbalancer/drivers/haproxy/namespace_driver.py +++ /dev/null @@ -1,396 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 New Dream Network, LLC (DreamHost) -# -# 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. -# -# @author: Mark McClain, DreamHost -import os -import shutil -import socket - -import netaddr -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent.linux import ip_lib -from neutron.agent.linux import utils -from neutron.common import exceptions -from neutron.common import utils as n_utils -from neutron.openstack.common import excutils -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.loadbalancer.agent import agent_device_driver -from neutron.services.loadbalancer import constants as lb_const -from neutron.services.loadbalancer.drivers.haproxy import cfg as hacfg - -LOG = logging.getLogger(__name__) -NS_PREFIX = 'qlbaas-' -DRIVER_NAME = 'haproxy_ns' - -STATE_PATH_DEFAULT = '$state_path/lbaas' -USER_GROUP_DEFAULT = 'nogroup' -OPTS = [ - cfg.StrOpt( - 'loadbalancer_state_path', - default=STATE_PATH_DEFAULT, - help=_('Location to store config and state files'), - deprecated_opts=[cfg.DeprecatedOpt('loadbalancer_state_path')], - ), - cfg.StrOpt( - 'user_group', - default=USER_GROUP_DEFAULT, - help=_('The user group'), - deprecated_opts=[cfg.DeprecatedOpt('user_group')], - ), - cfg.IntOpt( - 'send_gratuitous_arp', - default=3, - help=_('When delete and re-add the same vip, send this many ' - 'gratuitous ARPs to flush the ARP cache in the Router. ' - 'Set it below or equal to 0 to disable this feature.'), - ) -] -cfg.CONF.register_opts(OPTS, 'haproxy') - - -class HaproxyNSDriver(agent_device_driver.AgentDeviceDriver): - def __init__(self, conf, plugin_rpc): - self.conf = conf - self.root_helper = config.get_root_helper(conf) - self.state_path = conf.haproxy.loadbalancer_state_path - try: - vif_driver = importutils.import_object(conf.interface_driver, conf) - except ImportError: - with excutils.save_and_reraise_exception(): - msg = (_('Error importing interface driver: %s') - % conf.haproxy.interface_driver) - LOG.error(msg) - - self.vif_driver = vif_driver - self.plugin_rpc = plugin_rpc - self.pool_to_port_id = {} - - @classmethod - def get_name(cls): - return DRIVER_NAME - - def create(self, logical_config): - pool_id = logical_config['pool']['id'] - namespace = get_ns_name(pool_id) - - self._plug(namespace, logical_config['vip']['port']) - self._spawn(logical_config) - - def update(self, logical_config): - pool_id = logical_config['pool']['id'] - pid_path = self._get_state_file_path(pool_id, 'pid') - - extra_args = ['-sf'] - extra_args.extend(p.strip() for p in open(pid_path, 'r')) - self._spawn(logical_config, extra_args) - - def _spawn(self, logical_config, extra_cmd_args=()): - pool_id = logical_config['pool']['id'] - namespace = get_ns_name(pool_id) - conf_path = self._get_state_file_path(pool_id, 'conf') - pid_path = self._get_state_file_path(pool_id, 'pid') - sock_path = self._get_state_file_path(pool_id, 'sock') - user_group = self.conf.haproxy.user_group - - hacfg.save_config(conf_path, logical_config, sock_path, user_group) - cmd = ['haproxy', '-f', conf_path, '-p', pid_path] - cmd.extend(extra_cmd_args) - - ns = ip_lib.IPWrapper(self.root_helper, namespace) - ns.netns.execute(cmd) - - # remember the pool<>port mapping - self.pool_to_port_id[pool_id] = logical_config['vip']['port']['id'] - - @n_utils.synchronized('haproxy-driver') - def undeploy_instance(self, pool_id, cleanup_namespace=False): - namespace = get_ns_name(pool_id) - ns = ip_lib.IPWrapper(self.root_helper, namespace) - pid_path = self._get_state_file_path(pool_id, 'pid') - - # kill the process - kill_pids_in_file(self.root_helper, pid_path) - - # unplug the ports - if pool_id in self.pool_to_port_id: - self._unplug(namespace, self.pool_to_port_id[pool_id]) - - # delete all devices from namespace; - # used when deleting orphans and port_id is not known for pool_id - if cleanup_namespace: - for device in ns.get_devices(exclude_loopback=True): - self.vif_driver.unplug(device.name, namespace=namespace) - - # remove the configuration directory - conf_dir = os.path.dirname(self._get_state_file_path(pool_id, '')) - if os.path.isdir(conf_dir): - shutil.rmtree(conf_dir) - ns.garbage_collect_namespace() - - def exists(self, pool_id): - namespace = get_ns_name(pool_id) - root_ns = ip_lib.IPWrapper(self.root_helper) - - socket_path = self._get_state_file_path(pool_id, 'sock') - if root_ns.netns.exists(namespace) and os.path.exists(socket_path): - try: - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect(socket_path) - return True - except socket.error: - pass - return False - - def get_stats(self, pool_id): - socket_path = self._get_state_file_path(pool_id, 'sock') - TYPE_BACKEND_REQUEST = 2 - TYPE_SERVER_REQUEST = 4 - if os.path.exists(socket_path): - parsed_stats = self._get_stats_from_socket( - socket_path, - entity_type=TYPE_BACKEND_REQUEST | TYPE_SERVER_REQUEST) - pool_stats = self._get_backend_stats(parsed_stats) - pool_stats['members'] = self._get_servers_stats(parsed_stats) - return pool_stats - else: - LOG.warn(_('Stats socket not found for pool %s'), pool_id) - return {} - - def _get_backend_stats(self, parsed_stats): - TYPE_BACKEND_RESPONSE = '1' - for stats in parsed_stats: - if stats.get('type') == TYPE_BACKEND_RESPONSE: - unified_stats = dict((k, stats.get(v, '')) - for k, v in hacfg.STATS_MAP.items()) - return unified_stats - - return {} - - def _get_servers_stats(self, parsed_stats): - TYPE_SERVER_RESPONSE = '2' - res = {} - for stats in parsed_stats: - if stats.get('type') == TYPE_SERVER_RESPONSE: - res[stats['svname']] = { - lb_const.STATS_STATUS: (constants.INACTIVE - if stats['status'] == 'DOWN' - else constants.ACTIVE), - lb_const.STATS_HEALTH: stats['check_status'], - lb_const.STATS_FAILED_CHECKS: stats['chkfail'] - } - return res - - def _get_stats_from_socket(self, socket_path, entity_type): - try: - s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) - s.connect(socket_path) - s.send('show stat -1 %s -1\n' % entity_type) - raw_stats = '' - chunk_size = 1024 - while True: - chunk = s.recv(chunk_size) - raw_stats += chunk - if len(chunk) < chunk_size: - break - - return self._parse_stats(raw_stats) - except socket.error as e: - LOG.warn(_('Error while connecting to stats socket: %s'), e) - return {} - - def _parse_stats(self, raw_stats): - stat_lines = raw_stats.splitlines() - if len(stat_lines) < 2: - return [] - stat_names = [name.strip('# ') for name in stat_lines[0].split(',')] - res_stats = [] - for raw_values in stat_lines[1:]: - if not raw_values: - continue - stat_values = [value.strip() for value in raw_values.split(',')] - res_stats.append(dict(zip(stat_names, stat_values))) - - return res_stats - - def _get_state_file_path(self, pool_id, kind, ensure_state_dir=True): - """Returns the file name for a given kind of config file.""" - confs_dir = os.path.abspath(os.path.normpath(self.state_path)) - conf_dir = os.path.join(confs_dir, pool_id) - if ensure_state_dir: - if not os.path.isdir(conf_dir): - os.makedirs(conf_dir, 0o755) - return os.path.join(conf_dir, kind) - - def _plug(self, namespace, port, reuse_existing=True): - self.plugin_rpc.plug_vip_port(port['id']) - interface_name = self.vif_driver.get_device_name(Wrap(port)) - - if ip_lib.device_exists(interface_name, self.root_helper, namespace): - if not reuse_existing: - raise exceptions.PreexistingDeviceFailure( - dev_name=interface_name - ) - else: - self.vif_driver.plug( - port['network_id'], - port['id'], - interface_name, - port['mac_address'], - namespace=namespace - ) - - cidrs = [ - '%s/%s' % (ip['ip_address'], - netaddr.IPNetwork(ip['subnet']['cidr']).prefixlen) - for ip in port['fixed_ips'] - ] - self.vif_driver.init_l3(interface_name, cidrs, namespace=namespace) - - gw_ip = port['fixed_ips'][0]['subnet'].get('gateway_ip') - - if not gw_ip: - host_routes = port['fixed_ips'][0]['subnet'].get('host_routes', []) - for host_route in host_routes: - if host_route['destination'] == "0.0.0.0/0": - gw_ip = host_route['nexthop'] - break - - if gw_ip: - cmd = ['route', 'add', 'default', 'gw', gw_ip] - ip_wrapper = ip_lib.IPWrapper(self.root_helper, - namespace=namespace) - ip_wrapper.netns.execute(cmd, check_exit_code=False) - # When delete and re-add the same vip, we need to - # send gratuitous ARP to flush the ARP cache in the Router. - gratuitous_arp = self.conf.haproxy.send_gratuitous_arp - if gratuitous_arp > 0: - for ip in port['fixed_ips']: - cmd_arping = ['arping', '-U', - '-I', interface_name, - '-c', gratuitous_arp, - ip['ip_address']] - ip_wrapper.netns.execute(cmd_arping, check_exit_code=False) - - def _unplug(self, namespace, port_id): - port_stub = {'id': port_id} - self.plugin_rpc.unplug_vip_port(port_id) - interface_name = self.vif_driver.get_device_name(Wrap(port_stub)) - self.vif_driver.unplug(interface_name, namespace=namespace) - - @n_utils.synchronized('haproxy-driver') - def deploy_instance(self, logical_config): - # do actual deploy only if vip and pool are configured and active - if (not logical_config or - 'vip' not in logical_config or - (logical_config['vip']['status'] not in - constants.ACTIVE_PENDING_STATUSES) or - not logical_config['vip']['admin_state_up'] or - (logical_config['pool']['status'] not in - constants.ACTIVE_PENDING_STATUSES) or - not logical_config['pool']['admin_state_up']): - return - - if self.exists(logical_config['pool']['id']): - self.update(logical_config) - else: - self.create(logical_config) - - def _refresh_device(self, pool_id): - logical_config = self.plugin_rpc.get_logical_device(pool_id) - self.deploy_instance(logical_config) - - def create_vip(self, vip): - self._refresh_device(vip['pool_id']) - - def update_vip(self, old_vip, vip): - self._refresh_device(vip['pool_id']) - - def delete_vip(self, vip): - self.undeploy_instance(vip['pool_id']) - - def create_pool(self, pool): - # nothing to do here because a pool needs a vip to be useful - pass - - def update_pool(self, old_pool, pool): - self._refresh_device(pool['id']) - - def delete_pool(self, pool): - # delete_pool may be called before vip deletion in case - # pool's admin state set to down - if self.exists(pool['id']): - self.undeploy_instance(pool['id']) - - def create_member(self, member): - self._refresh_device(member['pool_id']) - - def update_member(self, old_member, member): - self._refresh_device(member['pool_id']) - - def delete_member(self, member): - self._refresh_device(member['pool_id']) - - def create_pool_health_monitor(self, health_monitor, pool_id): - self._refresh_device(pool_id) - - def update_pool_health_monitor(self, old_health_monitor, health_monitor, - pool_id): - self._refresh_device(pool_id) - - def delete_pool_health_monitor(self, health_monitor, pool_id): - self._refresh_device(pool_id) - - def remove_orphans(self, known_pool_ids): - if not os.path.exists(self.state_path): - return - - orphans = (pool_id for pool_id in os.listdir(self.state_path) - if pool_id not in known_pool_ids) - for pool_id in orphans: - if self.exists(pool_id): - self.undeploy_instance(pool_id, cleanup_namespace=True) - - -# NOTE (markmcclain) For compliance with interface.py which expects objects -class Wrap(object): - """A light attribute wrapper for compatibility with the interface lib.""" - def __init__(self, d): - self.__dict__.update(d) - - def __getitem__(self, key): - return self.__dict__[key] - - -def get_ns_name(namespace_id): - return NS_PREFIX + namespace_id - - -def kill_pids_in_file(root_helper, pid_path): - if os.path.exists(pid_path): - with open(pid_path, 'r') as pids: - for pid in pids: - pid = pid.strip() - try: - utils.execute(['kill', '-9', pid], root_helper) - except RuntimeError: - LOG.exception( - _('Unable to kill haproxy process: %s'), - pid - ) diff --git a/neutron/services/loadbalancer/drivers/haproxy/plugin_driver.py b/neutron/services/loadbalancer/drivers/haproxy/plugin_driver.py deleted file mode 100644 index 7dccaa3ac..000000000 --- a/neutron/services/loadbalancer/drivers/haproxy/plugin_driver.py +++ /dev/null @@ -1,23 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright (c) 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutron.services.loadbalancer.drivers.common import agent_driver_base -from neutron.services.loadbalancer.drivers.haproxy import namespace_driver - - -class HaproxyOnHostPluginDriver(agent_driver_base.AgentDriverBase): - device_driver = namespace_driver.DRIVER_NAME diff --git a/neutron/services/loadbalancer/drivers/netscaler/__init__.py b/neutron/services/loadbalancer/drivers/netscaler/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/neutron/services/loadbalancer/drivers/netscaler/ncc_client.py b/neutron/services/loadbalancer/drivers/netscaler/ncc_client.py deleted file mode 100644 index 98c8a35c2..000000000 --- a/neutron/services/loadbalancer/drivers/netscaler/ncc_client.py +++ /dev/null @@ -1,182 +0,0 @@ -# Copyright 2014 Citrix Systems -# -# 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 base64 -import requests - -from neutron.common import exceptions as n_exc -from neutron.openstack.common import jsonutils -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - -CONTENT_TYPE_HEADER = 'Content-type' -ACCEPT_HEADER = 'Accept' -AUTH_HEADER = 'Authorization' -DRIVER_HEADER = 'X-OpenStack-LBaaS' -TENANT_HEADER = 'X-Tenant-ID' -JSON_CONTENT_TYPE = 'application/json' -DRIVER_HEADER_VALUE = 'netscaler-openstack-lbaas' - - -class NCCException(n_exc.NeutronException): - - """Represents exceptions thrown by NSClient.""" - - CONNECTION_ERROR = 1 - REQUEST_ERROR = 2 - RESPONSE_ERROR = 3 - UNKNOWN_ERROR = 4 - - def __init__(self, error): - self.message = _("NCC Error %d") % error - super(NCCException, self).__init__() - self.error = error - - -class NSClient(object): - - """Client to operate on REST resources of NetScaler Control Center.""" - - def __init__(self, service_uri, username, password): - if not service_uri: - msg = _("No NetScaler Control Center URI specified. " - "Cannot connect.") - LOG.exception(msg) - raise NCCException(NCCException.CONNECTION_ERROR) - self.service_uri = service_uri.strip('/') - self.auth = None - if username and password: - base64string = base64.encodestring("%s:%s" % (username, password)) - base64string = base64string[:-1] - self.auth = 'Basic %s' % base64string - - def create_resource(self, tenant_id, resource_path, object_name, - object_data): - """Create a resource of NetScaler Control Center.""" - return self._resource_operation('POST', tenant_id, - resource_path, - object_name=object_name, - object_data=object_data) - - def retrieve_resource(self, tenant_id, resource_path, parse_response=True): - """Retrieve a resource of NetScaler Control Center.""" - return self._resource_operation('GET', tenant_id, resource_path) - - def update_resource(self, tenant_id, resource_path, object_name, - object_data): - """Update a resource of the NetScaler Control Center.""" - return self._resource_operation('PUT', tenant_id, - resource_path, - object_name=object_name, - object_data=object_data) - - def remove_resource(self, tenant_id, resource_path, parse_response=True): - """Remove a resource of NetScaler Control Center.""" - return self._resource_operation('DELETE', tenant_id, resource_path) - - def _resource_operation(self, method, tenant_id, resource_path, - object_name=None, object_data=None): - resource_uri = "%s/%s" % (self.service_uri, resource_path) - headers = self._setup_req_headers(tenant_id) - request_body = None - if object_data: - if isinstance(object_data, str): - request_body = object_data - else: - obj_dict = {object_name: object_data} - request_body = jsonutils.dumps(obj_dict) - - response_status, resp_dict = self._execute_request(method, - resource_uri, - headers, - body=request_body) - return response_status, resp_dict - - def _is_valid_response(self, response_status): - # when status is less than 400, the response is fine - return response_status < requests.codes.bad_request - - def _setup_req_headers(self, tenant_id): - headers = {ACCEPT_HEADER: JSON_CONTENT_TYPE, - CONTENT_TYPE_HEADER: JSON_CONTENT_TYPE, - DRIVER_HEADER: DRIVER_HEADER_VALUE, - TENANT_HEADER: tenant_id, - AUTH_HEADER: self.auth} - return headers - - def _get_response_dict(self, response): - response_dict = {'status': response.status_code, - 'body': response.text, - 'headers': response.headers} - if self._is_valid_response(response.status_code): - if response.text: - response_dict['dict'] = response.json() - return response_dict - - def _execute_request(self, method, resource_uri, headers, body=None): - try: - response = requests.request(method, url=resource_uri, - headers=headers, data=body) - except requests.exceptions.ConnectionError: - msg = (_("Connection error occurred while connecting to %s") % - self.service_uri) - LOG.exception(msg) - raise NCCException(NCCException.CONNECTION_ERROR) - except requests.exceptions.SSLError: - msg = (_("SSL error occurred while connecting to %s") % - self.service_uri) - LOG.exception(msg) - raise NCCException(NCCException.CONNECTION_ERROR) - except requests.exceptions.Timeout: - msg = _("Request to %s timed out") % self.service_uri - LOG.exception(msg) - raise NCCException(NCCException.CONNECTION_ERROR) - except (requests.exceptions.URLRequired, - requests.exceptions.InvalidURL, - requests.exceptions.MissingSchema, - requests.exceptions.InvalidSchema): - msg = _("Request did not specify a valid URL") - LOG.exception(msg) - raise NCCException(NCCException.REQUEST_ERROR) - except requests.exceptions.TooManyRedirects: - msg = _("Too many redirects occurred for request to %s") - LOG.exception(msg) - raise NCCException(NCCException.REQUEST_ERROR) - except requests.exceptions.RequestException: - msg = (_("A request error while connecting to %s") % - self.service_uri) - LOG.exception(msg) - raise NCCException(NCCException.REQUEST_ERROR) - except Exception: - msg = (_("A unknown error occurred during request to %s") % - self.service_uri) - LOG.exception(msg) - raise NCCException(NCCException.UNKNOWN_ERROR) - resp_dict = self._get_response_dict(response) - LOG.debug(_("Response: %s"), resp_dict['body']) - response_status = resp_dict['status'] - if response_status == requests.codes.unauthorized: - LOG.exception(_("Unable to login. Invalid credentials passed." - "for: %s"), self.service_uri) - raise NCCException(NCCException.RESPONSE_ERROR) - if not self._is_valid_response(response_status): - msg = (_("Failed %(method)s operation on %(url)s " - "status code: %(response_status)s") % - {"method": method, - "url": resource_uri, - "response_status": response_status}) - LOG.exception(msg) - raise NCCException(NCCException.RESPONSE_ERROR) - return response_status, resp_dict diff --git a/neutron/services/loadbalancer/drivers/netscaler/netscaler_driver.py b/neutron/services/loadbalancer/drivers/netscaler/netscaler_driver.py deleted file mode 100644 index 9f74d6a73..000000000 --- a/neutron/services/loadbalancer/drivers/netscaler/netscaler_driver.py +++ /dev/null @@ -1,489 +0,0 @@ -# Copyright 2014 Citrix Systems, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from neutron.api.v2 import attributes -from neutron.db.loadbalancer import loadbalancer_db -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.loadbalancer.drivers import abstract_driver -from neutron.services.loadbalancer.drivers.netscaler import ncc_client - -LOG = logging.getLogger(__name__) - -NETSCALER_CC_OPTS = [ - cfg.StrOpt( - 'netscaler_ncc_uri', - help=_('The URL to reach the NetScaler Control Center Server.'), - ), - cfg.StrOpt( - 'netscaler_ncc_username', - help=_('Username to login to the NetScaler Control Center Server.'), - ), - cfg.StrOpt( - 'netscaler_ncc_password', - help=_('Password to login to the NetScaler Control Center Server.'), - ) -] - -cfg.CONF.register_opts(NETSCALER_CC_OPTS, 'netscaler_driver') - -VIPS_RESOURCE = 'vips' -VIP_RESOURCE = 'vip' -POOLS_RESOURCE = 'pools' -POOL_RESOURCE = 'pool' -POOLMEMBERS_RESOURCE = 'members' -POOLMEMBER_RESOURCE = 'member' -MONITORS_RESOURCE = 'healthmonitors' -MONITOR_RESOURCE = 'healthmonitor' -POOLSTATS_RESOURCE = 'statistics' -PROV_SEGMT_ID = 'provider:segmentation_id' -PROV_NET_TYPE = 'provider:network_type' -DRIVER_NAME = 'netscaler_driver' - - -class NetScalerPluginDriver(abstract_driver.LoadBalancerAbstractDriver): - - """NetScaler LBaaS Plugin driver class.""" - - def __init__(self, plugin): - self.plugin = plugin - ncc_uri = cfg.CONF.netscaler_driver.netscaler_ncc_uri - ncc_username = cfg.CONF.netscaler_driver.netscaler_ncc_username - ncc_password = cfg.CONF.netscaler_driver.netscaler_ncc_password - self.client = ncc_client.NSClient(ncc_uri, - ncc_username, - ncc_password) - - def create_vip(self, context, vip): - """Create a vip on a NetScaler device.""" - network_info = self._get_vip_network_info(context, vip) - ncc_vip = self._prepare_vip_for_creation(vip) - ncc_vip = dict(ncc_vip.items() + network_info.items()) - msg = _("NetScaler driver vip creation: %s") % repr(ncc_vip) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.create_resource(context.tenant_id, VIPS_RESOURCE, - VIP_RESOURCE, ncc_vip) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Vip, vip["id"], - status) - - def update_vip(self, context, old_vip, vip): - """Update a vip on a NetScaler device.""" - update_vip = self._prepare_vip_for_update(vip) - resource_path = "%s/%s" % (VIPS_RESOURCE, vip["id"]) - msg = (_("NetScaler driver vip %(vip_id)s update: %(vip_obj)s") % - {"vip_id": vip["id"], "vip_obj": repr(vip)}) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.update_resource(context.tenant_id, resource_path, - VIP_RESOURCE, update_vip) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Vip, old_vip["id"], - status) - - def delete_vip(self, context, vip): - """Delete a vip on a NetScaler device.""" - resource_path = "%s/%s" % (VIPS_RESOURCE, vip["id"]) - msg = _("NetScaler driver vip removal: %s") % vip["id"] - LOG.debug(msg) - try: - self.client.remove_resource(context.tenant_id, resource_path) - except ncc_client.NCCException: - self.plugin.update_status(context, loadbalancer_db.Vip, - vip["id"], - constants.ERROR) - else: - self.plugin._delete_db_vip(context, vip['id']) - - def create_pool(self, context, pool): - """Create a pool on a NetScaler device.""" - network_info = self._get_pool_network_info(context, pool) - #allocate a snat port/ipaddress on the subnet if one doesn't exist - self._create_snatport_for_subnet_if_not_exists(context, - pool['tenant_id'], - pool['subnet_id'], - network_info) - ncc_pool = self._prepare_pool_for_creation(pool) - ncc_pool = dict(ncc_pool.items() + network_info.items()) - msg = _("NetScaler driver pool creation: %s") % repr(ncc_pool) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.create_resource(context.tenant_id, POOLS_RESOURCE, - POOL_RESOURCE, ncc_pool) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Pool, - ncc_pool["id"], status) - - def update_pool(self, context, old_pool, pool): - """Update a pool on a NetScaler device.""" - ncc_pool = self._prepare_pool_for_update(pool) - resource_path = "%s/%s" % (POOLS_RESOURCE, old_pool["id"]) - msg = (_("NetScaler driver pool %(pool_id)s update: %(pool_obj)s") % - {"pool_id": old_pool["id"], "pool_obj": repr(ncc_pool)}) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.update_resource(context.tenant_id, resource_path, - POOL_RESOURCE, ncc_pool) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Pool, - old_pool["id"], status) - - def delete_pool(self, context, pool): - """Delete a pool on a NetScaler device.""" - resource_path = "%s/%s" % (POOLS_RESOURCE, pool['id']) - msg = _("NetScaler driver pool removal: %s") % pool["id"] - LOG.debug(msg) - try: - self.client.remove_resource(context.tenant_id, resource_path) - except ncc_client.NCCException: - self.plugin.update_status(context, loadbalancer_db.Pool, - pool["id"], - constants.ERROR) - else: - self.plugin._delete_db_pool(context, pool['id']) - self._remove_snatport_for_subnet_if_not_used(context, - pool['tenant_id'], - pool['subnet_id']) - - def create_member(self, context, member): - """Create a pool member on a NetScaler device.""" - ncc_member = self._prepare_member_for_creation(member) - msg = (_("NetScaler driver poolmember creation: %s") % - repr(ncc_member)) - LOG.info(msg) - status = constants.ACTIVE - try: - self.client.create_resource(context.tenant_id, - POOLMEMBERS_RESOURCE, - POOLMEMBER_RESOURCE, - ncc_member) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Member, - member["id"], status) - - def update_member(self, context, old_member, member): - """Update a pool member on a NetScaler device.""" - ncc_member = self._prepare_member_for_update(member) - resource_path = "%s/%s" % (POOLMEMBERS_RESOURCE, old_member["id"]) - msg = (_("NetScaler driver poolmember %(member_id)s update:" - " %(member_obj)s") % - {"member_id": old_member["id"], - "member_obj": repr(ncc_member)}) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.update_resource(context.tenant_id, resource_path, - POOLMEMBER_RESOURCE, ncc_member) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_status(context, loadbalancer_db.Member, - old_member["id"], status) - - def delete_member(self, context, member): - """Delete a pool member on a NetScaler device.""" - resource_path = "%s/%s" % (POOLMEMBERS_RESOURCE, member['id']) - msg = (_("NetScaler driver poolmember removal: %s") % - member["id"]) - LOG.debug(msg) - try: - self.client.remove_resource(context.tenant_id, resource_path) - except ncc_client.NCCException: - self.plugin.update_status(context, loadbalancer_db.Member, - member["id"], - constants.ERROR) - else: - self.plugin._delete_db_member(context, member['id']) - - def create_pool_health_monitor(self, context, health_monitor, pool_id): - """Create a pool health monitor on a NetScaler device.""" - ncc_hm = self._prepare_healthmonitor_for_creation(health_monitor, - pool_id) - resource_path = "%s/%s/%s" % (POOLS_RESOURCE, pool_id, - MONITORS_RESOURCE) - msg = (_("NetScaler driver healthmonitor creation for pool %(pool_id)s" - ": %(monitor_obj)s") % - {"pool_id": pool_id, - "monitor_obj": repr(ncc_hm)}) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.create_resource(context.tenant_id, resource_path, - MONITOR_RESOURCE, - ncc_hm) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_pool_health_monitor(context, - health_monitor['id'], - pool_id, - status, "") - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, pool_id): - """Update a pool health monitor on a NetScaler device.""" - ncc_hm = self._prepare_healthmonitor_for_update(health_monitor) - resource_path = "%s/%s" % (MONITORS_RESOURCE, - old_health_monitor["id"]) - msg = (_("NetScaler driver healthmonitor %(monitor_id)s update: " - "%(monitor_obj)s") % - {"monitor_id": old_health_monitor["id"], - "monitor_obj": repr(ncc_hm)}) - LOG.debug(msg) - status = constants.ACTIVE - try: - self.client.update_resource(context.tenant_id, resource_path, - MONITOR_RESOURCE, ncc_hm) - except ncc_client.NCCException: - status = constants.ERROR - self.plugin.update_pool_health_monitor(context, - old_health_monitor['id'], - pool_id, - status, "") - - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - """Delete a pool health monitor on a NetScaler device.""" - resource_path = "%s/%s/%s/%s" % (POOLS_RESOURCE, pool_id, - MONITORS_RESOURCE, - health_monitor["id"]) - msg = (_("NetScaler driver healthmonitor %(monitor_id)s" - "removal for pool %(pool_id)s") % - {"monitor_id": health_monitor["id"], - "pool_id": pool_id}) - LOG.debug(msg) - try: - self.client.remove_resource(context.tenant_id, resource_path) - except ncc_client.NCCException: - self.plugin.update_pool_health_monitor(context, - health_monitor['id'], - pool_id, - constants.ERROR, "") - else: - self.plugin._delete_db_pool_health_monitor(context, - health_monitor['id'], - pool_id) - - def stats(self, context, pool_id): - """Retrieve pool statistics from the NetScaler device.""" - resource_path = "%s/%s" % (POOLSTATS_RESOURCE, pool_id) - msg = _("NetScaler driver pool stats retrieval: %s") % pool_id - LOG.debug(msg) - try: - stats = self.client.retrieve_resource(context.tenant_id, - resource_path)[1] - except ncc_client.NCCException: - self.plugin.update_status(context, loadbalancer_db.Pool, - pool_id, constants.ERROR) - else: - return stats - - def _prepare_vip_for_creation(self, vip): - creation_attrs = { - 'id': vip['id'], - 'tenant_id': vip['tenant_id'], - 'protocol': vip['protocol'], - 'address': vip['address'], - 'protocol_port': vip['protocol_port'], - } - if 'session_persistence' in vip: - creation_attrs['session_persistence'] = vip['session_persistence'] - update_attrs = self._prepare_vip_for_update(vip) - creation_attrs.update(update_attrs) - return creation_attrs - - def _prepare_vip_for_update(self, vip): - return { - 'name': vip['name'], - 'description': vip['description'], - 'pool_id': vip['pool_id'], - 'connection_limit': vip['connection_limit'], - 'admin_state_up': vip['admin_state_up'] - } - - def _prepare_pool_for_creation(self, pool): - creation_attrs = { - 'id': pool['id'], - 'tenant_id': pool['tenant_id'], - 'vip_id': pool['vip_id'], - 'protocol': pool['protocol'], - 'subnet_id': pool['subnet_id'], - } - update_attrs = self._prepare_pool_for_update(pool) - creation_attrs.update(update_attrs) - return creation_attrs - - def _prepare_pool_for_update(self, pool): - return { - 'name': pool['name'], - 'description': pool['description'], - 'lb_method': pool['lb_method'], - 'admin_state_up': pool['admin_state_up'] - } - - def _prepare_member_for_creation(self, member): - creation_attrs = { - 'id': member['id'], - 'tenant_id': member['tenant_id'], - 'address': member['address'], - 'protocol_port': member['protocol_port'], - } - update_attrs = self._prepare_member_for_update(member) - creation_attrs.update(update_attrs) - return creation_attrs - - def _prepare_member_for_update(self, member): - return { - 'pool_id': member['pool_id'], - 'weight': member['weight'], - 'admin_state_up': member['admin_state_up'] - } - - def _prepare_healthmonitor_for_creation(self, health_monitor, pool_id): - creation_attrs = { - 'id': health_monitor['id'], - 'tenant_id': health_monitor['tenant_id'], - 'type': health_monitor['type'], - } - update_attrs = self._prepare_healthmonitor_for_update(health_monitor) - creation_attrs.update(update_attrs) - return creation_attrs - - def _prepare_healthmonitor_for_update(self, health_monitor): - ncc_hm = { - 'delay': health_monitor['delay'], - 'timeout': health_monitor['timeout'], - 'max_retries': health_monitor['max_retries'], - 'admin_state_up': health_monitor['admin_state_up'] - } - if health_monitor['type'] in ['HTTP', 'HTTPS']: - ncc_hm['http_method'] = health_monitor['http_method'] - ncc_hm['url_path'] = health_monitor['url_path'] - ncc_hm['expected_codes'] = health_monitor['expected_codes'] - return ncc_hm - - def _get_network_info(self, context, entity): - network_info = {} - subnet_id = entity['subnet_id'] - subnet = self.plugin._core_plugin.get_subnet(context, subnet_id) - network_id = subnet['network_id'] - network = self.plugin._core_plugin.get_network(context, network_id) - network_info['network_id'] = network_id - network_info['subnet_id'] = subnet_id - if PROV_NET_TYPE in network: - network_info['network_type'] = network[PROV_NET_TYPE] - if PROV_SEGMT_ID in network: - network_info['segmentation_id'] = network[PROV_SEGMT_ID] - return network_info - - def _get_vip_network_info(self, context, vip): - network_info = self._get_network_info(context, vip) - network_info['port_id'] = vip['port_id'] - return network_info - - def _get_pool_network_info(self, context, pool): - return self._get_network_info(context, pool) - - def _get_pools_on_subnet(self, context, tenant_id, subnet_id): - filter_dict = {'subnet_id': [subnet_id], 'tenant_id': [tenant_id]} - return self.plugin.get_pools(context, filters=filter_dict) - - def _get_snatport_for_subnet(self, context, tenant_id, subnet_id): - device_id = '_lb-snatport-' + subnet_id - subnet = self.plugin._core_plugin.get_subnet(context, subnet_id) - network_id = subnet['network_id'] - msg = (_("Filtering ports based on network_id=%(network_id)s, " - "tenant_id=%(tenant_id)s, device_id=%(device_id)s") % - {'network_id': network_id, - 'tenant_id': tenant_id, - 'device_id': device_id}) - LOG.debug(msg) - filter_dict = { - 'network_id': [network_id], - 'tenant_id': [tenant_id], - 'device_id': [device_id], - 'device-owner': [DRIVER_NAME] - } - ports = self.plugin._core_plugin.get_ports(context, - filters=filter_dict) - if ports: - msg = _("Found an existing SNAT port for subnet %s") % subnet_id - LOG.info(msg) - return ports[0] - msg = _("Found no SNAT ports for subnet %s") % subnet_id - LOG.info(msg) - - def _create_snatport_for_subnet(self, context, tenant_id, subnet_id, - ip_address): - subnet = self.plugin._core_plugin.get_subnet(context, subnet_id) - fixed_ip = {'subnet_id': subnet['id']} - if ip_address and ip_address != attributes.ATTR_NOT_SPECIFIED: - fixed_ip['ip_address'] = ip_address - port_data = { - 'tenant_id': tenant_id, - 'name': '_lb-snatport-' + subnet_id, - 'network_id': subnet['network_id'], - 'mac_address': attributes.ATTR_NOT_SPECIFIED, - 'admin_state_up': False, - 'device_id': '_lb-snatport-' + subnet_id, - 'device_owner': DRIVER_NAME, - 'fixed_ips': [fixed_ip], - } - port = self.plugin._core_plugin.create_port(context, - {'port': port_data}) - msg = _("Created SNAT port: %s") % repr(port) - LOG.info(msg) - return port - - def _remove_snatport_for_subnet(self, context, tenant_id, subnet_id): - port = self._get_snatport_for_subnet(context, tenant_id, subnet_id) - if port: - self.plugin._core_plugin.delete_port(context, port['id']) - msg = _("Removed SNAT port: %s") % repr(port) - LOG.info(msg) - - def _create_snatport_for_subnet_if_not_exists(self, context, tenant_id, - subnet_id, network_info): - port = self._get_snatport_for_subnet(context, tenant_id, subnet_id) - if not port: - msg = _("No SNAT port found for subnet %s." - " Creating one...") % subnet_id - LOG.info(msg) - port = self._create_snatport_for_subnet(context, tenant_id, - subnet_id, - ip_address=None) - network_info['port_id'] = port['id'] - network_info['snat_ip'] = port['fixed_ips'][0]['ip_address'] - msg = _("SNAT port: %s") % repr(port) - LOG.info(msg) - - def _remove_snatport_for_subnet_if_not_used(self, context, tenant_id, - subnet_id): - pools = self._get_pools_on_subnet(context, tenant_id, subnet_id) - if not pools: - #No pools left on the old subnet. - #We can remove the SNAT port/ipaddress - self._remove_snatport_for_subnet(context, tenant_id, subnet_id) - msg = _("Removing SNAT port for subnet %s " - "as this is the last pool using it...") % subnet_id - LOG.info(msg) diff --git a/neutron/services/loadbalancer/drivers/radware/__init__.py b/neutron/services/loadbalancer/drivers/radware/__init__.py deleted file mode 100644 index 253de5544..000000000 --- a/neutron/services/loadbalancer/drivers/radware/__init__.py +++ /dev/null @@ -1,17 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Radware LLC (Radware) -# -# 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. -# -# @author: Avishay Balderman, Radware diff --git a/neutron/services/loadbalancer/drivers/radware/driver.py b/neutron/services/loadbalancer/drivers/radware/driver.py deleted file mode 100644 index 7f198a564..000000000 --- a/neutron/services/loadbalancer/drivers/radware/driver.py +++ /dev/null @@ -1,1097 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Radware LTD. -# -# 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. -# -# @author: Avishay Balderman, Radware - -import base64 -import copy -import httplib -import netaddr -import threading -import time - - -import eventlet -eventlet.monkey_patch(thread=True) - -from oslo.config import cfg -from six.moves import queue as Queue - -from neutron.api.v2 import attributes -from neutron.common import log as call_log -from neutron import context -from neutron.db.loadbalancer import loadbalancer_db as lb_db -from neutron.extensions import loadbalancer -from neutron.openstack.common import excutils -from neutron.openstack.common import jsonutils as json -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.loadbalancer.drivers import abstract_driver -from neutron.services.loadbalancer.drivers.radware import exceptions as r_exc - -LOG = logging.getLogger(__name__) - -RESP_STATUS = 0 -RESP_REASON = 1 -RESP_STR = 2 -RESP_DATA = 3 - -TEMPLATE_HEADER = {'Content-Type': - 'application/vnd.com.radware.vdirect.' - 'template-parameters+json'} -PROVISION_HEADER = {'Content-Type': - 'application/vnd.com.radware.' - 'vdirect.status+json'} -CREATE_SERVICE_HEADER = {'Content-Type': - 'application/vnd.com.radware.' - 'vdirect.adc-service-specification+json'} - -driver_opts = [ - cfg.StrOpt('vdirect_address', - help=_('IP address of vDirect server.')), - cfg.StrOpt('ha_secondary_address', - help=_('IP address of secondary vDirect server.')), - cfg.StrOpt('vdirect_user', - default='vDirect', - help=_('vDirect user name.')), - cfg.StrOpt('vdirect_password', - default='radware', - help=_('vDirect user password.')), - cfg.StrOpt('service_adc_type', - default="VA", - help=_('Service ADC type. Default: VA.')), - cfg.StrOpt('service_adc_version', - default="", - help=_('Service ADC version.')), - cfg.BoolOpt('service_ha_pair', - default=False, - help=_('Enables or disables the Service HA pair. ' - 'Default: False.')), - cfg.IntOpt('service_throughput', - default=1000, - help=_('Service throughput. Default: 1000.')), - cfg.IntOpt('service_ssl_throughput', - default=100, - help=_('Service SSL throughput. Default: 100.')), - cfg.IntOpt('service_compression_throughput', - default=100, - help=_('Service compression throughput. Default: 100.')), - cfg.IntOpt('service_cache', - default=20, - help=_('Size of service cache. Default: 20.')), - cfg.StrOpt('l2_l3_workflow_name', - default='openstack_l2_l3', - help=_('Name of l2_l3 workflow. Default: ' - 'openstack_l2_l3.')), - cfg.StrOpt('l4_workflow_name', - default='openstack_l4', - help=_('Name of l4 workflow. Default: openstack_l4.')), - cfg.DictOpt('l2_l3_ctor_params', - default={"service": "_REPLACE_", - "ha_network_name": "HA-Network", - "ha_ip_pool_name": "default", - "allocate_ha_vrrp": True, - "allocate_ha_ips": True, - "twoleg_enabled": "_REPLACE_"}, - help=_('Parameter for l2_l3 workflow constructor.')), - cfg.DictOpt('l2_l3_setup_params', - default={"data_port": 1, - "data_ip_address": "192.168.200.99", - "data_ip_mask": "255.255.255.0", - "gateway": "192.168.200.1", - "ha_port": 2}, - help=_('Parameter for l2_l3 workflow setup.')), - cfg.ListOpt('actions_to_skip', - default=['setup_l2_l3'], - help=_('List of actions that are not pushed to ' - 'the completion queue.')), - cfg.StrOpt('l4_action_name', - default='BaseCreate', - help=_('Name of the l4 workflow action. ' - 'Default: BaseCreate.')), - cfg.ListOpt('service_resource_pool_ids', - default=[], - help=_('Resource pool IDs.')), - cfg.IntOpt('service_isl_vlan', - default=-1, - help=_('A required VLAN for the interswitch link to use.')), - cfg.BoolOpt('service_session_mirroring_enabled', - default=False, - help=_('Enable or disable Alteon interswitch link for ' - 'stateful session failover. Default: False.')) -] - -cfg.CONF.register_opts(driver_opts, "radware") - - -class LoadBalancerDriver(abstract_driver.LoadBalancerAbstractDriver): - - """Radware lbaas driver.""" - - def __init__(self, plugin): - rad = cfg.CONF.radware - self.plugin = plugin - self.service = { - "haPair": rad.service_ha_pair, - "sessionMirroringEnabled": rad.service_session_mirroring_enabled, - "primary": { - "capacity": { - "throughput": rad.service_throughput, - "sslThroughput": rad.service_ssl_throughput, - "compressionThroughput": - rad.service_compression_throughput, - "cache": rad.service_cache - }, - "network": { - "type": "portgroup", - "portgroups": ['DATA_NETWORK'] - }, - "adcType": rad.service_adc_type, - "acceptableAdc": "Exact" - } - } - if rad.service_resource_pool_ids: - ids = rad.service_resource_pool_ids - self.service['resourcePoolIds'] = [ - {'name': id} for id in ids - ] - if rad.service_isl_vlan: - self.service['islVlan'] = rad.service_isl_vlan - self.l2_l3_wf_name = rad.l2_l3_workflow_name - self.l4_wf_name = rad.l4_workflow_name - self.l2_l3_ctor_params = rad.l2_l3_ctor_params - self.l2_l3_setup_params = rad.l2_l3_setup_params - self.l4_action_name = rad.l4_action_name - self.actions_to_skip = rad.actions_to_skip - vdirect_address = rad.vdirect_address - sec_server = rad.ha_secondary_address - self.rest_client = vDirectRESTClient(server=vdirect_address, - secondary_server=sec_server, - user=rad.vdirect_user, - password=rad.vdirect_password) - self.queue = Queue.Queue() - self.completion_handler = OperationCompletionHandler(self.queue, - self.rest_client, - plugin) - self.workflow_templates_exists = False - self.completion_handler.setDaemon(True) - self.completion_handler_started = False - - def _populate_vip_graph(self, context, vip): - ext_vip = self.plugin.populate_vip_graph(context, vip) - vip_network_id = self._get_vip_network_id(context, ext_vip) - pool_network_id = self._get_pool_network_id(context, ext_vip) - - # if VIP and PIP are different, we need an IP address for the PIP - # so create port on PIP's network and use its IP address - if vip_network_id != pool_network_id: - pip_address = self._create_port_for_pip( - context, - vip['tenant_id'], - _make_pip_name_from_vip(vip), - pool_network_id) - ext_vip['pip_address'] = pip_address - else: - ext_vip['pip_address'] = vip['address'] - - ext_vip['vip_network_id'] = vip_network_id - ext_vip['pool_network_id'] = pool_network_id - return ext_vip - - def create_vip(self, context, vip): - log_info = {'vip': vip, - 'extended_vip': 'NOT_ASSIGNED', - 'service_name': 'NOT_ASSIGNED'} - try: - ext_vip = self._populate_vip_graph(context, vip) - - service_name = self._get_service(ext_vip) - log_info['extended_vip'] = ext_vip - log_info['service_name'] = service_name - - self._create_workflow( - vip['pool_id'], self.l4_wf_name, - {"service": service_name}) - self._update_workflow( - vip['pool_id'], - self.l4_action_name, ext_vip, context) - - finally: - LOG.debug(_('vip: %(vip)s, ' - 'extended_vip: %(extended_vip)s, ' - 'service_name: %(service_name)s, '), - log_info) - - def update_vip(self, context, old_vip, vip): - ext_vip = self._populate_vip_graph(context, vip) - self._update_workflow( - vip['pool_id'], self.l4_action_name, - ext_vip, context, False, lb_db.Vip, vip['id']) - - def delete_vip(self, context, vip): - """Delete a Vip - - First delete it from the device. If deletion ended OK - - remove data from DB as well. - If the deletion failed - mark vip with error status in DB - - """ - - ext_vip = self._populate_vip_graph(context, vip) - params = _translate_vip_object_graph(ext_vip, - self.plugin, context) - ids = params.pop('__ids__') - - try: - # get neutron port id associated with the vip (present if vip and - # pip are different) and release it after workflow removed - port_filter = { - 'name': [_make_pip_name_from_vip(vip)], - } - ports = self.plugin._core_plugin.get_ports(context, - filters=port_filter) - if ports: - LOG.debug(_('Retrieved pip nport: %(port)r for ' - 'vip: %(vip)s'), {'port': ports[0], - 'vip': vip['id']}) - - delete_pip_nport_function = self._get_delete_pip_nports( - context, ports) - else: - delete_pip_nport_function = None - LOG.debug(_('Found no pip nports associated with ' - 'vip: %s'), vip['id']) - - # removing the WF will cause deletion of the configuration from the - # device - self._remove_workflow(ids, context, delete_pip_nport_function) - - except r_exc.RESTRequestFailure: - pool_id = ext_vip['pool_id'] - LOG.exception(_('Failed to remove workflow %s. ' - 'Going to set vip to ERROR status'), - pool_id) - - self.plugin.update_status(context, lb_db.Vip, ids['vip'], - constants.ERROR) - - def _get_delete_pip_nports(self, context, ports): - def _delete_pip_nports(success): - if success: - for port in ports: - try: - self.plugin._core_plugin.delete_port( - context, port['id']) - LOG.debug(_('pip nport id: %s'), port['id']) - except Exception as exception: - # stop exception propagation, nport may have - # been deleted by other means - LOG.warning(_('pip nport delete failed: %r'), - exception) - return _delete_pip_nports - - def create_pool(self, context, pool): - # nothing to do - pass - - def update_pool(self, context, old_pool, pool): - self._handle_pool(context, pool) - - def delete_pool(self, context, pool,): - self._handle_pool(context, pool, delete=True) - - def _handle_pool(self, context, pool, delete=False): - vip_id = self.plugin.get_pool(context, pool['id']).get('vip_id', None) - if vip_id: - if delete: - raise loadbalancer.PoolInUse(pool_id=pool['id']) - else: - vip = self.plugin.get_vip(context, vip_id) - ext_vip = self._populate_vip_graph(context, vip) - self._update_workflow( - pool['id'], self.l4_action_name, - ext_vip, context, delete, lb_db.Pool, pool['id']) - else: - if delete: - self.plugin._delete_db_pool(context, pool['id']) - else: - # we keep the pool in PENDING_UPDATE - # no point to modify it since it is not connected to vip yet - pass - - def create_member(self, context, member): - self._handle_member(context, member) - - def update_member(self, context, old_member, member): - self._handle_member(context, member) - - def delete_member(self, context, member): - self._handle_member(context, member, delete=True) - - def _handle_member(self, context, member, delete=False): - """Navigate the model. If a Vip is found - activate a bulk WF action. - """ - vip_id = self.plugin.get_pool( - context, member['pool_id']).get('vip_id') - if vip_id: - vip = self.plugin.get_vip(context, vip_id) - ext_vip = self._populate_vip_graph(context, vip) - self._update_workflow( - member['pool_id'], self.l4_action_name, - ext_vip, context, - delete, lb_db.Member, member['id']) - # We have to delete this member but it is not connected to a vip yet - elif delete: - self.plugin._delete_db_member(context, member['id']) - - def create_health_monitor(self, context, health_monitor): - # Anything to do here? the hm is not connected to the graph yet - pass - - def update_pool_health_monitor(self, context, old_health_monitor, - health_monitor, - pool_id): - self._handle_pool_health_monitor(context, health_monitor, pool_id) - - def create_pool_health_monitor(self, context, - health_monitor, pool_id): - self._handle_pool_health_monitor(context, health_monitor, pool_id) - - def delete_pool_health_monitor(self, context, health_monitor, pool_id): - self._handle_pool_health_monitor(context, health_monitor, pool_id, - True) - - def _handle_pool_health_monitor(self, context, health_monitor, - pool_id, delete=False): - """Push a graph to vDirect - - Navigate the model. Check if a pool is associated to the vip - and push the graph to vDirect - - """ - - vip_id = self.plugin.get_pool(context, pool_id).get('vip_id', None) - - debug_params = {"hm_id": health_monitor['id'], "pool_id": pool_id, - "delete": delete, "vip_id": vip_id} - LOG.debug(_('_handle_pool_health_monitor. health_monitor = %(hm_id)s ' - 'pool_id = %(pool_id)s delete = %(delete)s ' - 'vip_id = %(vip_id)s'), - debug_params) - - if vip_id: - vip = self.plugin.get_vip(context, vip_id) - ext_vip = self._populate_vip_graph(context, vip) - self._update_workflow(pool_id, self.l4_action_name, - ext_vip, context, - delete, lb_db.PoolMonitorAssociation, - health_monitor['id']) - elif delete: - self.plugin._delete_db_pool_health_monitor(context, - health_monitor['id'], - pool_id) - - def stats(self, context, pool_id): - # TODO(avishayb) implement - return {"bytes_in": 0, - "bytes_out": 0, - "active_connections": 0, - "total_connections": 0} - - def _get_vip_network_id(self, context, extended_vip): - subnet = self.plugin._core_plugin.get_subnet( - context, extended_vip['subnet_id']) - return subnet['network_id'] - - def _start_completion_handling_thread(self): - if not self.completion_handler_started: - LOG.info(_('Starting operation completion handling thread')) - self.completion_handler.start() - self.completion_handler_started = True - - def _get_pool_network_id(self, context, extended_vip): - subnet = self.plugin._core_plugin.get_subnet( - context, extended_vip['pool']['subnet_id']) - return subnet['network_id'] - - @call_log.log - def _update_workflow(self, wf_name, action, - wf_params, context, - delete=False, - lbaas_entity=None, entity_id=None): - """Update the WF state. Push the result to a queue for processing.""" - - if not self.workflow_templates_exists: - self._verify_workflow_templates() - - if action not in self.actions_to_skip: - params = _translate_vip_object_graph(wf_params, - self.plugin, - context) - else: - params = wf_params - - resource = '/api/workflow/%s/action/%s' % (wf_name, action) - response = _rest_wrapper(self.rest_client.call('POST', resource, - {'parameters': params}, - TEMPLATE_HEADER)) - LOG.debug(_('_update_workflow response: %s '), response) - - if action not in self.actions_to_skip: - ids = params.pop('__ids__', None) - oper = OperationAttributes(response['uri'], - ids, - lbaas_entity, - entity_id, - delete=delete) - LOG.debug(_('Pushing operation %s to the queue'), oper) - - self._start_completion_handling_thread() - self.queue.put_nowait(oper) - - def _remove_workflow(self, ids, context, post_remove_function): - - wf_name = ids['pool'] - LOG.debug(_('Remove the workflow %s') % wf_name) - resource = '/api/workflow/%s' % (wf_name) - rest_return = self.rest_client.call('DELETE', resource, None, None) - response = _rest_wrapper(rest_return, [204, 202, 404]) - if rest_return[RESP_STATUS] in [404]: - if post_remove_function: - try: - post_remove_function(True) - LOG.debug(_('Post-remove workflow function ' - '%r completed'), post_remove_function) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_('Post-remove workflow function ' - '%r failed'), post_remove_function) - self.plugin._delete_db_vip(context, ids['vip']) - else: - oper = OperationAttributes( - response['uri'], - ids, - lb_db.Vip, - ids['vip'], - delete=True, - post_op_function=post_remove_function) - LOG.debug(_('Pushing operation %s to the queue'), oper) - - self._start_completion_handling_thread() - self.queue.put_nowait(oper) - - def _remove_service(self, service_name): - resource = '/api/service/%s' % (service_name) - _rest_wrapper(self.rest_client.call('DELETE', - resource, None, None), - [202]) - - def _get_service(self, ext_vip): - """Get a service name. - - if you can't find one, - create a service and create l2_l3 WF. - - """ - if not self.workflow_templates_exists: - self._verify_workflow_templates() - if ext_vip['vip_network_id'] != ext_vip['pool_network_id']: - networks_name = '%s_%s' % (ext_vip['vip_network_id'], - ext_vip['pool_network_id']) - self.l2_l3_ctor_params["twoleg_enabled"] = True - else: - networks_name = ext_vip['vip_network_id'] - self.l2_l3_ctor_params["twoleg_enabled"] = False - incoming_service_name = 'srv_%s' % (networks_name,) - service_name = self._get_available_service(incoming_service_name) - if not service_name: - LOG.debug( - 'Could not find a service named ' + incoming_service_name) - service_name = self._create_service(ext_vip['vip_network_id'], - ext_vip['pool_network_id'], - ext_vip['tenant_id']) - self.l2_l3_ctor_params["service"] = incoming_service_name - wf_name = 'l2_l3_' + networks_name - self._create_workflow( - wf_name, self.l2_l3_wf_name, self.l2_l3_ctor_params) - self._update_workflow( - wf_name, "setup_l2_l3", self.l2_l3_setup_params, None) - else: - LOG.debug('A service named ' + service_name + ' was found.') - return service_name - - def _create_service(self, vip_network_id, pool_network_id, tenant_id): - """create the service and provision it (async).""" - # 1) create the service - service = copy.deepcopy(self.service) - if vip_network_id != pool_network_id: - service_name = 'srv_%s_%s' % (vip_network_id, pool_network_id) - service['primary']['network']['portgroups'] = [vip_network_id, - pool_network_id] - else: - service_name = 'srv_' + vip_network_id - service['primary']['network']['portgroups'] = [vip_network_id] - resource = '/api/service?name=%s&tenant=%s' % (service_name, tenant_id) - - response = _rest_wrapper(self.rest_client.call('POST', resource, - service, - CREATE_SERVICE_HEADER), [201]) - - # 2) provision the service - provision_uri = response['links']['actions']['provision'] - _rest_wrapper(self.rest_client.call('POST', provision_uri, - None, PROVISION_HEADER)) - return service_name - - def _get_available_service(self, service_name): - """Check if service exsists and return its name if it does.""" - resource = '/api/service/' + service_name - try: - _rest_wrapper(self.rest_client.call('GET', - resource, - None, None), [200]) - except Exception: - return - return service_name - - def _workflow_exists(self, pool_id): - """Check if a WF having the name of the pool_id exists.""" - resource = '/api/workflow/' + pool_id - try: - _rest_wrapper(self.rest_client.call('GET', - resource, - None, - None), [200]) - except Exception: - return False - return True - - def _create_workflow(self, wf_name, wf_template_name, - create_workflow_params=None): - """Create a WF if it doesn't exists yet.""" - if not self.workflow_templates_exists: - self._verify_workflow_templates() - if not self._workflow_exists(wf_name): - if not create_workflow_params: - create_workflow_params = {} - resource = '/api/workflowTemplate/%s?name=%s' % ( - wf_template_name, wf_name) - params = {'parameters': create_workflow_params} - response = _rest_wrapper(self.rest_client.call('POST', - resource, - params, - TEMPLATE_HEADER)) - LOG.debug(_('create_workflow response: %s'), str(response)) - - def _verify_workflow_templates(self): - """Verify the existence of workflows on vDirect server.""" - workflows = {self.l2_l3_wf_name: - False, self.l4_wf_name: False} - resource = '/api/workflowTemplate' - response = _rest_wrapper(self.rest_client.call('GET', - resource, - None, - None), [200]) - for wf in workflows.keys(): - for wf_template in response: - if wf == wf_template['name']: - workflows[wf] = True - break - for wf, found in workflows.items(): - if not found: - raise r_exc.WorkflowMissing(workflow=wf) - self.workflow_templates_exists = True - - def _create_port_for_pip(self, context, tenant_id, port_name, subnet): - """Creates port on subnet, returns that port's IP.""" - - # create port, we just want any IP allocated to the port based on the - # network id, so setting 'fixed_ips' to ATTR_NOT_SPECIFIED - port_data = { - 'tenant_id': tenant_id, - 'name': port_name, - 'network_id': subnet, - 'mac_address': attributes.ATTR_NOT_SPECIFIED, - 'admin_state_up': False, - 'device_id': '', - 'device_owner': 'neutron:' + constants.LOADBALANCER, - 'fixed_ips': attributes.ATTR_NOT_SPECIFIED - } - port = self.plugin._core_plugin.create_port(context, - {'port': port_data}) - return port['fixed_ips'][0]['ip_address'] - - -class vDirectRESTClient: - """REST server proxy to Radware vDirect.""" - - def __init__(self, - server='localhost', - secondary_server=None, - user=None, - password=None, - port=2189, - ssl=True, - timeout=5000, - base_uri=''): - self.server = server - self.secondary_server = secondary_server - self.port = port - self.ssl = ssl - self.base_uri = base_uri - self.timeout = timeout - if user and password: - self.auth = base64.encodestring('%s:%s' % (user, password)) - self.auth = self.auth.replace('\n', '') - else: - raise r_exc.AuthenticationMissing() - - debug_params = {'server': self.server, - 'sec_server': self.secondary_server, - 'port': self.port, - 'ssl': self.ssl} - LOG.debug(_('vDirectRESTClient:init server=%(server)s, ' - 'secondary server=%(sec_server)s, ' - 'port=%(port)d, ' - 'ssl=%(ssl)r'), debug_params) - - def _flip_servers(self): - LOG.warning(_('Fliping servers. Current is: %(server)s, ' - 'switching to %(secondary)s'), - {'server': self.server, - 'secondary': self.secondary_server}) - self.server, self.secondary_server = self.secondary_server, self.server - - def _recover(self, action, resource, data, headers, binary=False): - if self.server and self.secondary_server: - self._flip_servers() - resp = self._call(action, resource, data, - headers, binary) - return resp - else: - LOG.exception(_('REST client is not able to recover ' - 'since only one vDirect server is ' - 'configured.')) - return -1, None, None, None - - def call(self, action, resource, data, headers, binary=False): - resp = self._call(action, resource, data, headers, binary) - if resp[RESP_STATUS] == -1: - LOG.warning(_('vDirect server is not responding (%s).'), - self.server) - return self._recover(action, resource, data, headers, binary) - elif resp[RESP_STATUS] in (301, 307): - LOG.warning(_('vDirect server is not active (%s).'), - self.server) - return self._recover(action, resource, data, headers, binary) - else: - return resp - - @call_log.log - def _call(self, action, resource, data, headers, binary=False): - if resource.startswith('http'): - uri = resource - else: - uri = self.base_uri + resource - if binary: - body = data - else: - body = json.dumps(data) - - debug_data = 'binary' if binary else body - debug_data = debug_data if debug_data else 'EMPTY' - if not headers: - headers = {'Authorization': 'Basic %s' % self.auth} - else: - headers['Authorization'] = 'Basic %s' % self.auth - conn = None - if self.ssl: - conn = httplib.HTTPSConnection( - self.server, self.port, timeout=self.timeout) - if conn is None: - LOG.error(_('vdirectRESTClient: Could not establish HTTPS ' - 'connection')) - return 0, None, None, None - else: - conn = httplib.HTTPConnection( - self.server, self.port, timeout=self.timeout) - if conn is None: - LOG.error(_('vdirectRESTClient: Could not establish HTTP ' - 'connection')) - return 0, None, None, None - - try: - conn.request(action, uri, body, headers) - response = conn.getresponse() - respstr = response.read() - respdata = respstr - try: - respdata = json.loads(respstr) - except ValueError: - # response was not JSON, ignore the exception - pass - ret = (response.status, response.reason, respstr, respdata) - except Exception as e: - log_dict = {'action': action, 'e': e} - LOG.error(_('vdirectRESTClient: %(action)s failure, %(e)r'), - log_dict) - ret = -1, None, None, None - conn.close() - return ret - - -class OperationAttributes: - - """Holds operation attributes. - - The parameter 'post_op_function' (if supplied) is a function that takes - one boolean argument, specifying the success of the operation - - """ - - def __init__(self, - operation_url, - object_graph, - lbaas_entity=None, - entity_id=None, - delete=False, - post_op_function=None): - self.operation_url = operation_url - self.object_graph = object_graph - self.delete = delete - self.lbaas_entity = lbaas_entity - self.entity_id = entity_id - self.creation_time = time.time() - self.post_op_function = post_op_function - - def __repr__(self): - items = ("%s = %r" % (k, v) for k, v in self.__dict__.items()) - return "<%s: {%s}>" % (self.__class__.__name__, ', '.join(items)) - - -class OperationCompletionHandler(threading.Thread): - - """Update DB with operation status or delete the entity from DB.""" - - def __init__(self, queue, rest_client, plugin): - threading.Thread.__init__(self) - self.queue = queue - self.rest_client = rest_client - self.plugin = plugin - self.stoprequest = threading.Event() - self.opers_to_handle_before_rest = 0 - - def join(self, timeout=None): - self.stoprequest.set() - super(OperationCompletionHandler, self).join(timeout) - - def handle_operation_completion(self, oper): - result = self.rest_client.call('GET', - oper.operation_url, - None, - None) - completed = result[RESP_DATA]['complete'] - reason = result[RESP_REASON], - description = result[RESP_STR] - if completed: - # operation is done - update the DB with the status - # or delete the entire graph from DB - success = result[RESP_DATA]['success'] - sec_to_completion = time.time() - oper.creation_time - debug_data = {'oper': oper, - 'sec_to_completion': sec_to_completion, - 'success': success} - LOG.debug(_('Operation %(oper)s is completed after ' - '%(sec_to_completion)d sec ' - 'with success status: %(success)s :'), - debug_data) - db_status = None - if not success: - # failure - log it and set the return ERROR as DB state - if reason or description: - msg = 'Reason:%s. Description:%s' % (reason, description) - else: - msg = "unknown" - error_params = {"operation": oper, "msg": msg} - LOG.error(_('Operation %(operation)s failed. Reason: %(msg)s'), - error_params) - db_status = constants.ERROR - else: - if oper.delete: - _remove_object_from_db(self.plugin, oper) - else: - db_status = constants.ACTIVE - - if db_status: - _update_vip_graph_status(self.plugin, oper, db_status) - - OperationCompletionHandler._run_post_op_function(success, oper) - - return completed - - def run(self): - while not self.stoprequest.isSet(): - try: - oper = self.queue.get(timeout=1) - - # Get the current queue size (N) and set the counter with it. - # Handle N operations with no intermission. - # Once N operations handles, get the size again and repeat. - if self.opers_to_handle_before_rest <= 0: - self.opers_to_handle_before_rest = self.queue.qsize() + 1 - - LOG.debug('Operation consumed from the queue: ' + - str(oper)) - # check the status - if oper is done: update the db , - # else push the oper again to the queue - if not self.handle_operation_completion(oper): - LOG.debug(_('Operation %s is not completed yet..') % oper) - # Not completed - push to the queue again - self.queue.put_nowait(oper) - - self.queue.task_done() - self.opers_to_handle_before_rest -= 1 - - # Take one second rest before start handling - # new operations or operations handled before - if self.opers_to_handle_before_rest <= 0: - time.sleep(1) - - except Queue.Empty: - continue - except Exception: - m = _("Exception was thrown inside OperationCompletionHandler") - LOG.exception(m) - - @staticmethod - def _run_post_op_function(success, oper): - if oper.post_op_function: - log_data = {'func': oper.post_op_function, 'oper': oper} - try: - oper.post_op_function(success) - LOG.debug(_('Post-operation function ' - '%(func)r completed ' - 'after operation %(oper)r'), - log_data) - except Exception: - with excutils.save_and_reraise_exception(): - LOG.exception(_('Post-operation function ' - '%(func)r failed ' - 'after operation %(oper)r'), - log_data) - - -def _rest_wrapper(response, success_codes=[202]): - """Wrap a REST call and make sure a valid status is returned.""" - if not response: - raise r_exc.RESTRequestFailure( - status=-1, - reason="Unknown", - description="Unknown", - success_codes=success_codes - ) - elif response[RESP_STATUS] not in success_codes: - raise r_exc.RESTRequestFailure( - status=response[RESP_STATUS], - reason=response[RESP_REASON], - description=response[RESP_STR], - success_codes=success_codes - ) - else: - return response[RESP_DATA] - - -def _make_pip_name_from_vip(vip): - """Standard way of making PIP name based on VIP ID.""" - return 'pip_' + vip['id'] - - -def _update_vip_graph_status(plugin, oper, status): - """Update the status - - Of all the Vip object graph - or a specific entity in the graph. - - """ - - ctx = context.get_admin_context(load_admin_roles=False) - - LOG.debug(_('_update: %s '), oper) - if oper.lbaas_entity == lb_db.PoolMonitorAssociation: - plugin.update_pool_health_monitor(ctx, - oper.entity_id, - oper.object_graph['pool'], - status) - elif oper.entity_id: - plugin.update_status(ctx, - oper.lbaas_entity, - oper.entity_id, - status) - else: - _update_vip_graph_status_cascade(plugin, - oper.object_graph, - ctx, status) - - -def _update_vip_graph_status_cascade(plugin, ids, ctx, status): - plugin.update_status(ctx, - lb_db.Vip, - ids['vip'], - status) - plugin.update_status(ctx, - lb_db.Pool, - ids['pool'], - status) - for member_id in ids['members']: - plugin.update_status(ctx, - lb_db.Member, - member_id, - status) - for hm_id in ids['health_monitors']: - plugin.update_pool_health_monitor(ctx, - hm_id, - ids['pool'], - status) - - -def _remove_object_from_db(plugin, oper): - """Remove a specific entity from db.""" - LOG.debug(_('_remove_object_from_db %s'), str(oper)) - - ctx = context.get_admin_context(load_admin_roles=False) - - if oper.lbaas_entity == lb_db.PoolMonitorAssociation: - plugin._delete_db_pool_health_monitor(ctx, - oper.entity_id, - oper.object_graph['pool']) - elif oper.lbaas_entity == lb_db.Member: - plugin._delete_db_member(ctx, oper.entity_id) - elif oper.lbaas_entity == lb_db.Vip: - plugin._delete_db_vip(ctx, oper.entity_id) - elif oper.lbaas_entity == lb_db.Pool: - plugin._delete_db_pool(ctx, oper.entity_id) - else: - raise r_exc.UnsupportedEntityOperation( - operation='Remove from DB', entity=oper.lbaas_entity - ) - -TRANSLATION_DEFAULTS = {'session_persistence_type': 'none', - 'session_persistence_cookie_name': 'none', - 'url_path': '/', - 'http_method': 'GET', - 'expected_codes': '200', - 'subnet': '255.255.255.255', - 'mask': '255.255.255.255', - 'gw': '255.255.255.255', - } -VIP_PROPERTIES = ['address', 'protocol_port', 'protocol', 'connection_limit', - 'admin_state_up', 'session_persistence_type', - 'session_persistence_cookie_name'] -POOL_PROPERTIES = ['protocol', 'lb_method', 'admin_state_up'] -MEMBER_PROPERTIES = ['address', 'protocol_port', 'weight', 'admin_state_up', - 'subnet', 'mask', 'gw'] -HEALTH_MONITOR_PROPERTIES = ['type', 'delay', 'timeout', 'max_retries', - 'admin_state_up', 'url_path', 'http_method', - 'expected_codes', 'id'] - - -def _translate_vip_object_graph(extended_vip, plugin, context): - """Translate the extended vip - - translate to a structure that can be - understood by the workflow. - - """ - def _create_key(prefix, property_name): - return prefix + '_' + property_name + '_array' - - def _trans_prop_name(prop_name): - if prop_name == 'id': - return 'uuid' - else: - return prop_name - - def get_ids(extended_vip): - ids = {} - ids['vip'] = extended_vip['id'] - ids['pool'] = extended_vip['pool']['id'] - ids['members'] = [m['id'] for m in extended_vip['members']] - ids['health_monitors'] = [ - hm['id'] for hm in extended_vip['health_monitors'] - ] - return ids - - trans_vip = {} - LOG.debug('Vip graph to be translated: ' + str(extended_vip)) - for vip_property in VIP_PROPERTIES: - trans_vip['vip_' + vip_property] = extended_vip.get( - vip_property, TRANSLATION_DEFAULTS.get(vip_property)) - for pool_property in POOL_PROPERTIES: - trans_vip['pool_' + pool_property] = extended_vip[ - 'pool'][pool_property] - for member_property in MEMBER_PROPERTIES: - trans_vip[_create_key('member', member_property)] = [] - - two_leg = (extended_vip['pip_address'] != extended_vip['address']) - if two_leg: - pool_subnet = plugin._core_plugin.get_subnet( - context, extended_vip['pool']['subnet_id']) - - for member in extended_vip['members']: - if member['status'] != constants.PENDING_DELETE: - if (two_leg and netaddr.IPAddress(member['address']) - not in netaddr.IPNetwork(pool_subnet['cidr'])): - member_ports = plugin._core_plugin.get_ports( - context, - filters={'fixed_ips': {'ip_address': [member['address']]}, - 'tenant_id': [extended_vip['tenant_id']]}) - if len(member_ports) == 1: - member_subnet = plugin._core_plugin.get_subnet( - context, - member_ports[0]['fixed_ips'][0]['subnet_id']) - member_network = netaddr.IPNetwork(member_subnet['cidr']) - member['subnet'] = str(member_network.network) - member['mask'] = str(member_network.netmask) - else: - member['subnet'] = member['address'] - - member['gw'] = pool_subnet['gateway_ip'] - - for member_property in MEMBER_PROPERTIES: - trans_vip[_create_key('member', member_property)].append( - member.get(member_property, - TRANSLATION_DEFAULTS.get(member_property))) - - for hm_property in HEALTH_MONITOR_PROPERTIES: - trans_vip[ - _create_key('hm', _trans_prop_name(hm_property))] = [] - for hm in extended_vip['health_monitors']: - hm_pool = plugin.get_pool_health_monitor(context, - hm['id'], - extended_vip['pool']['id']) - if hm_pool['status'] != constants.PENDING_DELETE: - for hm_property in HEALTH_MONITOR_PROPERTIES: - value = hm.get(hm_property, - TRANSLATION_DEFAULTS.get(hm_property)) - trans_vip[_create_key('hm', - _trans_prop_name(hm_property))].append(value) - ids = get_ids(extended_vip) - trans_vip['__ids__'] = ids - for key in ['pip_address']: - if key in extended_vip: - trans_vip[key] = extended_vip[key] - LOG.debug('Translated Vip graph: ' + str(trans_vip)) - return trans_vip diff --git a/neutron/services/loadbalancer/drivers/radware/exceptions.py b/neutron/services/loadbalancer/drivers/radware/exceptions.py deleted file mode 100644 index eec22a252..000000000 --- a/neutron/services/loadbalancer/drivers/radware/exceptions.py +++ /dev/null @@ -1,44 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# Copyright 2013 Radware LTD. -# -# 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. -# -# @author: Evgeny Fedoruk, Radware - - -from neutron.common import exceptions - - -class RadwareLBaasException(exceptions.NeutronException): - message = _('An unknown exception occurred in Radware LBaaS provider.') - - -class AuthenticationMissing(RadwareLBaasException): - message = _('vDirect user/password missing. ' - 'Specify in configuration file, under [radware] section') - - -class WorkflowMissing(RadwareLBaasException): - message = _('Workflow %(workflow)s is missing on vDirect server. ' - 'Upload missing workflow') - - -class RESTRequestFailure(RadwareLBaasException): - message = _('REST request failed with status %(status)s. ' - 'Reason: %(reason)s, Description: %(description)s. ' - 'Success status codes are %(success_codes)s') - - -class UnsupportedEntityOperation(RadwareLBaasException): - message = _('%(operation)s operation is not supported for %(entity)s.') diff --git a/neutron/services/loadbalancer/plugin.py b/neutron/services/loadbalancer/plugin.py deleted file mode 100644 index 4e992e085..000000000 --- a/neutron/services/loadbalancer/plugin.py +++ /dev/null @@ -1,326 +0,0 @@ -# -# Copyright 2013 Radware LTD. -# -# 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. -# -# @author: Avishay Balderman, Radware - -from neutron.api.v2 import attributes as attrs -from neutron.common import exceptions as n_exc -from neutron import context -from neutron.db import api as qdbapi -from neutron.db.loadbalancer import loadbalancer_db as ldb -from neutron.db import servicetype_db as st_db -from neutron.extensions import loadbalancer -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.loadbalancer import agent_scheduler -from neutron.services import provider_configuration as pconf -from neutron.services import service_base - -LOG = logging.getLogger(__name__) - - -class LoadBalancerPlugin(ldb.LoadBalancerPluginDb, - agent_scheduler.LbaasAgentSchedulerDbMixin): - """Implementation of the Neutron Loadbalancer Service Plugin. - - This class manages the workflow of LBaaS request/response. - Most DB related works are implemented in class - loadbalancer_db.LoadBalancerPluginDb. - """ - supported_extension_aliases = ["lbaas", - "lbaas_agent_scheduler", - "service-type"] - - # lbaas agent notifiers to handle agent update operations; - # can be updated by plugin drivers while loading; - # will be extracted by neutron manager when loading service plugins; - agent_notifiers = {} - - def __init__(self): - """Initialization for the loadbalancer service plugin.""" - - qdbapi.register_models() - self.service_type_manager = st_db.ServiceTypeManager.get_instance() - self._load_drivers() - - def _load_drivers(self): - """Loads plugin-drivers specified in configuration.""" - self.drivers, self.default_provider = service_base.load_drivers( - constants.LOADBALANCER, self) - - # we're at the point when extensions are not loaded yet - # so prevent policy from being loaded - ctx = context.get_admin_context(load_admin_roles=False) - # stop service in case provider was removed, but resources were not - self._check_orphan_pool_associations(ctx, self.drivers.keys()) - - def _check_orphan_pool_associations(self, context, provider_names): - """Checks remaining associations between pools and providers. - - If admin has not undeployed resources with provider that was deleted - from configuration, neutron service is stopped. Admin must delete - resources prior to removing providers from configuration. - """ - pools = self.get_pools(context) - lost_providers = set([pool['provider'] for pool in pools - if pool['provider'] not in provider_names]) - # resources are left without provider - stop the service - if lost_providers: - msg = _("Delete associated loadbalancer pools before " - "removing providers %s") % list(lost_providers) - LOG.exception(msg) - raise SystemExit(1) - - def _get_driver_for_provider(self, provider): - if provider in self.drivers: - return self.drivers[provider] - # raise if not associated (should never be reached) - raise n_exc.Invalid(_("Error retrieving driver for provider %s") % - provider) - - def _get_driver_for_pool(self, context, pool_id): - pool = self.get_pool(context, pool_id) - try: - return self.drivers[pool['provider']] - except KeyError: - raise n_exc.Invalid(_("Error retrieving provider for pool %s") % - pool_id) - - def get_plugin_type(self): - return constants.LOADBALANCER - - def get_plugin_description(self): - return "Neutron LoadBalancer Service Plugin" - - def create_vip(self, context, vip): - v = super(LoadBalancerPlugin, self).create_vip(context, vip) - driver = self._get_driver_for_pool(context, v['pool_id']) - driver.create_vip(context, v) - return v - - def update_vip(self, context, id, vip): - if 'status' not in vip['vip']: - vip['vip']['status'] = constants.PENDING_UPDATE - old_vip = self.get_vip(context, id) - v = super(LoadBalancerPlugin, self).update_vip(context, id, vip) - driver = self._get_driver_for_pool(context, v['pool_id']) - driver.update_vip(context, old_vip, v) - return v - - def _delete_db_vip(self, context, id): - # proxy the call until plugin inherits from DBPlugin - super(LoadBalancerPlugin, self).delete_vip(context, id) - - def delete_vip(self, context, id): - self.update_status(context, ldb.Vip, - id, constants.PENDING_DELETE) - v = self.get_vip(context, id) - driver = self._get_driver_for_pool(context, v['pool_id']) - driver.delete_vip(context, v) - - def _get_provider_name(self, context, pool): - if ('provider' in pool and - pool['provider'] != attrs.ATTR_NOT_SPECIFIED): - provider_name = pconf.normalize_provider_name(pool['provider']) - self.validate_provider(provider_name) - return provider_name - else: - if not self.default_provider: - raise pconf.DefaultServiceProviderNotFound( - service_type=constants.LOADBALANCER) - return self.default_provider - - def create_pool(self, context, pool): - provider_name = self._get_provider_name(context, pool['pool']) - p = super(LoadBalancerPlugin, self).create_pool(context, pool) - - self.service_type_manager.add_resource_association( - context, - constants.LOADBALANCER, - provider_name, p['id']) - #need to add provider name to pool dict, - #because provider was not known to db plugin at pool creation - p['provider'] = provider_name - driver = self.drivers[provider_name] - try: - driver.create_pool(context, p) - except loadbalancer.NoEligibleBackend: - # that should catch cases when backend of any kind - # is not available (agent, appliance, etc) - self.update_status(context, ldb.Pool, - p['id'], constants.ERROR, - "No eligible backend") - raise loadbalancer.NoEligibleBackend(pool_id=p['id']) - return p - - def update_pool(self, context, id, pool): - if 'status' not in pool['pool']: - pool['pool']['status'] = constants.PENDING_UPDATE - old_pool = self.get_pool(context, id) - p = super(LoadBalancerPlugin, self).update_pool(context, id, pool) - driver = self._get_driver_for_provider(p['provider']) - driver.update_pool(context, old_pool, p) - return p - - def _delete_db_pool(self, context, id): - # proxy the call until plugin inherits from DBPlugin - # rely on uuid uniqueness: - try: - with context.session.begin(subtransactions=True): - self.service_type_manager.del_resource_associations( - context, [id]) - super(LoadBalancerPlugin, self).delete_pool(context, id) - except Exception: - # that should not happen - # if it's still a case - something goes wrong - # log the error and mark the pool as ERROR - LOG.error(_('Failed to delete pool %s, putting it in ERROR state'), - id) - with excutils.save_and_reraise_exception(): - self.update_status(context, ldb.Pool, - id, constants.ERROR) - - def delete_pool(self, context, id): - # check for delete conditions and update the status - # within a transaction to avoid a race - with context.session.begin(subtransactions=True): - self.update_status(context, ldb.Pool, - id, constants.PENDING_DELETE) - self._ensure_pool_delete_conditions(context, id) - p = self.get_pool(context, id) - driver = self._get_driver_for_provider(p['provider']) - driver.delete_pool(context, p) - - def create_member(self, context, member): - m = super(LoadBalancerPlugin, self).create_member(context, member) - driver = self._get_driver_for_pool(context, m['pool_id']) - driver.create_member(context, m) - return m - - def update_member(self, context, id, member): - if 'status' not in member['member']: - member['member']['status'] = constants.PENDING_UPDATE - old_member = self.get_member(context, id) - m = super(LoadBalancerPlugin, self).update_member(context, id, member) - driver = self._get_driver_for_pool(context, m['pool_id']) - driver.update_member(context, old_member, m) - return m - - def _delete_db_member(self, context, id): - # proxy the call until plugin inherits from DBPlugin - super(LoadBalancerPlugin, self).delete_member(context, id) - - def delete_member(self, context, id): - self.update_status(context, ldb.Member, - id, constants.PENDING_DELETE) - m = self.get_member(context, id) - driver = self._get_driver_for_pool(context, m['pool_id']) - driver.delete_member(context, m) - - def _validate_hm_parameters(self, delay, timeout): - if delay < timeout: - raise loadbalancer.DelayOrTimeoutInvalid() - - def create_health_monitor(self, context, health_monitor): - new_hm = health_monitor['health_monitor'] - self._validate_hm_parameters(new_hm['delay'], new_hm['timeout']) - - hm = super(LoadBalancerPlugin, self).create_health_monitor( - context, - health_monitor - ) - return hm - - def update_health_monitor(self, context, id, health_monitor): - new_hm = health_monitor['health_monitor'] - old_hm = self.get_health_monitor(context, id) - delay = new_hm.get('delay', old_hm.get('delay')) - timeout = new_hm.get('timeout', old_hm.get('timeout')) - self._validate_hm_parameters(delay, timeout) - - hm = super(LoadBalancerPlugin, self).update_health_monitor( - context, - id, - health_monitor - ) - - with context.session.begin(subtransactions=True): - qry = context.session.query( - ldb.PoolMonitorAssociation - ).filter_by(monitor_id=hm['id']).join(ldb.Pool) - for assoc in qry: - driver = self._get_driver_for_pool(context, assoc['pool_id']) - driver.update_pool_health_monitor(context, old_hm, - hm, assoc['pool_id']) - return hm - - def _delete_db_pool_health_monitor(self, context, hm_id, pool_id): - super(LoadBalancerPlugin, self).delete_pool_health_monitor(context, - hm_id, - pool_id) - - def _delete_db_health_monitor(self, context, id): - super(LoadBalancerPlugin, self).delete_health_monitor(context, id) - - def create_pool_health_monitor(self, context, health_monitor, pool_id): - retval = super(LoadBalancerPlugin, self).create_pool_health_monitor( - context, - health_monitor, - pool_id - ) - monitor_id = health_monitor['health_monitor']['id'] - hm = self.get_health_monitor(context, monitor_id) - driver = self._get_driver_for_pool(context, pool_id) - driver.create_pool_health_monitor(context, hm, pool_id) - return retval - - def delete_pool_health_monitor(self, context, id, pool_id): - self.update_pool_health_monitor(context, id, pool_id, - constants.PENDING_DELETE) - hm = self.get_health_monitor(context, id) - driver = self._get_driver_for_pool(context, pool_id) - driver.delete_pool_health_monitor(context, hm, pool_id) - - def stats(self, context, pool_id): - driver = self._get_driver_for_pool(context, pool_id) - stats_data = driver.stats(context, pool_id) - # if we get something from the driver - - # update the db and return the value from db - # else - return what we have in db - if stats_data: - super(LoadBalancerPlugin, self).update_pool_stats( - context, - pool_id, - stats_data - ) - return super(LoadBalancerPlugin, self).stats(context, - pool_id) - - def populate_vip_graph(self, context, vip): - """Populate the vip with: pool, members, healthmonitors.""" - - pool = self.get_pool(context, vip['pool_id']) - vip['pool'] = pool - vip['members'] = [self.get_member(context, member_id) - for member_id in pool['members']] - vip['health_monitors'] = [self.get_health_monitor(context, hm_id) - for hm_id in pool['health_monitors']] - return vip - - def validate_provider(self, provider): - if provider not in self.drivers: - raise pconf.ServiceProviderNotFound( - provider=provider, service_type=constants.LOADBALANCER) diff --git a/neutron/services/metering/__init__.py b/neutron/services/metering/__init__.py deleted file mode 100644 index 82a447213..000000000 --- a/neutron/services/metering/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/metering/agents/__init__.py b/neutron/services/metering/agents/__init__.py deleted file mode 100644 index 82a447213..000000000 --- a/neutron/services/metering/agents/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/metering/agents/metering_agent.py b/neutron/services/metering/agents/metering_agent.py deleted file mode 100644 index 80883f41b..000000000 --- a/neutron/services/metering/agents/metering_agent.py +++ /dev/null @@ -1,297 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# 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 sys -import time - -import eventlet -eventlet.monkey_patch() - -from oslo.config import cfg - -from neutron.agent.common import config -from neutron.agent import rpc as agent_rpc -from neutron.common import config as common_config -from neutron.common import constants as constants -from neutron.common import rpc as n_rpc -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.common import utils -from neutron import context -from neutron import manager -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -from neutron.openstack.common import periodic_task -from neutron.openstack.common import service -from neutron import service as neutron_service - - -LOG = logging.getLogger(__name__) - - -class MeteringPluginRpc(rpc_compat.RpcProxy): - - BASE_RPC_API_VERSION = '1.0' - - def __init__(self, host): - super(MeteringPluginRpc, - self).__init__(topic=topics.METERING_AGENT, - default_version=self.BASE_RPC_API_VERSION) - - def _get_sync_data_metering(self, context): - try: - return self.call(context, - self.make_msg('get_sync_data_metering', - host=self.host), - topic=topics.METERING_PLUGIN) - except Exception: - LOG.exception(_("Failed synchronizing routers")) - - -class MeteringAgent(MeteringPluginRpc, manager.Manager): - - Opts = [ - cfg.StrOpt('driver', - default='neutron.services.metering.drivers.noop.' - 'noop_driver.NoopMeteringDriver', - help=_("Metering driver")), - cfg.IntOpt('measure_interval', default=30, - help=_("Interval between two metering measures")), - cfg.IntOpt('report_interval', default=300, - help=_("Interval between two metering reports")), - ] - - def __init__(self, host, conf=None): - self.conf = conf or cfg.CONF - self._load_drivers() - self.root_helper = config.get_root_helper(self.conf) - self.context = context.get_admin_context_without_session() - self.metering_info = {} - self.metering_loop = loopingcall.FixedIntervalLoopingCall( - self._metering_loop - ) - measure_interval = self.conf.measure_interval - self.last_report = 0 - self.metering_loop.start(interval=measure_interval) - self.host = host - - self.label_tenant_id = {} - self.routers = {} - self.metering_infos = {} - super(MeteringAgent, self).__init__(host=host) - - def _load_drivers(self): - """Loads plugin-driver from configuration.""" - LOG.info(_("Loading Metering driver %s"), self.conf.driver) - if not self.conf.driver: - raise SystemExit(_('A metering driver must be specified')) - self.metering_driver = importutils.import_object( - self.conf.driver, self, self.conf) - - def _metering_notification(self): - for label_id, info in self.metering_infos.items(): - data = {'label_id': label_id, - 'tenant_id': self.label_tenant_id.get(label_id), - 'pkts': info['pkts'], - 'bytes': info['bytes'], - 'time': info['time'], - 'first_update': info['first_update'], - 'last_update': info['last_update'], - 'host': self.host} - - LOG.debug(_("Send metering report: %s"), data) - notifier = n_rpc.get_notifier('metering') - notifier.info(self.context, 'l3.meter', data) - info['pkts'] = 0 - info['bytes'] = 0 - info['time'] = 0 - - def _purge_metering_info(self): - ts = int(time.time()) - report_interval = self.conf.report_interval - for label_id, info in self.metering_info.items(): - if info['last_update'] > ts + report_interval: - del self.metering_info[label_id] - - def _add_metering_info(self, label_id, pkts, bytes): - ts = int(time.time()) - info = self.metering_infos.get(label_id, {'bytes': 0, - 'pkts': 0, - 'time': 0, - 'first_update': ts, - 'last_update': ts}) - info['bytes'] += bytes - info['pkts'] += pkts - info['time'] += ts - info['last_update'] - info['last_update'] = ts - - self.metering_infos[label_id] = info - - return info - - def _add_metering_infos(self): - self.label_tenant_id = {} - for router in self.routers.values(): - tenant_id = router['tenant_id'] - labels = router.get(constants.METERING_LABEL_KEY, []) - for label in labels: - label_id = label['id'] - self.label_tenant_id[label_id] = tenant_id - - tenant_id = self.label_tenant_id.get - accs = self._get_traffic_counters(self.context, self.routers.values()) - if not accs: - return - - for label_id, acc in accs.items(): - self._add_metering_info(label_id, acc['pkts'], acc['bytes']) - - def _metering_loop(self): - self._add_metering_infos() - - ts = int(time.time()) - delta = ts - self.last_report - - report_interval = self.conf.report_interval - if delta > report_interval: - self._metering_notification() - self._purge_metering_info() - self.last_report = ts - - @utils.synchronized('metering-agent') - def _invoke_driver(self, context, meterings, func_name): - try: - return getattr(self.metering_driver, func_name)(context, meterings) - except AttributeError: - LOG.exception(_("Driver %(driver)s does not implement %(func)s"), - {'driver': self.conf.driver, - 'func': func_name}) - except RuntimeError: - LOG.exception(_("Driver %(driver)s:%(func)s runtime error"), - {'driver': self.conf.driver, - 'func': func_name}) - - @periodic_task.periodic_task(run_immediately=True) - def _sync_routers_task(self, context): - routers = self._get_sync_data_metering(self.context) - if not routers: - return - self._update_routers(context, routers) - - def router_deleted(self, context, router_id): - self._add_metering_infos() - - if router_id in self.routers: - del self.routers[router_id] - - return self._invoke_driver(context, router_id, - 'remove_router') - - def routers_updated(self, context, routers=None): - if not routers: - routers = self._get_sync_data_metering(self.context) - if not routers: - return - self._update_routers(context, routers) - - def _update_routers(self, context, routers): - for router in routers: - self.routers[router['id']] = router - - return self._invoke_driver(context, routers, - 'update_routers') - - def _get_traffic_counters(self, context, routers): - LOG.debug(_("Get router traffic counters")) - return self._invoke_driver(context, routers, 'get_traffic_counters') - - def update_metering_label_rules(self, context, routers): - LOG.debug(_("Update metering rules from agent")) - return self._invoke_driver(context, routers, - 'update_metering_label_rules') - - def add_metering_label(self, context, routers): - LOG.debug(_("Creating a metering label from agent")) - return self._invoke_driver(context, routers, - 'add_metering_label') - - def remove_metering_label(self, context, routers): - self._add_metering_infos() - - LOG.debug(_("Delete a metering label from agent")) - return self._invoke_driver(context, routers, - 'remove_metering_label') - - -class MeteringAgentWithStateReport(MeteringAgent): - - def __init__(self, host, conf=None): - super(MeteringAgentWithStateReport, self).__init__(host=host, - conf=conf) - self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) - self.agent_state = { - 'binary': 'neutron-metering-agent', - 'host': host, - 'topic': topics.METERING_AGENT, - 'configurations': { - 'metering_driver': self.conf.driver, - 'measure_interval': - self.conf.measure_interval, - 'report_interval': self.conf.report_interval - }, - 'start_flag': True, - 'agent_type': constants.AGENT_TYPE_METERING} - report_interval = cfg.CONF.AGENT.report_interval - self.use_call = True - if report_interval: - self.heartbeat = loopingcall.FixedIntervalLoopingCall( - self._report_state) - self.heartbeat.start(interval=report_interval) - - def _report_state(self): - try: - self.state_rpc.report_state(self.context, self.agent_state, - self.use_call) - self.agent_state.pop('start_flag', None) - self.use_call = False - except AttributeError: - # This means the server does not support report_state - LOG.warn(_("Neutron server does not support state report." - " State report for this agent will be disabled.")) - self.heartbeat.stop() - return - except Exception: - LOG.exception(_("Failed reporting state!")) - - def agent_updated(self, context, payload): - LOG.info(_("agent_updated by server side %s!"), payload) - - -def main(): - conf = cfg.CONF - conf.register_opts(MeteringAgent.Opts) - config.register_agent_state_opts_helper(conf) - config.register_root_helper(conf) - common_config.init(sys.argv[1:]) - config.setup_logging(conf) - server = neutron_service.Service.create( - binary='neutron-metering-agent', - topic=topics.METERING_AGENT, - report_interval=cfg.CONF.AGENT.report_interval, - manager='neutron.services.metering.agents.' - 'metering_agent.MeteringAgentWithStateReport') - service.launch(server).wait() diff --git a/neutron/services/metering/drivers/__init__.py b/neutron/services/metering/drivers/__init__.py deleted file mode 100644 index 82a447213..000000000 --- a/neutron/services/metering/drivers/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/metering/drivers/abstract_driver.py b/neutron/services/metering/drivers/abstract_driver.py deleted file mode 100644 index 884fbba50..000000000 --- a/neutron/services/metering/drivers/abstract_driver.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# 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 six - - -@six.add_metaclass(abc.ABCMeta) -class MeteringAbstractDriver(object): - """Abstract Metering driver.""" - - def __init__(self, plugin, conf): - pass - - @abc.abstractmethod - def update_routers(self, context, routers): - pass - - @abc.abstractmethod - def remove_router(self, context, router_id): - pass - - @abc.abstractmethod - def update_metering_label_rules(self, context, routers): - pass - - @abc.abstractmethod - def add_metering_label(self, context, routers): - pass - - @abc.abstractmethod - def remove_metering_label(self, context, routers): - pass - - @abc.abstractmethod - def get_traffic_counters(self, context, routers): - pass diff --git a/neutron/services/metering/drivers/iptables/__init__.py b/neutron/services/metering/drivers/iptables/__init__.py deleted file mode 100644 index 82a447213..000000000 --- a/neutron/services/metering/drivers/iptables/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/metering/drivers/iptables/iptables_driver.py b/neutron/services/metering/drivers/iptables/iptables_driver.py deleted file mode 100644 index 3eb17c246..000000000 --- a/neutron/services/metering/drivers/iptables/iptables_driver.py +++ /dev/null @@ -1,284 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# 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.common import config -from neutron.agent.linux import interface -from neutron.agent.linux import iptables_manager -from neutron.common import constants as constants -from neutron.common import log -from neutron.openstack.common import importutils -from neutron.openstack.common import log as logging -from neutron.services.metering.drivers import abstract_driver - - -LOG = logging.getLogger(__name__) -NS_PREFIX = 'qrouter-' -WRAP_NAME = 'neutron-meter' -EXTERNAL_DEV_PREFIX = 'qg-' -TOP_CHAIN = WRAP_NAME + "-FORWARD" -RULE = '-r-' -LABEL = '-l-' - -config.register_interface_driver_opts_helper(cfg.CONF) -config.register_use_namespaces_opts_helper(cfg.CONF) -config.register_root_helper(cfg.CONF) -cfg.CONF.register_opts(interface.OPTS) - - -class IptablesManagerTransaction(object): - __transactions = {} - - def __init__(self, im): - self.im = im - - transaction = self.__transactions.get(im, 0) - transaction += 1 - self.__transactions[im] = transaction - - def __enter__(self): - return self.im - - def __exit__(self, type, value, traceback): - transaction = self.__transactions.get(self.im) - if transaction == 1: - self.im.apply() - del self.__transactions[self.im] - else: - transaction -= 1 - self.__transactions[self.im] = transaction - - -class RouterWithMetering(object): - - def __init__(self, conf, router): - self.conf = conf - self.id = router['id'] - self.router = router - self.root_helper = config.get_root_helper(self.conf) - self.ns_name = NS_PREFIX + self.id if conf.use_namespaces else None - self.iptables_manager = iptables_manager.IptablesManager( - root_helper=self.root_helper, - namespace=self.ns_name, - binary_name=WRAP_NAME) - self.metering_labels = {} - - -class IptablesMeteringDriver(abstract_driver.MeteringAbstractDriver): - - def __init__(self, plugin, conf): - self.plugin = plugin - self.conf = conf or cfg.CONF - self.routers = {} - - if not self.conf.interface_driver: - raise SystemExit(_('An interface driver must be specified')) - LOG.info(_("Loading interface driver %s"), self.conf.interface_driver) - self.driver = importutils.import_object(self.conf.interface_driver, - self.conf) - - def _update_router(self, router): - r = self.routers.get(router['id'], - RouterWithMetering(self.conf, router)) - r.router = router - self.routers[r.id] = r - - return r - - @log.log - def update_routers(self, context, routers): - # disassociate removed routers - router_ids = [router['id'] for router in routers] - for router_id in self.routers: - if router_id not in router_ids: - self._process_disassociate_metering_label(router) - - for router in routers: - old_gw_port_id = None - old_rm = self.routers.get(router['id']) - if old_rm: - old_gw_port_id = old_rm.router['gw_port_id'] - gw_port_id = router['gw_port_id'] - - if gw_port_id != old_gw_port_id: - if old_rm: - with IptablesManagerTransaction(old_rm.iptables_manager): - self._process_disassociate_metering_label(router) - if gw_port_id: - self._process_associate_metering_label(router) - elif gw_port_id: - self._process_associate_metering_label(router) - - @log.log - def remove_router(self, context, router_id): - if router_id in self.routers: - del self.routers[router_id] - - def get_external_device_name(self, port_id): - return (EXTERNAL_DEV_PREFIX + port_id)[:self.driver.DEV_NAME_LEN] - - def _process_metering_label_rules(self, rm, rules, label_chain, - rules_chain): - im = rm.iptables_manager - ext_dev = self.get_external_device_name(rm.router['gw_port_id']) - if not ext_dev: - return - - for rule in rules: - remote_ip = rule['remote_ip_prefix'] - - dir = '-i ' + ext_dev - if rule['direction'] == 'egress': - dir = '-o ' + ext_dev - - if rule['excluded']: - ipt_rule = dir + ' -d ' + remote_ip + ' -j RETURN' - im.ipv4['filter'].add_rule(rules_chain, ipt_rule, wrap=False, - top=True) - else: - ipt_rule = dir + ' -d ' + remote_ip + ' -j ' + label_chain - im.ipv4['filter'].add_rule(rules_chain, ipt_rule, - wrap=False, top=False) - - def _process_associate_metering_label(self, router): - self._update_router(router) - rm = self.routers.get(router['id']) - - with IptablesManagerTransaction(rm.iptables_manager): - labels = router.get(constants.METERING_LABEL_KEY, []) - for label in labels: - label_id = label['id'] - - label_chain = iptables_manager.get_chain_name(WRAP_NAME + - LABEL + label_id, - wrap=False) - rm.iptables_manager.ipv4['filter'].add_chain(label_chain, - wrap=False) - - rules_chain = iptables_manager.get_chain_name(WRAP_NAME + - RULE + label_id, - wrap=False) - rm.iptables_manager.ipv4['filter'].add_chain(rules_chain, - wrap=False) - rm.iptables_manager.ipv4['filter'].add_rule(TOP_CHAIN, '-j ' + - rules_chain, - wrap=False) - - rm.iptables_manager.ipv4['filter'].add_rule(label_chain, - '', - wrap=False) - - rules = label.get('rules') - if rules: - self._process_metering_label_rules(rm, rules, - label_chain, - rules_chain) - - rm.metering_labels[label_id] = label - - def _process_disassociate_metering_label(self, router): - rm = self.routers.get(router['id']) - if not rm: - return - - with IptablesManagerTransaction(rm.iptables_manager): - labels = router.get(constants.METERING_LABEL_KEY, []) - for label in labels: - label_id = label['id'] - if label_id not in rm.metering_labels: - continue - - label_chain = iptables_manager.get_chain_name(WRAP_NAME + - LABEL + label_id, - wrap=False) - rules_chain = iptables_manager.get_chain_name(WRAP_NAME + - RULE + label_id, - wrap=False) - - rm.iptables_manager.ipv4['filter'].remove_chain(label_chain, - wrap=False) - rm.iptables_manager.ipv4['filter'].remove_chain(rules_chain, - wrap=False) - - del rm.metering_labels[label_id] - - @log.log - def add_metering_label(self, context, routers): - for router in routers: - self._process_associate_metering_label(router) - - @log.log - def update_metering_label_rules(self, context, routers): - for router in routers: - self._update_metering_label_rules(router) - - def _update_metering_label_rules(self, router): - rm = self.routers.get(router['id']) - if not rm: - return - - with IptablesManagerTransaction(rm.iptables_manager): - labels = router.get(constants.METERING_LABEL_KEY, []) - for label in labels: - label_id = label['id'] - - label_chain = iptables_manager.get_chain_name(WRAP_NAME + - LABEL + label_id, - wrap=False) - rules_chain = iptables_manager.get_chain_name(WRAP_NAME + - RULE + label_id, - wrap=False) - rm.iptables_manager.ipv4['filter'].empty_chain(rules_chain, - wrap=False) - - rules = label.get('rules') - if rules: - self._process_metering_label_rules(rm, rules, - label_chain, - rules_chain) - - @log.log - def remove_metering_label(self, context, routers): - for router in routers: - self._process_disassociate_metering_label(router) - - @log.log - def get_traffic_counters(self, context, routers): - accs = {} - for router in routers: - rm = self.routers.get(router['id']) - if not rm: - continue - - for label_id, label in rm.metering_labels.items(): - chain = iptables_manager.get_chain_name(WRAP_NAME + LABEL + - label_id, wrap=False) - - chain_acc = rm.iptables_manager.get_traffic_counters( - chain, wrap=False, zero=True) - - if not chain_acc: - continue - - acc = accs.get(label_id, {'pkts': 0, 'bytes': 0}) - - acc['pkts'] += chain_acc['pkts'] - acc['bytes'] += chain_acc['bytes'] - - accs[label_id] = acc - - return accs diff --git a/neutron/services/metering/drivers/noop/__init__.py b/neutron/services/metering/drivers/noop/__init__.py deleted file mode 100644 index 82a447213..000000000 --- a/neutron/services/metering/drivers/noop/__init__.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. diff --git a/neutron/services/metering/drivers/noop/noop_driver.py b/neutron/services/metering/drivers/noop/noop_driver.py deleted file mode 100644 index d3f5e7df4..000000000 --- a/neutron/services/metering/drivers/noop/noop_driver.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutron.common import log -from neutron.services.metering.drivers import abstract_driver - - -class NoopMeteringDriver(abstract_driver.MeteringAbstractDriver): - - @log.log - def update_routers(self, context, routers): - pass - - @log.log - def remove_router(self, context, router_id): - pass - - @log.log - def update_metering_label_rules(self, context, routers): - pass - - @log.log - def add_metering_label(self, context, routers): - pass - - @log.log - def remove_metering_label(self, context, routers): - pass - - @log.log - def get_traffic_counters(self, context, routers): - pass diff --git a/neutron/services/metering/metering_plugin.py b/neutron/services/metering/metering_plugin.py deleted file mode 100644 index e67dbab60..000000000 --- a/neutron/services/metering/metering_plugin.py +++ /dev/null @@ -1,74 +0,0 @@ -# Copyright (C) 2013 eNovance SAS -# -# Author: Sylvain Afchain -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from neutron.api.rpc.agentnotifiers import metering_rpc_agent_api -from neutron.common import rpc_compat -from neutron.common import topics -from neutron.db.metering import metering_db -from neutron.db.metering import metering_rpc - - -class MeteringPlugin(metering_db.MeteringDbMixin): - """Implementation of the Neutron Metering Service Plugin.""" - supported_extension_aliases = ["metering"] - - def __init__(self): - super(MeteringPlugin, self).__init__() - - self.endpoints = [metering_rpc.MeteringRpcCallbacks(self)] - - self.conn = rpc_compat.create_connection(new=True) - self.conn.create_consumer( - topics.METERING_PLUGIN, self.endpoints, fanout=False) - self.conn.consume_in_threads() - - self.meter_rpc = metering_rpc_agent_api.MeteringAgentNotifyAPI() - - def create_metering_label(self, context, metering_label): - label = super(MeteringPlugin, self).create_metering_label( - context, metering_label) - - data = self.get_sync_data_metering(context) - self.meter_rpc.add_metering_label(context, data) - - return label - - def delete_metering_label(self, context, label_id): - data = self.get_sync_data_metering(context, label_id) - label = super(MeteringPlugin, self).delete_metering_label( - context, label_id) - - self.meter_rpc.remove_metering_label(context, data) - - return label - - def create_metering_label_rule(self, context, metering_label_rule): - rule = super(MeteringPlugin, self).create_metering_label_rule( - context, metering_label_rule) - - data = self.get_sync_data_metering(context) - self.meter_rpc.update_metering_label_rules(context, data) - - return rule - - def delete_metering_label_rule(self, context, rule_id): - rule = super(MeteringPlugin, self).delete_metering_label_rule( - context, rule_id) - - data = self.get_sync_data_metering(context) - self.meter_rpc.update_metering_label_rules(context, data) - - return rule diff --git a/neutron/services/provider_configuration.py b/neutron/services/provider_configuration.py deleted file mode 100644 index 88a64b72b..000000000 --- a/neutron/services/provider_configuration.py +++ /dev/null @@ -1,162 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2013 OpenStack Foundation. -# All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. You may obtain -# a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -# License for the specific language governing permissions and limitations -# under the License. - -from oslo.config import cfg - -from neutron.common import exceptions as n_exc -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants - -LOG = logging.getLogger(__name__) - - -serviceprovider_opts = [ - cfg.MultiStrOpt('service_provider', default=[], - help=_('Defines providers for advanced services ' - 'using the format: ' - '::[:default]')) -] - -cfg.CONF.register_opts(serviceprovider_opts, 'service_providers') - - -#global scope function that should be used in service APIs -def normalize_provider_name(name): - return name.lower() - - -def parse_service_provider_opt(): - """Parse service definition opts and returns result.""" - def validate_name(name): - if len(name) > 255: - raise n_exc.Invalid( - _("Provider name is limited by 255 characters: %s") % name) - - svc_providers_opt = cfg.CONF.service_providers.service_provider - res = [] - for prov_def in svc_providers_opt: - split = prov_def.split(':') - try: - svc_type, name, driver = split[:3] - except ValueError: - raise n_exc.Invalid(_("Invalid service provider format")) - validate_name(name) - name = normalize_provider_name(name) - default = False - if len(split) == 4 and split[3]: - if split[3] == 'default': - default = True - else: - msg = (_("Invalid provider format. " - "Last part should be 'default' or empty: %s") % - prov_def) - LOG.error(msg) - raise n_exc.Invalid(msg) - if svc_type not in constants.ALLOWED_SERVICES: - msg = (_("Service type '%(svc_type)s' is not allowed, " - "allowed types: %(allowed)s") % - {'svc_type': svc_type, - 'allowed': constants.ALLOWED_SERVICES}) - LOG.error(msg) - raise n_exc.Invalid(msg) - res.append({'service_type': svc_type, - 'name': name, - 'driver': driver, - 'default': default}) - return res - - -class ServiceProviderNotFound(n_exc.InvalidInput): - message = _("Service provider '%(provider)s' could not be found " - "for service type %(service_type)s") - - -class DefaultServiceProviderNotFound(n_exc.InvalidInput): - message = _("Service type %(service_type)s does not have a default " - "service provider") - - -class ServiceProviderAlreadyAssociated(n_exc.Conflict): - message = _("Resource '%(resource_id)s' is already associated with " - "provider '%(provider)s' for service type '%(service_type)s'") - - -class ProviderConfiguration(object): - def __init__(self, prov_data): - self.providers = {} - for prov in prov_data: - self.add_provider(prov) - - def _ensure_driver_unique(self, driver): - for k, v in self.providers.items(): - if v['driver'] == driver: - msg = (_("Driver %s is not unique across providers") % - driver) - LOG.exception(msg) - raise n_exc.Invalid(msg) - - def _ensure_default_unique(self, type, default): - if not default: - return - for k, v in self.providers.items(): - if k[0] == type and v['default']: - msg = _("Multiple default providers " - "for service %s") % type - LOG.exception(msg) - raise n_exc.Invalid(msg) - - def add_provider(self, provider): - self._ensure_driver_unique(provider['driver']) - self._ensure_default_unique(provider['service_type'], - provider['default']) - provider_type = (provider['service_type'], provider['name']) - if provider_type in self.providers: - msg = (_("Multiple providers specified for service " - "%s") % provider['service_type']) - LOG.exception(msg) - raise n_exc.Invalid(msg) - self.providers[provider_type] = {'driver': provider['driver'], - 'default': provider['default']} - - def _check_entry(self, k, v, filters): - # small helper to deal with query filters - if not filters: - return True - for index, key in enumerate(['service_type', 'name']): - if key in filters: - if k[index] not in filters[key]: - return False - - for key in ['driver', 'default']: - if key in filters: - if v[key] not in filters[key]: - return False - return True - - def _fields(self, resource, fields): - if fields: - return dict(((key, item) for key, item in resource.items() - if key in fields)) - return resource - - def get_service_providers(self, filters=None, fields=None): - return [self._fields({'service_type': k[0], - 'name': k[1], - 'driver': v['driver'], - 'default': v['default']}, - fields) - for k, v in self.providers.items() - if self._check_entry(k, v, filters)] diff --git a/neutron/services/vpn/__init__.py b/neutron/services/vpn/__init__.py deleted file mode 100644 index 29c415d98..000000000 --- a/neutron/services/vpn/__init__.py +++ /dev/null @@ -1,18 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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. -# -# @author: Swaminathan Vasudevan, Hewlett-Packard diff --git a/neutron/services/vpn/agent.py b/neutron/services/vpn/agent.py deleted file mode 100644 index 771a67dce..000000000 --- a/neutron/services/vpn/agent.py +++ /dev/null @@ -1,148 +0,0 @@ -# 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, top=top) - - 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') diff --git a/neutron/services/vpn/common/__init__.py b/neutron/services/vpn/common/__init__.py deleted file mode 100644 index 9b27a7520..000000000 --- a/neutron/services/vpn/common/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# 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. diff --git a/neutron/services/vpn/common/topics.py b/neutron/services/vpn/common/topics.py deleted file mode 100644 index 2639fbe6d..000000000 --- a/neutron/services/vpn/common/topics.py +++ /dev/null @@ -1,22 +0,0 @@ -# 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' -CISCO_IPSEC_DRIVER_TOPIC = 'cisco_csr_ipsec_driver' -CISCO_IPSEC_AGENT_TOPIC = 'cisco_csr_ipsec_agent' diff --git a/neutron/services/vpn/device_drivers/__init__.py b/neutron/services/vpn/device_drivers/__init__.py deleted file mode 100644 index c6d5f69e3..000000000 --- a/neutron/services/vpn/device_drivers/__init__.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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 six - - -@six.add_metaclass(abc.ABCMeta) -class DeviceDriver(object): - - 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 diff --git a/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py b/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py deleted file mode 100644 index 61693e9e1..000000000 --- a/neutron/services/vpn/device_drivers/cisco_csr_rest_client.py +++ /dev/null @@ -1,258 +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. -# -# @author: Paul Michali, Cisco Systems, Inc. - -import time - -import netaddr -import requests -from requests import exceptions as r_exc - -from neutron.openstack.common import jsonutils -from neutron.openstack.common import log as logging - - -TIMEOUT = 20.0 - -LOG = logging.getLogger(__name__) -HEADER_CONTENT_TYPE_JSON = {'content-type': 'application/json'} -URL_BASE = 'https://%(host)s/api/v1/%(resource)s' - - -def make_route_id(cidr, interface): - """Build ID that will be used to identify route for later deletion.""" - net = netaddr.IPNetwork(cidr) - return '%(network)s_%(prefix)s_%(interface)s' % { - 'network': net.network, - 'prefix': net.prefixlen, - 'interface': interface} - - -class CsrRestClient(object): - - """REST CsrRestClient for accessing the Cisco Cloud Services Router.""" - - def __init__(self, host, tunnel_ip, username, password, timeout=None): - self.host = host - self.tunnel_ip = tunnel_ip - self.auth = (username, password) - self.token = None - self.status = requests.codes.OK - self.timeout = timeout - self.max_tries = 5 - self.session = requests.Session() - - def _response_info_for(self, response, method): - """Return contents or location from response. - - For a POST or GET with a 200 response, the response content - is returned. - - For a POST with a 201 response, return the header's location, - which contains the identifier for the created resource. - - If there is an error, return the response content, so that - it can be used in error processing ('error-code', 'error-message', - and 'detail' fields). - """ - if method in ('POST', 'GET') and self.status == requests.codes.OK: - LOG.debug(_('RESPONSE: %s'), response.json()) - return response.json() - if method == 'POST' and self.status == requests.codes.CREATED: - return response.headers.get('location', '') - if self.status >= requests.codes.BAD_REQUEST and response.content: - if 'error-code' in response.content: - content = jsonutils.loads(response.content) - LOG.debug("Error response content %s", content) - return content - - def _request(self, method, url, **kwargs): - """Perform REST request and save response info.""" - try: - LOG.debug(_("%(method)s: Request for %(resource)s payload: " - "%(payload)s"), - {'method': method.upper(), 'resource': url, - 'payload': kwargs.get('data')}) - start_time = time.time() - response = self.session.request(method, url, verify=False, - timeout=self.timeout, **kwargs) - LOG.debug(_("%(method)s Took %(time).2f seconds to process"), - {'method': method.upper(), - 'time': time.time() - start_time}) - except (r_exc.Timeout, r_exc.SSLError) as te: - # Should never see SSLError, unless requests package is old (<2.0) - timeout_val = 0.0 if self.timeout is None else self.timeout - LOG.warning(_("%(method)s: Request timeout%(ssl)s " - "(%(timeout).3f sec) for CSR(%(host)s)"), - {'method': method, - 'timeout': timeout_val, - 'ssl': '(SSLError)' - if isinstance(te, r_exc.SSLError) else '', - 'host': self.host}) - self.status = requests.codes.REQUEST_TIMEOUT - except r_exc.ConnectionError: - LOG.exception(_("%(method)s: Unable to connect to CSR(%(host)s)"), - {'method': method, 'host': self.host}) - self.status = requests.codes.NOT_FOUND - except Exception as e: - LOG.error(_("%(method)s: Unexpected error for CSR (%(host)s): " - "%(error)s"), - {'method': method, 'host': self.host, 'error': e}) - self.status = requests.codes.INTERNAL_SERVER_ERROR - else: - self.status = response.status_code - LOG.debug(_("%(method)s: Completed [%(status)s]"), - {'method': method, 'status': self.status}) - return self._response_info_for(response, method) - - def authenticate(self): - """Obtain a token to use for subsequent CSR REST requests. - - This is called when there is no token yet, or if the token has expired - and attempts to use it resulted in an UNAUTHORIZED REST response. - """ - - url = URL_BASE % {'host': self.host, 'resource': 'auth/token-services'} - headers = {'Content-Length': '0', - 'Accept': 'application/json'} - headers.update(HEADER_CONTENT_TYPE_JSON) - LOG.debug(_("%(auth)s with CSR %(host)s"), - {'auth': 'Authenticating' if self.token is None - else 'Reauthenticating', 'host': self.host}) - self.token = None - response = self._request("POST", url, headers=headers, auth=self.auth) - if response: - self.token = response['token-id'] - LOG.debug(_("Successfully authenticated with CSR %s"), self.host) - return True - LOG.error(_("Failed authentication with CSR %(host)s [%(status)s]"), - {'host': self.host, 'status': self.status}) - - def _do_request(self, method, resource, payload=None, more_headers=None, - full_url=False): - """Perform a REST request to a CSR resource. - - If this is the first time interacting with the CSR, a token will - be obtained. If the request fails, due to an expired token, the - token will be obtained and the request will be retried once more. - """ - - if self.token is None: - if not self.authenticate(): - return - - if full_url: - url = resource - else: - url = ('https://%(host)s/api/v1/%(resource)s' % - {'host': self.host, 'resource': resource}) - headers = {'Accept': 'application/json', 'X-auth-token': self.token} - if more_headers: - headers.update(more_headers) - if payload: - payload = jsonutils.dumps(payload) - response = self._request(method, url, data=payload, headers=headers) - if self.status == requests.codes.UNAUTHORIZED: - if not self.authenticate(): - return - headers['X-auth-token'] = self.token - response = self._request(method, url, data=payload, - headers=headers) - if self.status != requests.codes.REQUEST_TIMEOUT: - return response - LOG.error(_("%(method)s: Request timeout for CSR(%(host)s)"), - {'method': method, 'host': self.host}) - - def get_request(self, resource, full_url=False): - """Perform a REST GET requests for a CSR resource.""" - return self._do_request('GET', resource, full_url=full_url) - - def post_request(self, resource, payload=None): - """Perform a POST request to a CSR resource.""" - return self._do_request('POST', resource, payload=payload, - more_headers=HEADER_CONTENT_TYPE_JSON) - - def put_request(self, resource, payload=None): - """Perform a PUT request to a CSR resource.""" - return self._do_request('PUT', resource, payload=payload, - more_headers=HEADER_CONTENT_TYPE_JSON) - - def delete_request(self, resource): - """Perform a DELETE request on a CSR resource.""" - return self._do_request('DELETE', resource, - more_headers=HEADER_CONTENT_TYPE_JSON) - - def create_ike_policy(self, policy_info): - base_ike_policy_info = {u'version': u'v1', - u'local-auth-method': u'pre-share'} - base_ike_policy_info.update(policy_info) - return self.post_request('vpn-svc/ike/policies', - payload=base_ike_policy_info) - - def create_ipsec_policy(self, policy_info): - base_ipsec_policy_info = {u'mode': u'tunnel'} - base_ipsec_policy_info.update(policy_info) - return self.post_request('vpn-svc/ipsec/policies', - payload=base_ipsec_policy_info) - - def create_pre_shared_key(self, psk_info): - return self.post_request('vpn-svc/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'} - connection_info.update(base_conn_info) - return self.post_request('vpn-svc/site-to-site', - payload=connection_info) - - def configure_ike_keepalive(self, keepalive_info): - base_keepalive_info = {u'periodic': True} - keepalive_info.update(base_keepalive_info) - return self.put_request('vpn-svc/ike/keepalive', keepalive_info) - - def create_static_route(self, route_info): - return self.post_request('routing-svc/static-routes', - payload=route_info) - - def delete_static_route(self, route_id): - return self.delete_request('routing-svc/static-routes/%s' % route_id) - - def set_ipsec_connection_state(self, tunnel, admin_up=True): - """Set the IPSec site-to-site connection (tunnel) admin state. - - 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('vpn-svc/site-to-site/%s/state' % tunnel, info) - - def delete_ipsec_connection(self, conn_id): - return self.delete_request('vpn-svc/site-to-site/%s' % conn_id) - - def delete_ipsec_policy(self, policy_id): - return self.delete_request('vpn-svc/ipsec/policies/%s' % policy_id) - - def delete_ike_policy(self, policy_id): - return self.delete_request('vpn-svc/ike/policies/%s' % policy_id) - - def delete_pre_shared_key(self, key_id): - return self.delete_request('vpn-svc/ike/keyrings/%s' % key_id) - - def read_tunnel_statuses(self): - results = self.get_request('vpn-svc/site-to-site/active/sessions') - if self.status != requests.codes.OK or not results: - return [] - tunnels = [(t[u'vpn-interface-name'], t[u'status']) - for t in results['items']] - return tunnels diff --git a/neutron/services/vpn/device_drivers/cisco_ipsec.py b/neutron/services/vpn/device_drivers/cisco_ipsec.py deleted file mode 100644 index 7d73735c1..000000000 --- a/neutron/services/vpn/device_drivers/cisco_ipsec.py +++ /dev/null @@ -1,858 +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. -# -# @author: Paul Michali, Cisco Systems, Inc. - -import abc -import collections -import requests - -import netaddr -from oslo.config import cfg -from oslo import messaging -import six - -from neutron.common import exceptions -from neutron.common import rpc_compat -from neutron import context as ctx -from neutron.openstack.common import lockutils -from neutron.openstack.common import log as logging -from neutron.openstack.common import loopingcall -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 -from neutron.services.vpn.device_drivers import ( - cisco_csr_rest_client as csr_client) - - -ipsec_opts = [ - cfg.IntOpt('status_check_interval', - default=60, - help=_("Status check interval for Cisco CSR IPSec connections")) -] -cfg.CONF.register_opts(ipsec_opts, 'cisco_csr_ipsec') - -LOG = logging.getLogger(__name__) - -RollbackStep = collections.namedtuple('RollbackStep', - ['action', 'resource_id', 'title']) - - -class CsrResourceCreateFailure(exceptions.NeutronException): - message = _("Cisco CSR failed to create %(resource)s (%(which)s)") - - -class CsrAdminStateChangeFailure(exceptions.NeutronException): - message = _("Cisco CSR failed to change %(tunnel)s admin state to " - "%(state)s") - - -class CsrDriverMismatchError(exceptions.NeutronException): - message = _("Required %(resource)s attribute %(attr)s mapping for Cisco " - "CSR is missing in device driver") - - -class CsrUnknownMappingError(exceptions.NeutronException): - message = _("Device driver does not have a mapping of '%(value)s for " - "attribute %(attr)s of %(resource)s") - - -def find_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(_("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(_("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(_("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] - except KeyError as ke: - LOG.error(_("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(_("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(_("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(_("Ignoring Cisco CSR for router %s - " - "local tunnel is not an IP address"), - for_router) - continue - csrs_found[for_router] = {'rest_mgmt': rest_mgmt_ip, - 'tunnel_ip': tunnel_ip, - 'username': username, - 'password': password, - 'timeout': timeout} - - LOG.debug(_("Found CSR for router %(router)s: %(info)s"), - {'router': for_router, - 'info': csrs_found[for_router]}) - return csrs_found - - -class CiscoCsrIPsecVpnDriverApi(rpc_compat.RpcProxy): - """RPC API for agent to plugin messaging.""" - - def get_vpn_services_on_host(self, context, host): - """Get list of vpnservices on this host. - - The vpnservices including related ipsec_site_connection, - ikepolicy, ipsecpolicy, and Cisco info on this host. - """ - return self.call(context, - self.make_msg('get_vpn_services_on_host', - host=host), - topic=self.topic) - - def update_status(self, context, status): - """Update status for all VPN services and connections.""" - return self.cast(context, - self.make_msg('update_status', - status=status), - topic=self.topic) - - -@six.add_metaclass(abc.ABCMeta) -class CiscoCsrIPsecDriver(device_drivers.DeviceDriver): - """Cisco CSR 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' - - # TODO(ihrachys): we can't use RpcCallback here due to inheritance - # issues - target = messaging.Target(version=RPC_API_VERSION) - - def __init__(self, agent, host): - self.host = host - self.conn = rpc_compat.create_connection(new=True) - context = ctx.get_admin_context_without_session() - node_topic = '%s.%s' % (topics.CISCO_IPSEC_AGENT_TOPIC, self.host) - - self.service_state = {} - - self.endpoints = [self] - self.conn.create_consumer(node_topic, self.endpoints, fanout=False) - self.conn.consume_in_threads() - self.agent_rpc = ( - CiscoCsrIPsecVpnDriverApi(topics.CISCO_IPSEC_DRIVER_TOPIC, '1.0')) - self.periodic_report = loopingcall.FixedIntervalLoopingCall( - self.report_status, context) - self.periodic_report.start( - interval=agent.conf.cisco_csr_ipsec.status_check_interval) - - csrs_found = find_available_csrs_from_config(cfg.CONF.config_file) - if csrs_found: - LOG.info(_("Loaded %(num)d Cisco CSR configuration%(plural)s"), - {'num': len(csrs_found), - 'plural': 's'[len(csrs_found) == 1:]}) - else: - raise SystemExit(_('No Cisco CSR configurations found in: %s') % - cfg.CONF.config_file) - self.csrs = dict([(k, csr_client.CsrRestClient(v['rest_mgmt'], - v['tunnel_ip'], - v['username'], - v['password'], - v['timeout'])) - for k, v in csrs_found.items()]) - - def vpnservice_updated(self, context, **kwargs): - """Handle VPNaaS service driver change notifications.""" - LOG.debug(_("Handling VPN service update notification '%s'"), - kwargs.get('reason', '')) - self.sync(context, []) - - def create_vpn_service(self, service_data): - """Create new entry to track VPN service and its connections.""" - vpn_service_id = service_data['id'] - vpn_service_router = service_data['external_ip'] - self.service_state[vpn_service_id] = CiscoCsrVpnService( - service_data, self.csrs.get(vpn_service_router)) - return self.service_state[vpn_service_id] - - def update_connection(self, context, vpn_service_id, conn_data): - """Handle notification for a single IPSec connection.""" - vpn_service = self.service_state[vpn_service_id] - conn_id = conn_data['id'] - conn_is_admin_up = conn_data[u'admin_state_up'] - - if conn_id in vpn_service.conn_state: # Existing connection... - ipsec_conn = vpn_service.conn_state[conn_id] - config_changed = ipsec_conn.check_for_changes(conn_data) - if config_changed: - LOG.debug(_("Update: Existing connection %s changed"), conn_id) - ipsec_conn.delete_ipsec_site_connection(context, conn_id) - ipsec_conn.create_ipsec_site_connection(context, conn_data) - ipsec_conn.conn_info = conn_data - - if ipsec_conn.forced_down: - if vpn_service.is_admin_up and conn_is_admin_up: - LOG.debug(_("Update: Connection %s no longer admin down"), - conn_id) - ipsec_conn.set_admin_state(is_up=True) - ipsec_conn.forced_down = False - else: - if not vpn_service.is_admin_up or not conn_is_admin_up: - LOG.debug(_("Update: Connection %s forced to admin down"), - conn_id) - ipsec_conn.set_admin_state(is_up=False) - ipsec_conn.forced_down = True - else: # New connection... - ipsec_conn = vpn_service.create_connection(conn_data) - ipsec_conn.create_ipsec_site_connection(context, conn_data) - if not vpn_service.is_admin_up or not conn_is_admin_up: - LOG.debug(_("Update: Created new connection %s in admin down " - "state"), conn_id) - ipsec_conn.set_admin_state(is_up=False) - ipsec_conn.forced_down = True - else: - LOG.debug(_("Update: Created new connection %s"), conn_id) - - ipsec_conn.is_dirty = False - ipsec_conn.last_status = conn_data['status'] - ipsec_conn.is_admin_up = conn_is_admin_up - return ipsec_conn - - def update_service(self, context, service_data): - """Handle notification for a single VPN Service and its connections.""" - vpn_service_id = service_data['id'] - csr_id = service_data['external_ip'] - if csr_id not in self.csrs: - LOG.error(_("Update: Skipping VPN service %(service)s as it's " - "router (%(csr_id)s is not associated with a Cisco " - "CSR"), {'service': vpn_service_id, 'csr_id': csr_id}) - return - - if vpn_service_id in self.service_state: - LOG.debug(_("Update: Existing VPN service %s detected"), - vpn_service_id) - vpn_service = self.service_state[vpn_service_id] - else: - LOG.debug(_("Update: New VPN service %s detected"), vpn_service_id) - vpn_service = self.create_vpn_service(service_data) - - vpn_service.is_dirty = False - vpn_service.connections_removed = False - vpn_service.last_status = service_data['status'] - vpn_service.is_admin_up = service_data[u'admin_state_up'] - for conn_data in service_data['ipsec_conns']: - self.update_connection(context, vpn_service_id, conn_data) - LOG.debug(_("Update: Completed update processing")) - return vpn_service - - def update_all_services_and_connections(self, context): - """Update services and connections based on plugin info. - - Perform any create and update operations and then update status. - Mark every visited connection as no longer "dirty" so they will - not be deleted at end of sync processing. - """ - services_data = self.agent_rpc.get_vpn_services_on_host(context, - self.host) - LOG.debug("Sync updating for %d VPN services", len(services_data)) - vpn_services = [] - for service_data in services_data: - vpn_service = self.update_service(context, service_data) - if vpn_service: - vpn_services.append(vpn_service) - return vpn_services - - def mark_existing_connections_as_dirty(self): - """Mark all existing connections as "dirty" for sync.""" - service_count = 0 - connection_count = 0 - for service_state in self.service_state.values(): - service_state.is_dirty = True - service_count += 1 - for conn_id in service_state.conn_state: - service_state.conn_state[conn_id].is_dirty = True - connection_count += 1 - LOG.debug(_("Mark: %(service)d VPN services and %(conn)d IPSec " - "connections marked dirty"), {'service': service_count, - 'conn': connection_count}) - - def remove_unknown_connections(self, context): - """Remove connections that are not known by service driver.""" - service_count = 0 - connection_count = 0 - for vpn_service_id, vpn_service in self.service_state.items(): - dirty = [c_id for c_id, c in vpn_service.conn_state.items() - if c.is_dirty] - vpn_service.connections_removed = len(dirty) > 0 - for conn_id in dirty: - conn_state = vpn_service.conn_state[conn_id] - conn_state.delete_ipsec_site_connection(context, conn_id) - connection_count += 1 - del vpn_service.conn_state[conn_id] - if vpn_service.is_dirty: - service_count += 1 - del self.service_state[vpn_service_id] - elif dirty: - self.connections_removed = True - LOG.debug(_("Sweep: Removed %(service)d dirty VPN service%(splural)s " - "and %(conn)d dirty IPSec connection%(cplural)s"), - {'service': service_count, 'conn': connection_count, - 'splural': 's'[service_count == 1:], - 'cplural': 's'[connection_count == 1:]}) - - def build_report_for_connections_on(self, vpn_service): - """Create the report fragment for IPSec connections on a service. - - Collect the current status from the Cisco CSR and use that to update - the status and generate report fragment for each connection on the - service. If there is no status information, or no change, then no - report info will be created for the connection. The combined report - data is returned. - """ - LOG.debug(_("Report: Collecting status for IPSec connections on VPN " - "service %s"), vpn_service.service_id) - tunnels = vpn_service.get_ipsec_connections_status() - report = {} - for connection in vpn_service.conn_state.values(): - if connection.forced_down: - LOG.debug(_("Connection %s forced down"), connection.conn_id) - current_status = constants.DOWN - else: - current_status = connection.find_current_status_in(tunnels) - LOG.debug(_("Connection %(conn)s reported %(status)s"), - {'conn': connection.conn_id, - 'status': current_status}) - frag = connection.update_status_and_build_report(current_status) - if frag: - LOG.debug(_("Report: Adding info for IPSec connection %s"), - connection.conn_id) - report.update(frag) - return report - - def build_report_for_service(self, vpn_service): - """Create the report info for a VPN service and its IPSec connections. - - Get the report info for the connections on the service, and include - it into the report info for the VPN service. If there is no report - info for the connection, then no change has occurred and no report - will be generated. If there is only one connection for the service, - we'll set the service state to match the connection (with ERROR seen - as DOWN). - """ - conn_report = self.build_report_for_connections_on(vpn_service) - if conn_report or vpn_service.connections_removed: - pending_handled = plugin_utils.in_pending_status( - vpn_service.last_status) - vpn_service.update_last_status() - LOG.debug(_("Report: Adding info for VPN service %s"), - vpn_service.service_id) - return {u'id': vpn_service.service_id, - u'status': vpn_service.last_status, - u'updated_pending_status': pending_handled, - u'ipsec_site_connections': conn_report} - else: - return {} - - @lockutils.synchronized('vpn-agent', 'neutron-') - def report_status(self, context): - """Report status of all VPN services and IPSec connections to plugin. - - This is called periodically by the agent, to push up changes in - status. Use a lock to serialize access to (and changing of) - running state. - """ - return self.report_status_internal(context) - - def report_status_internal(self, context): - """Generate report and send to plugin, if anything changed.""" - service_report = [] - LOG.debug(_("Report: Starting status report processing")) - for vpn_service_id, vpn_service in self.service_state.items(): - LOG.debug(_("Report: Collecting status for VPN service %s"), - vpn_service_id) - report = self.build_report_for_service(vpn_service) - if report: - service_report.append(report) - if service_report: - LOG.info(_("Sending status report update to plugin")) - self.agent_rpc.update_status(context, service_report) - LOG.debug(_("Report: Completed status report processing")) - return service_report - - @lockutils.synchronized('vpn-agent', 'neutron-') - def sync(self, context, routers): - """Synchronize with plugin and report current status. - - Mark all "known" services/connections as dirty, update them based on - information from the plugin, remove (sweep) any connections that are - not updated (dirty), and report updates, if any, back to plugin. - Called when update/delete a service or create/update/delete a - connection (vpnservice_updated message), or router change - (_process_routers). - - Use lock to serialize access (and changes) to running state for VPN - service and IPsec connections. - """ - self.mark_existing_connections_as_dirty() - self.update_all_services_and_connections(context) - self.remove_unknown_connections(context) - self.report_status_internal(context) - - def create_router(self, process_id): - """Actions taken when router created.""" - # Note: Since Cisco CSR is running out-of-band, nothing to do here - pass - - def destroy_router(self, process_id): - """Actions taken when router deleted.""" - # Note: Since Cisco CSR is running out-of-band, nothing to do here - pass - - -class CiscoCsrVpnService(object): - - """Maintains state/status information for a service and its connections.""" - - def __init__(self, service_data, csr): - self.service_id = service_data['id'] - self.conn_state = {} - self.csr = csr - self.is_admin_up = True - # TODO(pcm) FUTURE - handle sharing of policies - - def create_connection(self, conn_data): - conn_id = conn_data['id'] - self.conn_state[conn_id] = CiscoCsrIPSecConnection(conn_data, self.csr) - return self.conn_state[conn_id] - - def get_connection(self, conn_id): - return self.conn_state.get(conn_id) - - def conn_status(self, conn_id): - conn_state = self.get_connection(conn_id) - if conn_state: - return conn_state.last_status - - def snapshot_conn_state(self, ipsec_conn): - """Create/obtain connection state and save current status.""" - conn_state = self.conn_state.setdefault( - ipsec_conn['id'], CiscoCsrIPSecConnection(ipsec_conn, self.csr)) - conn_state.last_status = ipsec_conn['status'] - conn_state.is_dirty = False - return conn_state - - STATUS_MAP = {'ERROR': constants.ERROR, - 'UP-ACTIVE': constants.ACTIVE, - 'UP-IDLE': constants.ACTIVE, - 'UP-NO-IKE': constants.ACTIVE, - 'DOWN': constants.DOWN, - 'DOWN-NEGOTIATING': constants.DOWN} - - def get_ipsec_connections_status(self): - """Obtain current status of all tunnels on a Cisco CSR. - - Convert them to OpenStack status values. - """ - tunnels = self.csr.read_tunnel_statuses() - for tunnel in tunnels: - LOG.debug("CSR Reports %(tunnel)s status '%(status)s'", - {'tunnel': tunnel[0], 'status': tunnel[1]}) - return dict(map(lambda x: (x[0], self.STATUS_MAP[x[1]]), tunnels)) - - def find_matching_connection(self, tunnel_id): - """Find IPSec connection using Cisco CSR tunnel specified, if any.""" - for connection in self.conn_state.values(): - if connection.tunnel == tunnel_id: - return connection.conn_id - - def no_connections_up(self): - return not any(c.last_status == 'ACTIVE' - for c in self.conn_state.values()) - - def update_last_status(self): - if not self.is_admin_up or self.no_connections_up(): - self.last_status = constants.DOWN - else: - self.last_status = constants.ACTIVE - - -class CiscoCsrIPSecConnection(object): - - """State and actions for IPSec site-to-site connections.""" - - def __init__(self, conn_info, csr): - self.conn_info = conn_info - self.csr = csr - self.steps = [] - self.forced_down = False - self.changed = False - - @property - def conn_id(self): - return self.conn_info['id'] - - @property - def is_admin_up(self): - return self.conn_info['admin_state_up'] - - @is_admin_up.setter - def is_admin_up(self, is_up): - self.conn_info['admin_state_up'] = is_up - - @property - def tunnel(self): - return self.conn_info['cisco']['site_conn_id'] - - def check_for_changes(self, curr_conn): - return not all([self.conn_info[attr] == curr_conn[attr] - for attr in ('mtu', 'psk', 'peer_address', - 'peer_cidrs', 'ike_policy', - 'ipsec_policy', 'cisco')]) - - def find_current_status_in(self, statuses): - if self.tunnel in statuses: - return statuses[self.tunnel] - else: - return constants.ERROR - - def update_status_and_build_report(self, current_status): - if current_status != self.last_status: - pending_handled = plugin_utils.in_pending_status(self.last_status) - self.last_status = current_status - return {self.conn_id: {'status': current_status, - 'updated_pending_status': pending_handled}} - else: - return {} - - DIALECT_MAP = {'ike_policy': {'name': 'IKE Policy', - 'v1': u'v1', - # auth_algorithm -> hash - 'sha1': u'sha', - # encryption_algorithm -> encryption - '3des': u'3des', - 'aes-128': u'aes', - 'aes-192': u'aes192', - 'aes-256': u'aes256', - # pfs -> dhGroup - 'group2': 2, - 'group5': 5, - 'group14': 14}, - 'ipsec_policy': {'name': 'IPSec Policy', - # auth_algorithm -> esp-authentication - 'sha1': u'esp-sha-hmac', - # transform_protocol -> ah - 'esp': None, - 'ah': u'ah-sha-hmac', - 'ah-esp': u'ah-sha-hmac', - # encryption_algorithm -> esp-encryption - '3des': u'esp-3des', - 'aes-128': u'esp-aes', - 'aes-192': u'esp-192-aes', - 'aes-256': u'esp-256-aes', - # pfs -> pfs - 'group2': u'group2', - 'group5': u'group5', - 'group14': u'group14'}} - - def translate_dialect(self, resource, attribute, info): - """Map VPNaaS attributes values to CSR values for a resource.""" - name = self.DIALECT_MAP[resource]['name'] - if attribute not in info: - raise CsrDriverMismatchError(resource=name, attr=attribute) - value = info[attribute].lower() - if value in self.DIALECT_MAP[resource]: - return self.DIALECT_MAP[resource][value] - raise CsrUnknownMappingError(resource=name, attr=attribute, - value=value) - - def create_psk_info(self, psk_id, conn_info): - """Collect/create attributes needed for pre-shared key.""" - return {u'keyring-name': psk_id, - u'pre-shared-key-list': [ - {u'key': conn_info['psk'], - u'encrypted': False, - u'peer-address': conn_info['peer_address']}]} - - def create_ike_policy_info(self, ike_policy_id, conn_info): - """Collect/create/map attributes needed for IKE policy.""" - for_ike = 'ike_policy' - policy_info = conn_info[for_ike] - version = self.translate_dialect(for_ike, - 'ike_version', - policy_info) - encrypt_algorithm = self.translate_dialect(for_ike, - 'encryption_algorithm', - policy_info) - auth_algorithm = self.translate_dialect(for_ike, - 'auth_algorithm', - policy_info) - group = self.translate_dialect(for_ike, - 'pfs', - policy_info) - lifetime = policy_info['lifetime_value'] - return {u'version': version, - u'priority-id': ike_policy_id, - u'encryption': encrypt_algorithm, - u'hash': auth_algorithm, - u'dhGroup': group, - u'lifetime': lifetime} - - def create_ipsec_policy_info(self, ipsec_policy_id, info): - """Collect/create attributes needed for IPSec policy. - - Note: OpenStack will provide a default encryption algorithm, if one is - not provided, so a authentication only configuration of (ah, sha1), - which maps to ah-sha-hmac transform protocol, cannot be selected. - As a result, we'll always configure the encryption algorithm, and - will select ah-sha-hmac for transform protocol. - """ - - for_ipsec = 'ipsec_policy' - policy_info = info[for_ipsec] - transform_protocol = self.translate_dialect(for_ipsec, - 'transform_protocol', - policy_info) - auth_algorithm = self.translate_dialect(for_ipsec, - 'auth_algorithm', - policy_info) - encrypt_algorithm = self.translate_dialect(for_ipsec, - 'encryption_algorithm', - policy_info) - group = self.translate_dialect(for_ipsec, 'pfs', policy_info) - lifetime = policy_info['lifetime_value'] - settings = {u'policy-id': ipsec_policy_id, - u'protection-suite': { - u'esp-encryption': encrypt_algorithm, - u'esp-authentication': auth_algorithm}, - u'lifetime-sec': lifetime, - u'pfs': group, - u'anti-replay-window-size': u'disable'} - if transform_protocol: - settings[u'protection-suite'][u'ah'] = transform_protocol - return settings - - def create_site_connection_info(self, site_conn_id, ipsec_policy_id, - conn_info): - """Collect/create attributes needed for the IPSec connection.""" - # TODO(pcm) Enable, once CSR is embedded as a Neutron router - # gw_ip = vpnservice['external_ip'] (need to pass in) - mtu = conn_info['mtu'] - return { - u'vpn-interface-name': site_conn_id, - u'ipsec-policy-id': ipsec_policy_id, - u'local-device': { - # TODO(pcm): FUTURE - Get CSR port of interface with - # local subnet - u'ip-address': u'GigabitEthernet3', - # TODO(pcm): FUTURE - Get IP address of router's public - # I/F, once CSR is used as embedded router. - u'tunnel-ip-address': self.csr.tunnel_ip - # u'tunnel-ip-address': u'%s' % gw_ip - }, - u'remote-device': { - u'tunnel-ip-address': conn_info['peer_address'] - }, - u'mtu': mtu - } - - def create_routes_info(self, site_conn_id, conn_info): - """Collect/create attributes for static routes.""" - routes_info = [] - for peer_cidr in conn_info.get('peer_cidrs', []): - route = {u'destination-network': peer_cidr, - u'outgoing-interface': site_conn_id} - route_id = csr_client.make_route_id(peer_cidr, site_conn_id) - routes_info.append((route_id, route)) - return routes_info - - def _check_create(self, resource, which): - """Determine if REST create request was successful.""" - if self.csr.status == requests.codes.CREATED: - LOG.debug("%(resource)s %(which)s is configured", - {'resource': resource, 'which': which}) - return - LOG.error(_("Unable to create %(resource)s %(which)s: " - "%(status)d"), - {'resource': resource, 'which': which, - 'status': self.csr.status}) - # ToDO(pcm): Set state to error - raise CsrResourceCreateFailure(resource=resource, which=which) - - def do_create_action(self, action_suffix, info, resource_id, title): - """Perform a single REST step for IPSec site connection create.""" - create_action = 'create_%s' % action_suffix - try: - getattr(self.csr, create_action)(info) - except AttributeError: - LOG.exception(_("Internal error - '%s' is not defined"), - create_action) - raise CsrResourceCreateFailure(resource=title, - which=resource_id) - self._check_create(title, resource_id) - self.steps.append(RollbackStep(action_suffix, resource_id, title)) - - def _verify_deleted(self, status, resource, which): - """Determine if REST delete request was successful.""" - if status in (requests.codes.NO_CONTENT, requests.codes.NOT_FOUND): - LOG.debug("%(resource)s configuration %(which)s was removed", - {'resource': resource, 'which': which}) - else: - LOG.warning(_("Unable to delete %(resource)s %(which)s: " - "%(status)d"), {'resource': resource, - 'which': which, - 'status': status}) - - def do_rollback(self): - """Undo create steps that were completed successfully.""" - for step in reversed(self.steps): - delete_action = 'delete_%s' % step.action - LOG.debug(_("Performing rollback action %(action)s for " - "resource %(resource)s"), {'action': delete_action, - 'resource': step.title}) - try: - getattr(self.csr, delete_action)(step.resource_id) - except AttributeError: - LOG.exception(_("Internal error - '%s' is not defined"), - delete_action) - raise CsrResourceCreateFailure(resource=step.title, - which=step.resource_id) - self._verify_deleted(self.csr.status, step.title, step.resource_id) - self.steps = [] - - def create_ipsec_site_connection(self, context, conn_info): - """Creates an IPSec site-to-site connection on CSR. - - Create the PSK, IKE policy, IPSec policy, connection, static route, - and (future) DPD. - """ - # Get all the IDs - conn_id = conn_info['id'] - psk_id = conn_id - site_conn_id = conn_info['cisco']['site_conn_id'] - ike_policy_id = conn_info['cisco']['ike_policy_id'] - ipsec_policy_id = conn_info['cisco']['ipsec_policy_id'] - - LOG.debug(_('Creating IPSec connection %s'), conn_id) - # Get all the attributes needed to create - try: - psk_info = self.create_psk_info(psk_id, conn_info) - ike_policy_info = self.create_ike_policy_info(ike_policy_id, - conn_info) - ipsec_policy_info = self.create_ipsec_policy_info(ipsec_policy_id, - conn_info) - connection_info = self.create_site_connection_info(site_conn_id, - ipsec_policy_id, - conn_info) - routes_info = self.create_routes_info(site_conn_id, conn_info) - except (CsrUnknownMappingError, CsrDriverMismatchError) as e: - LOG.exception(e) - return - - try: - self.do_create_action('pre_shared_key', psk_info, - conn_id, 'Pre-Shared Key') - self.do_create_action('ike_policy', ike_policy_info, - ike_policy_id, 'IKE Policy') - self.do_create_action('ipsec_policy', ipsec_policy_info, - ipsec_policy_id, 'IPSec Policy') - self.do_create_action('ipsec_connection', connection_info, - site_conn_id, 'IPSec Connection') - - # TODO(pcm): FUTURE - Do DPD for v1 and handle if >1 connection - # and different DPD settings - for route_id, route_info in routes_info: - self.do_create_action('static_route', route_info, - route_id, 'Static Route') - except CsrResourceCreateFailure: - self.do_rollback() - LOG.info(_("FAILED: Create of IPSec site-to-site connection %s"), - conn_id) - else: - LOG.info(_("SUCCESS: Created IPSec site-to-site connection %s"), - conn_id) - - def delete_ipsec_site_connection(self, context, conn_id): - """Delete the site-to-site IPSec connection. - - This will be best effort and will continue, if there are any - failures. - """ - LOG.debug(_('Deleting IPSec connection %s'), conn_id) - if not self.steps: - LOG.warning(_('Unable to find connection %s'), conn_id) - else: - self.do_rollback() - - LOG.info(_("SUCCESS: Deleted IPSec site-to-site connection %s"), - conn_id) - - def set_admin_state(self, is_up): - """Change the admin state for the IPSec connection.""" - self.csr.set_ipsec_connection_state(self.tunnel, admin_up=is_up) - if self.csr.status != requests.codes.NO_CONTENT: - state = "UP" if is_up else "DOWN" - LOG.error(_("Unable to change %(tunnel)s admin state to " - "%(state)s"), {'tunnel': self.tunnel, 'state': state}) - raise CsrAdminStateChangeFailure(tunnel=self.tunnel, state=state) diff --git a/neutron/services/vpn/device_drivers/ipsec.py b/neutron/services/vpn/device_drivers/ipsec.py deleted file mode 100644 index aef47919c..000000000 --- a/neutron/services/vpn/device_drivers/ipsec.py +++ /dev/null @@ -1,713 +0,0 @@ -# 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 oslo import messaging -import six - -from neutron.agent.linux import ip_lib -from neutron.agent.linux import utils -from neutron.common import rpc_compat -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.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 -} - -IPSEC_CONNS = 'ipsec_site_connections' - - -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) - - -@six.add_metaclass(abc.ABCMeta) -class BaseSwanProcess(): - """Swan Family Process Manager - - This class manages start/restart/stop ipsec process. - This class create/delete config template - """ - - 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.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.update_vpnservice(vpnservice) - - 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 update_vpnservice(self, vpnservice): - self.vpnservice = vpnservice - self.translate_dialect() - - 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', - ]) - #clean connection_status info - self.connection_status = {} - - -class IPsecVpnDriverApi(rpc_compat.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) - - -@six.add_metaclass(abc.ABCMeta) -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' - - # TODO(ihrachys): we can't use RpcCallback here due to inheritance - # issues - target = messaging.Target(version=RPC_API_VERSION) - - 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_compat.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.endpoints = [self] - self.conn.create_consumer(node_topic, self.endpoints, fanout=False) - self.conn.consume_in_threads() - 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 _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 doesn'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 - elif vpnservice: - process.update_vpnservice(vpnservice) - 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 update_downed_connections(self, process_id, new_status): - """Update info to be reported, if connections just went down. - - If there is no longer any information for a connection, because it - has been removed (e.g. due to an admin down of VPN service or IPSec - connection), but there was previous status information for the - connection, mark the connection as down for reporting purposes. - """ - if process_id in self.process_status_cache: - for conn in self.process_status_cache[process_id][IPSEC_CONNS]: - if conn not in new_status[IPSEC_CONNS]: - new_status[IPSEC_CONNS][conn] = { - 'status': constants.DOWN, - 'updated_pending_status': True - } - - 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.update_downed_connections(process.id, new_status) - status_changed_vpn_services.append(new_status) - self.process_status_cache[process.id] = ( - self.copy_process_status(process)) - # 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) diff --git a/neutron/services/vpn/device_drivers/template/openswan/ipsec.conf.template b/neutron/services/vpn/device_drivers/template/openswan/ipsec.conf.template deleted file mode 100644 index 546e27ec6..000000000 --- a/neutron/services/vpn/device_drivers/template/openswan/ipsec.conf.template +++ /dev/null @@ -1,64 +0,0 @@ -# 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 if ipsec_site_connection.admin_state_up -%}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 %} diff --git a/neutron/services/vpn/device_drivers/template/openswan/ipsec.secret.template b/neutron/services/vpn/device_drivers/template/openswan/ipsec.secret.template deleted file mode 100644 index 8302e859f..000000000 --- a/neutron/services/vpn/device_drivers/template/openswan/ipsec.secret.template +++ /dev/null @@ -1,3 +0,0 @@ -# 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 %} diff --git a/neutron/services/vpn/plugin.py b/neutron/services/vpn/plugin.py deleted file mode 100644 index 771188d65..000000000 --- a/neutron/services/vpn/plugin.py +++ /dev/null @@ -1,107 +0,0 @@ - -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# -# (c) Copyright 2013 Hewlett-Packard Development Company, L.P. -# 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. -# -# @author: Swaminathan Vasudevan, Hewlett-Packard - -from neutron.db.vpn import vpn_db -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services import service_base - -LOG = logging.getLogger(__name__) - - -class VPNPlugin(vpn_db.VPNPluginDb): - - """Implementation of the VPN Service Plugin. - - This class manages the workflow of VPNaaS request/response. - Most DB related works are implemented in class - vpn_db.VPNPluginDb. - """ - supported_extension_aliases = ["vpnaas", "service-type"] - - -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__() - # Load the service driver from neutron.conf. - drivers, default_provider = service_base.load_drivers( - constants.VPN, self) - LOG.info(_("VPN plugin using service driver: %s"), default_provider) - self.ipsec_driver = drivers[default_provider] - - 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 update_vpnservice(self, context, vpnservice_id, vpnservice): - old_vpn_service = self.get_vpnservice(context, vpnservice_id) - new_vpn_service = super( - VPNDriverPlugin, self).update_vpnservice(context, vpnservice_id, - vpnservice) - driver = self._get_driver_for_vpnservice(old_vpn_service) - driver.update_vpnservice(context, old_vpn_service, new_vpn_service) - return new_vpn_service - - 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) diff --git a/neutron/services/vpn/service_drivers/__init__.py b/neutron/services/vpn/service_drivers/__init__.py deleted file mode 100644 index e95637e9b..000000000 --- a/neutron/services/vpn/service_drivers/__init__.py +++ /dev/null @@ -1,92 +0,0 @@ -# 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 six - -from neutron.common import rpc_compat -from neutron import manager -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants - -LOG = logging.getLogger(__name__) - - -@six.add_metaclass(abc.ABCMeta) -class VpnDriver(object): - - def __init__(self, service_plugin): - self.service_plugin = service_plugin - - @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 - - -class BaseIPsecVpnAgentApi(rpc_compat.RpcProxy): - """Base class for IPSec API to agent.""" - - def __init__(self, to_agent_topic, topic, default_version): - self.to_agent_topic = to_agent_topic - super(BaseIPsecVpnAgentApi, self).__init__(topic, default_version) - - def _agent_notification(self, context, method, router_id, - version=None, **kwargs): - """Notify update for the agent. - - This method will find where is the router, and - dispatch notification for the agent. - """ - admin_context = context.is_admin and context or context.elevated() - plugin = manager.NeutronManager.get_service_plugins().get( - constants.L3_ROUTER_NAT) - if not version: - version = self.RPC_API_VERSION - l3_agents = plugin.get_l3_agents_hosting_routers( - admin_context, [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 %(args)s'), - {'topic': self.to_agent_topic, - 'host': l3_agent.host, - 'method': method, - 'args': kwargs}) - self.cast( - context, self.make_msg(method, **kwargs), - version=version, - topic='%s.%s' % (self.to_agent_topic, l3_agent.host)) - - def vpnservice_updated(self, context, router_id, **kwargs): - """Send update event of vpnservices.""" - self._agent_notification(context, 'vpnservice_updated', router_id, - **kwargs) diff --git a/neutron/services/vpn/service_drivers/cisco_csr_db.py b/neutron/services/vpn/service_drivers/cisco_csr_db.py deleted file mode 100644 index e1f0760cd..000000000 --- a/neutron/services/vpn/service_drivers/cisco_csr_db.py +++ /dev/null @@ -1,239 +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. -# -# @author: Paul Michali, Cisco Systems, Inc. - -import sqlalchemy as sa -from sqlalchemy.orm import exc as sql_exc - -from neutron.common import exceptions -from neutron.db import model_base -from neutron.db import models_v2 -from neutron.db.vpn import vpn_db -from neutron.openstack.common.db import exception as db_exc -from neutron.openstack.common import log as logging - -LOG = logging.getLogger(__name__) - -# Note: Artificially limit these to reduce mapping table size and performance -# Tunnel can be 0..7FFFFFFF, IKE policy can be 1..10000, IPSec policy can be -# 1..31 characters long. -MAX_CSR_TUNNELS = 10000 -MAX_CSR_IKE_POLICIES = 2000 -MAX_CSR_IPSEC_POLICIES = 2000 - -TUNNEL = 'Tunnel' -IKE_POLICY = 'IKE Policy' -IPSEC_POLICY = 'IPSec Policy' - -MAPPING_LIMITS = {TUNNEL: (0, MAX_CSR_TUNNELS), - IKE_POLICY: (1, MAX_CSR_IKE_POLICIES), - IPSEC_POLICY: (1, MAX_CSR_IPSEC_POLICIES)} - - -class CsrInternalError(exceptions.NeutronException): - message = _("Fatal - %(reason)s") - - -class IdentifierMap(model_base.BASEV2, models_v2.HasTenant): - - """Maps OpenStack IDs to compatible numbers for Cisco CSR.""" - - __tablename__ = 'cisco_csr_identifier_map' - - ipsec_site_conn_id = sa.Column(sa.String(64), - sa.ForeignKey('ipsec_site_connections.id', - ondelete="CASCADE"), - primary_key=True) - csr_tunnel_id = sa.Column(sa.Integer, nullable=False) - csr_ike_policy_id = sa.Column(sa.Integer, nullable=False) - csr_ipsec_policy_id = sa.Column(sa.Integer, nullable=False) - - -def get_next_available_id(session, table_field, id_type): - """Find first unused id for the specified field in IdentifierMap table. - - As entries are removed, find the first "hole" and return that as the - next available ID. To improve performance, artificially limit - the number of entries to a smaller range. Currently, these IDs are - globally unique. Could enhance in the future to be unique per router - (CSR). - """ - min_value = MAPPING_LIMITS[id_type][0] - max_value = MAPPING_LIMITS[id_type][1] - rows = session.query(table_field).order_by(table_field) - used_ids = set([row[0] for row in rows]) - all_ids = set(range(min_value, max_value + min_value)) - available_ids = all_ids - used_ids - if not available_ids: - msg = _("No available Cisco CSR %(type)s IDs from " - "%(min)d..%(max)d") % {'type': id_type, - 'min': min_value, - 'max': max_value} - LOG.error(msg) - raise IndexError(msg) - return available_ids.pop() - - -def get_next_available_tunnel_id(session): - """Find first available tunnel ID from 0..MAX_CSR_TUNNELS-1.""" - return get_next_available_id(session, IdentifierMap.csr_tunnel_id, - TUNNEL) - - -def get_next_available_ike_policy_id(session): - """Find first available IKE Policy ID from 1..MAX_CSR_IKE_POLICIES.""" - return get_next_available_id(session, IdentifierMap.csr_ike_policy_id, - IKE_POLICY) - - -def get_next_available_ipsec_policy_id(session): - """Find first available IPSec Policy ID from 1..MAX_CSR_IKE_POLICIES.""" - return get_next_available_id(session, IdentifierMap.csr_ipsec_policy_id, - IPSEC_POLICY) - - -def find_conn_with_policy(policy_field, policy_id, conn_id, session): - """Return ID of another conneciton (if any) that uses same policy ID.""" - qry = session.query(vpn_db.IPsecSiteConnection.id) - match = qry.filter_request( - policy_field == policy_id, - vpn_db.IPsecSiteConnection.id != conn_id).first() - if match: - return match[0] - - -def find_connection_using_ike_policy(ike_policy_id, conn_id, session): - """Return ID of another connection that uses same IKE policy ID.""" - return find_conn_with_policy(vpn_db.IPsecSiteConnection.ikepolicy_id, - ike_policy_id, conn_id, session) - - -def find_connection_using_ipsec_policy(ipsec_policy_id, conn_id, session): - """Return ID of another connection that uses same IPSec policy ID.""" - return find_conn_with_policy(vpn_db.IPsecSiteConnection.ipsecpolicy_id, - ipsec_policy_id, conn_id, session) - - -def lookup_policy(policy_type, policy_field, conn_id, session): - """Obtain specified policy's mapping from other connection.""" - try: - return session.query(policy_field).filter_by( - ipsec_site_conn_id=conn_id).one()[0] - except sql_exc.NoResultFound: - msg = _("Database inconsistency between IPSec connection and " - "Cisco CSR mapping table (%s)") % policy_type - raise CsrInternalError(reason=msg) - - -def lookup_ike_policy_id_for(conn_id, session): - """Obtain existing Cisco CSR IKE policy ID from another connection.""" - return lookup_policy(IKE_POLICY, IdentifierMap.csr_ike_policy_id, - conn_id, session) - - -def lookup_ipsec_policy_id_for(conn_id, session): - """Obtain existing Cisco CSR IPSec policy ID from another connection.""" - return lookup_policy(IPSEC_POLICY, IdentifierMap.csr_ipsec_policy_id, - conn_id, session) - - -def determine_csr_policy_id(policy_type, conn_policy_field, map_policy_field, - policy_id, conn_id, session): - """Use existing or reserve a new policy ID for Cisco CSR use. - - TODO(pcm) FUTURE: Once device driver adds support for IKE/IPSec policy - ID sharing, add call to find_conn_with_policy() to find used ID and - then call lookup_policy() to find the current mapping for that ID. - """ - csr_id = get_next_available_id(session, map_policy_field, policy_type) - LOG.debug(_("Reserved new CSR ID %(csr_id)d for %(policy)s " - "ID %(policy_id)s"), {'csr_id': csr_id, - 'policy': policy_type, - 'policy_id': policy_id}) - return csr_id - - -def determine_csr_ike_policy_id(ike_policy_id, conn_id, session): - """Use existing, or reserve a new IKE policy ID for Cisco CSR.""" - return determine_csr_policy_id(IKE_POLICY, - vpn_db.IPsecSiteConnection.ikepolicy_id, - IdentifierMap.csr_ike_policy_id, - ike_policy_id, conn_id, session) - - -def determine_csr_ipsec_policy_id(ipsec_policy_id, conn_id, session): - """Use existing, or reserve a new IPSec policy ID for Cisco CSR.""" - return determine_csr_policy_id(IPSEC_POLICY, - vpn_db.IPsecSiteConnection.ipsecpolicy_id, - IdentifierMap.csr_ipsec_policy_id, - ipsec_policy_id, conn_id, session) - - -def get_tunnel_mapping_for(conn_id, session): - try: - entry = session.query(IdentifierMap).filter_by( - ipsec_site_conn_id=conn_id).one() - LOG.debug(_("Mappings for IPSec connection %(conn)s - " - "tunnel=%(tunnel)s ike_policy=%(csr_ike)d " - "ipsec_policy=%(csr_ipsec)d"), - {'conn': conn_id, 'tunnel': entry.csr_tunnel_id, - 'csr_ike': entry.csr_ike_policy_id, - 'csr_ipsec': entry.csr_ipsec_policy_id}) - return (entry.csr_tunnel_id, entry.csr_ike_policy_id, - entry.csr_ipsec_policy_id) - except sql_exc.NoResultFound: - msg = _("Existing entry for IPSec connection %s not found in Cisco " - "CSR mapping table") % conn_id - raise CsrInternalError(reason=msg) - - -def create_tunnel_mapping(context, conn_info): - """Create Cisco CSR IDs, using mapping table and OpenStack UUIDs.""" - conn_id = conn_info['id'] - ike_policy_id = conn_info['ikepolicy_id'] - ipsec_policy_id = conn_info['ipsecpolicy_id'] - tenant_id = conn_info['tenant_id'] - with context.session.begin(): - csr_tunnel_id = get_next_available_tunnel_id(context.session) - csr_ike_id = determine_csr_ike_policy_id(ike_policy_id, conn_id, - context.session) - csr_ipsec_id = determine_csr_ipsec_policy_id(ipsec_policy_id, conn_id, - context.session) - map_entry = IdentifierMap(tenant_id=tenant_id, - ipsec_site_conn_id=conn_id, - csr_tunnel_id=csr_tunnel_id, - csr_ike_policy_id=csr_ike_id, - csr_ipsec_policy_id=csr_ipsec_id) - try: - context.session.add(map_entry) - # Force committing to database - context.session.flush() - except db_exc.DBDuplicateEntry: - msg = _("Attempt to create duplicate entry in Cisco CSR " - "mapping table for connection %s") % conn_id - raise CsrInternalError(reason=msg) - LOG.info(_("Mapped connection %(conn_id)s to Tunnel%(tunnel_id)d " - "using IKE policy ID %(ike_id)d and IPSec policy " - "ID %(ipsec_id)d"), - {'conn_id': conn_id, 'tunnel_id': csr_tunnel_id, - 'ike_id': csr_ike_id, 'ipsec_id': csr_ipsec_id}) - - -def delete_tunnel_mapping(context, conn_info): - conn_id = conn_info['id'] - with context.session.begin(): - sess_qry = context.session.query(IdentifierMap) - sess_qry.filter_by(ipsec_site_conn_id=conn_id).delete() - LOG.info(_("Removed mapping for connection %s"), conn_id) diff --git a/neutron/services/vpn/service_drivers/cisco_ipsec.py b/neutron/services/vpn/service_drivers/cisco_ipsec.py deleted file mode 100644 index ed34f41ff..000000000 --- a/neutron/services/vpn/service_drivers/cisco_ipsec.py +++ /dev/null @@ -1,245 +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. - -import netaddr -from netaddr import core as net_exc - -from neutron.common import exceptions -from neutron.common import rpc_compat -from neutron.openstack.common import excutils -from neutron.openstack.common import log as logging -from neutron.plugins.common import constants -from neutron.services.vpn.common import topics -from neutron.services.vpn import service_drivers -from neutron.services.vpn.service_drivers import cisco_csr_db as csr_id_map - - -LOG = logging.getLogger(__name__) - -IPSEC = 'ipsec' -BASE_IPSEC_VERSION = '1.0' -LIFETIME_LIMITS = {'IKE Policy': {'min': 60, 'max': 86400}, - 'IPSec Policy': {'min': 120, 'max': 2592000}} -MIN_CSR_MTU = 1500 -MAX_CSR_MTU = 9192 - - -class CsrValidationFailure(exceptions.BadRequest): - message = _("Cisco CSR does not support %(resource)s attribute %(key)s " - "with value '%(value)s'") - - -class CiscoCsrIPsecVpnDriverCallBack(rpc_compat.RpcCallback): - - """Handler for agent to plugin RPC messaging.""" - - # history - # 1.0 Initial version - - RPC_API_VERSION = BASE_IPSEC_VERSION - - def __init__(self, driver): - super(CiscoCsrIPsecVpnDriverCallBack, self).__init__() - self.driver = driver - - def get_vpn_services_on_host(self, context, host=None): - """Retuns info on 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, context) - for vpnservice in vpnservices] - - def update_status(self, context, status): - """Update status of all vpnservices.""" - plugin = self.driver.service_plugin - plugin.update_status_by_agent(context, status) - - -class CiscoCsrIPsecVpnAgentApi(service_drivers.BaseIPsecVpnAgentApi, - rpc_compat.RpcCallback): - - """API and handler for Cisco IPSec plugin to agent RPC messaging.""" - - RPC_API_VERSION = BASE_IPSEC_VERSION - - def __init__(self, topic, default_version): - super(CiscoCsrIPsecVpnAgentApi, self).__init__( - topics.CISCO_IPSEC_AGENT_TOPIC, topic, default_version) - - -class CiscoCsrIPsecVPNDriver(service_drivers.VpnDriver): - - """Cisco CSR VPN Service Driver class for IPsec.""" - - def __init__(self, service_plugin): - super(CiscoCsrIPsecVPNDriver, self).__init__(service_plugin) - self.endpoints = [CiscoCsrIPsecVpnDriverCallBack(self)] - self.conn = rpc_compat.create_connection(new=True) - self.conn.create_consumer( - topics.CISCO_IPSEC_DRIVER_TOPIC, self.endpoints, fanout=False) - self.conn.consume_in_threads() - self.agent_rpc = CiscoCsrIPsecVpnAgentApi( - topics.CISCO_IPSEC_AGENT_TOPIC, BASE_IPSEC_VERSION) - - @property - def service_type(self): - return IPSEC - - def validate_lifetime(self, for_policy, policy_info): - """Ensure lifetime in secs and value is supported, based on policy.""" - units = policy_info['lifetime']['units'] - if units != 'seconds': - raise CsrValidationFailure(resource=for_policy, - key='lifetime:units', - value=units) - value = policy_info['lifetime']['value'] - if (value < LIFETIME_LIMITS[for_policy]['min'] or - value > LIFETIME_LIMITS[for_policy]['max']): - raise CsrValidationFailure(resource=for_policy, - key='lifetime:value', - value=value) - - def validate_ike_version(self, policy_info): - """Ensure IKE policy is v1 for current REST API.""" - version = policy_info['ike_version'] - if version != 'v1': - raise CsrValidationFailure(resource='IKE Policy', - key='ike_version', - value=version) - - def validate_mtu(self, conn_info): - """Ensure the MTU value is supported.""" - mtu = conn_info['mtu'] - if mtu < MIN_CSR_MTU or mtu > MAX_CSR_MTU: - raise CsrValidationFailure(resource='IPSec Connection', - key='mtu', - value=mtu) - - def validate_public_ip_present(self, vpn_service): - """Ensure there is one gateway IP specified for the router used.""" - gw_port = vpn_service.router.gw_port - if not gw_port or len(gw_port.fixed_ips) != 1: - raise CsrValidationFailure(resource='IPSec Connection', - key='router:gw_port:ip_address', - value='missing') - - def validate_peer_id(self, ipsec_conn): - """Ensure that an IP address is specified for peer ID.""" - # TODO(pcm) Should we check peer_address too? - peer_id = ipsec_conn['peer_id'] - try: - netaddr.IPAddress(peer_id) - except net_exc.AddrFormatError: - raise CsrValidationFailure(resource='IPSec Connection', - key='peer_id', value=peer_id) - - def validate_ipsec_connection(self, context, ipsec_conn, vpn_service): - """Validate attributes w.r.t. Cisco CSR capabilities.""" - ike_policy = self.service_plugin.get_ikepolicy( - context, ipsec_conn['ikepolicy_id']) - ipsec_policy = self.service_plugin.get_ipsecpolicy( - context, ipsec_conn['ipsecpolicy_id']) - self.validate_lifetime('IKE Policy', ike_policy) - self.validate_lifetime('IPSec Policy', ipsec_policy) - self.validate_ike_version(ike_policy) - self.validate_mtu(ipsec_conn) - self.validate_public_ip_present(vpn_service) - self.validate_peer_id(ipsec_conn) - LOG.debug(_("IPSec connection %s validated for Cisco CSR"), - ipsec_conn['id']) - - def create_ipsec_site_connection(self, context, ipsec_site_connection): - vpnservice = self.service_plugin._get_vpnservice( - context, ipsec_site_connection['vpnservice_id']) - try: - self.validate_ipsec_connection(context, ipsec_site_connection, - vpnservice) - except CsrValidationFailure: - with excutils.save_and_reraise_exception(): - self.service_plugin.update_ipsec_site_conn_status( - context, ipsec_site_connection['id'], constants.ERROR) - csr_id_map.create_tunnel_mapping(context, ipsec_site_connection) - self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'], - reason='ipsec-conn-create') - - 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'], - reason='ipsec-conn-update') - - 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'], - reason='ipsec-conn-delete') - - 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'], - reason='vpn-service-update') - - def delete_vpnservice(self, context, vpnservice): - self.agent_rpc.vpnservice_updated(context, vpnservice['router_id'], - reason='vpn-service-delete') - - def get_cisco_connection_mappings(self, conn_id, context): - """Obtain persisted mappings for IDs related to connection.""" - tunnel_id, ike_id, ipsec_id = csr_id_map.get_tunnel_mapping_for( - conn_id, context.session) - return {'site_conn_id': u'Tunnel%d' % tunnel_id, - 'ike_policy_id': u'%d' % ike_id, - 'ipsec_policy_id': u'%s' % ipsec_id} - - def _make_vpnservice_dict(self, vpnservice, context): - """Collect all info on service, including Cisco info per IPSec conn.""" - vpnservice_dict = dict(vpnservice) - vpnservice_dict['ipsec_conns'] = [] - vpnservice_dict['subnet'] = dict( - vpnservice.subnet) - vpnservice_dict['external_ip'] = vpnservice.router.gw_port[ - 'fixed_ips'][0]['ip_address'] - for ipsec_conn in vpnservice.ipsec_site_connections: - ipsec_conn_dict = dict(ipsec_conn) - ipsec_conn_dict['ike_policy'] = dict(ipsec_conn.ikepolicy) - ipsec_conn_dict['ipsec_policy'] = dict(ipsec_conn.ipsecpolicy) - ipsec_conn_dict['peer_cidrs'] = [ - peer_cidr.cidr for peer_cidr in ipsec_conn.peer_cidrs] - ipsec_conn_dict['cisco'] = self.get_cisco_connection_mappings( - ipsec_conn['id'], context) - vpnservice_dict['ipsec_conns'].append(ipsec_conn_dict) - return vpnservice_dict diff --git a/neutron/services/vpn/service_drivers/ipsec.py b/neutron/services/vpn/service_drivers/ipsec.py deleted file mode 100644 index be6aa9e26..000000000 --- a/neutron/services/vpn/service_drivers/ipsec.py +++ /dev/null @@ -1,156 +0,0 @@ -# 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_compat -from neutron.openstack.common import log as logging -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(rpc_compat.RpcCallback): - """Callback for IPSecVpnDriver rpc.""" - - # history - # 1.0 Initial version - - RPC_API_VERSION = BASE_IPSEC_VERSION - - def __init__(self, driver): - super(IPsecVpnDriverCallBack, self).__init__() - self.driver = driver - - def get_vpn_services_on_host(self, context, host=None): - """Returns 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(service_drivers.BaseIPsecVpnAgentApi, - rpc_compat.RpcCallback): - """Agent RPC API for IPsecVPNAgent.""" - - RPC_API_VERSION = BASE_IPSEC_VERSION - - def __init__(self, topic, default_version): - super(IPsecVpnAgentApi, self).__init__( - topics.IPSEC_AGENT_TOPIC, topic, default_version) - - -class IPsecVPNDriver(service_drivers.VpnDriver): - """VPN Service Driver class for IPsec.""" - - def __init__(self, service_plugin): - super(IPsecVPNDriver, self).__init__(service_plugin) - self.endpoints = [IPsecVpnDriverCallBack(self)] - self.conn = rpc_compat.create_connection(new=True) - self.conn.create_consumer( - topics.IPSEC_DRIVER_TOPIC, self.endpoints, fanout=False) - self.conn.consume_in_threads() - 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