group-based-policy/gbpservice/contrib/nfp/configurator/agents/vpn.py

462 lines
16 KiB
Python

# 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 os
import six
from gbpservice.contrib.nfp.configurator.agents import agent_base
from gbpservice.contrib.nfp.configurator.drivers.base import base_driver
from gbpservice.contrib.nfp.configurator.lib import data_filter
from gbpservice.contrib.nfp.configurator.lib import utils
from gbpservice.contrib.nfp.configurator.lib import vpn_constants as const
from gbpservice.nfp.core import event as nfp_event
from gbpservice.nfp.core import log as nfp_logging
from gbpservice.nfp.core import module as nfp_api
import oslo_messaging as messaging
LOG = nfp_logging.getLogger(__name__)
class VpnaasRpcSender(data_filter.Filter):
"""
Implements VPNaas response path to Neutron plugin.
Methods of this class are invoked by the VPNaasEventHandler class
for sending response from driver to the VPNaas Neutron plugin.
"""
RPC_API_VERSION = '1.0'
target = messaging.Target(version=RPC_API_VERSION)
def __init__(self, sc):
self._sc = sc
self._notify = agent_base.AgentBaseNotification(sc)
super(VpnaasRpcSender, self).__init__(None, None)
def get_vpn_services(self, context, ids=None, filters=None):
"""Gets list of vpnservices for tenant.
:param context: dictionary which holds details of vpn service type like
For IPSEC connections :
List of vpnservices
lIst of ipsec connections
ike policy & ipsec policy.
:param ids: based on which the filter library extracts the data.
:param filter: based on which the filter library extracts the data.
Returns: Dictionary of vpn service type which matches with the filters.
"""
LOG.info("Sending RPC for GET VPN SERVICES with %(filters)s ",
{'filters': filters})
return self.call(
context,
self.make_msg('get_vpn_services', ids=ids, filters=filters))
def get_vpn_servicecontext(self, context, filters=None):
"""Get list of vpnservice context on this host.
:param context: dictionary which holds details of vpn service type like
For IPSEC connections :
List of vpnservices
lIst of ipsec connections
ike policy & ipsec policy.
:param filter: based on which the filter library extracts the data
from context dictionary.
Returns: dictionary of vpnservice
"""
LOG.info("Sending RPC for GET VPN SERVICECONTEXT with "
"Filters:%(filters)s ",
{'filters': filters})
return self.call(
context,
self.make_msg(
'get_vpn_servicecontext', filters=filters))
def get_ipsec_conns(self, context, filters):
"""
Get list of ipsec conns with filters
specified.
"""
LOG.info("Sending RPC for GET IPSEC CONNS with Filters:"
"%(filters)s ",
{'filters': filters})
return self.call(
context,
self.make_msg(
'get_ipsec_conns',
filters=filters))
def update_status(self, context, status):
"""Update local status.
This method call updates status attribute of
VPNServices.
"""
msg = {'info': {'service_type': const.SERVICE_TYPE,
'context': context['agent_info']['context']},
'notification': [{
'resource': context['agent_info']['resource'],
'data': {'status': status,
'notification_type': (
'update_status')}}]
}
LOG.info("Sending Notification 'Update Status' with "
"status:%(status)s ",
{'status': status})
self._notify._notification(msg)
def ipsec_site_conn_deleted(self, context, resource_id):
""" Notify VPNaaS plugin about delete of ipsec-site-conn """
msg = {'info': {'service_type': const.SERVICE_TYPE,
'context': context['agent_info']['context']},
'notification': [{
'resource': context['agent_info']['resource'],
'data': {'resource_id': resource_id,
'notification_type': (
'ipsec_site_conn_deleted')}}]
}
LOG.info("Sending Notification 'Ipsec Site Conn Deleted' "
"for resource:%(resource_id)s ",
{'resource_id': resource_id})
self._notify._notification(msg)
class VPNaasRpcManager(agent_base.AgentBaseRPCManager):
"""
Implements VPNaasRpcManager class which receives requests
from Configurator to Agent.
Methods of this class are invoked by the configurator. Events are
created according to the requests received and enqueued to worker queues.
"""
RPC_API_VERSION = '1.0'
target = messaging.Target(version=RPC_API_VERSION)
def __init__(self, conf, sc):
"""Instantiates child and parent class objects.
Passes the instances of core service controller and oslo configuration
to parent instance in order to provide event enqueue facility for batch
processing event.
:param sc: Service Controller object that is used for interfacing
with core service controller.
:param conf: Configuration object that is used for configuration
parameter access.
"""
super(VPNaasRpcManager, self).__init__(sc, conf)
def vpnservice_updated(self, context, **resource_data):
"""Registers the VPNaas plugin events to update the vpn configurations.
:param context: dictionary, confined to the specific service type.
:param resource_data: dictionary, confined to the specific
operation type.
Returns: None
"""
LOG.info("Received request 'VPN Service Updated'."
"for API '%(api)s'",
{'api': resource_data.get('reason', '')})
arg_dict = {'context': context,
'resource_data': resource_data}
# Serializing the event because simultaneous configure
# requests overrides the same crypto-map in the service VM
# which results in corrupting the crypto-map
resource_type = resource_data.get('rsrc_type')
if resource_type and resource_type.lower() == 'ipsec_site_connection':
ev = self.sc.new_event(id='VPNSERVICE_UPDATED',
key=resource_data['resource']['id'],
data=arg_dict,
serialize=True,
binding_key=resource_data[
'resource']['vpnservice_id'])
msg = "serializing event: %s" % ('VPNSERVICE_UPDATED')
LOG.debug(msg)
else:
ev = self.sc.new_event(id='VPNSERVICE_UPDATED', data=arg_dict)
self.sc.post_event(ev)
class VPNaasEventHandler(nfp_api.NfpEventHandler):
"""
Handler class to invoke the vpn driver methods.
For every event that gets invoked from worker process lands over here
to make a call to the driver methods.
"""
def __init__(self, sc, drivers):
""" Instantiates class object.
:param sc: Service Controller object that is used to communicate
with process model core file.
:param drivers: dictionary of driver name to object mapping
"""
self._sc = sc
self._drivers = drivers
self._plugin_rpc = VpnaasRpcSender(self._sc)
def _get_driver(self, service_vendor, service_feature):
driver_id = const.SERVICE_TYPE + service_vendor + service_feature
return self._drivers[driver_id]
def handle_event(self, ev):
"""
Demultiplexes the vpn request to appropriate driver methods.
:param ev: event object sent from the process model.
Returns: None
"""
if ev.id == 'VPN_SYNC':
self._sc.poll_event(ev)
if ev.id == 'VPNSERVICE_UPDATED':
try:
msg = ("Worker process with ID: %s starting "
"to handle task: %s of topic: %s. "
% (os.getpid(),
ev.id, const.VPN_GENERIC_CONFIG_RPC_TOPIC))
LOG.debug(msg)
agent_info = ev.data['context']['agent_info']
service_vendor = agent_info['service_vendor']
service_feature = agent_info['service_feature']
driver = self._get_driver(service_vendor, service_feature)
LOG.info("Invoking driver with service vendor:"
"%(service_vendor)s ",
{'service_vendor': service_vendor})
setattr(VPNaasEventHandler, "service_driver", driver)
self._vpnservice_updated(ev, driver)
except Exception as err:
msg = ("Failed to perform the operation: %s. %s"
% (ev.id, str(err).capitalize()))
LOG.error(msg)
finally:
self._sc.event_complete(ev)
def _vpnservice_updated(self, ev, driver):
"""
Makes call to the respective operation method of vpn driver.
:param ev: event object sent from the process model.
:param driver: vpn driver class object.
Returns: None.
"""
context = ev.data.get('context')
resource_data = ev.data.get('resource_data')
msg = "Vpn service updated from server side"
LOG.info(msg)
try:
driver.vpnservice_updated(context, resource_data)
if 'ipsec_site_conns' in context['service_info']:
for item in context['service_info']['ipsec_site_conns']:
if item['id'] == resource_data['resource']['id'] and (
resource_data['reason'] == 'create'):
item['status'] = 'INIT'
arg_dict = {'context': context,
'resource_data': resource_data}
ev1 = self._sc.new_event(id='VPN_SYNC',
key='VPN_SYNC', data=arg_dict)
self._sc.post_event(ev1)
break
except Exception as err:
msg = ("Failed to update VPN service. %s"
% str(err).capitalize())
LOG.error(msg)
reason = resource_data.get('reason')
rsrc = resource_data.get('rsrc_type')
if (reason == 'delete' and rsrc == 'ipsec_site_connection'):
conn = resource_data['resource']
resource_id = conn['id']
self._plugin_rpc.ipsec_site_conn_deleted(context,
resource_id=resource_id)
def _get_service_vendor(self, vpn_svc):
"""
Extracts the vendor from the description.
:param vpn_svc: vpn service operation type dictionary,
which it gets from filter library
Returns: None
"""
svc_desc = vpn_svc['description']
tokens = svc_desc.split(';')
vendor = tokens[5].split('=')[1]
return vendor
def _sync_ipsec_conns(self, context, svc_context):
"""
Gets the status of the vpn service.
:param context: Dictionary of the vpn service type.
:param vendor: vendor name
:param svc_context: vpn service operation type dictionary,
which it gets filter library
Returns: None
"""
try:
return self.service_driver.check_status(context, svc_context)
except Exception as err:
msg = ("Failed to sync ipsec connection information. %s."
% str(err).capitalize())
LOG.error(msg)
@nfp_api.poll_event_desc(event='VPN_SYNC', spacing=10)
def sync(self, ev):
"""Periodically updates the status of vpn service, whether the
tunnel is UP or DOWN.
:param context: Dictionary of the vpn service type.
Returns: None
"""
context = ev.data.get('context')
s2s_contexts = self._plugin_rpc.get_vpn_servicecontext(context)
state = self._sync_ipsec_conns(context, s2s_contexts[0])
if state in {const.STATE_ACTIVE,
const.STATE_ERROR}:
return {'poll': False}
def events_init(sc, drivers):
"""Registers events with core service controller.
All the events will come to handle_event method of class instance
registered in 'handler' field.
:param sc: Object of Service Controller from the process model to regiters
the different events
:param drivers: Driver instance registered with the service agent
Returns: None
"""
evs = [
nfp_event.Event(id='VPNSERVICE_UPDATED',
handler=VPNaasEventHandler(sc, drivers)),
nfp_event.Event(id='VPN_SYNC',
handler=VPNaasEventHandler(sc, drivers))]
sc.register_events(evs)
def load_drivers(sc, conf):
"""Loads the drivers dynamically.
Loads the drivers that register with the agents.
:param sc: Object of the Service Controller class from core
service controller.
Returns: dictionary of instances of the respective driver classes.
"""
ld = utils.ConfiguratorUtils(conf)
drivers = ld.load_drivers(const.SERVICE_TYPE)
for service_type, driver_name in six.iteritems(drivers):
driver_obj = driver_name(conf=conf)
drivers[service_type] = driver_obj
return drivers
def register_service_agent(cm, sc, conf):
"""Registers the agents with Cofigurator module.
Puts all the agents into the dictionary with their service types.
:prarm cm: Configurator module's object to communicate back and forth
:param sc: Object of the Service Controller class from core
service controller.
:param conf: Object of oslo configurator passed from the core service
controller
Returns: None
"""
rpcmgr = VPNaasRpcManager(conf, sc)
cm.register_service_agent(const.SERVICE_TYPE, rpcmgr)
def init_agent(cm, sc, conf):
"""Loads the drivers and registers the agents.
Loads the dynamicaaly both the drivers and agents, registers the agents
with their service types.
:prarm cm: Configurator module's object to communicate back and forth
:param sc: Object of the Service Controller class from core
service controller.
:param conf: Object of oslo configurator passed from the core service
controller
Returns: None
"""
try:
drivers = load_drivers(sc, conf)
except Exception as err:
msg = ("VPNaas failed to load drivers. %s"
% (str(err).capitalize()))
LOG.error(msg)
raise err
else:
msg = "VPNaas loaded drivers successfully."
LOG.debug(msg)
try:
events_init(sc, drivers)
except Exception as err:
msg = ("VPNaas Events initialization unsuccessful. %s"
% (str(err).capitalize()))
LOG.error(msg)
raise err
else:
msg = "VPNaas Events initialization successful."
LOG.debug(msg)
try:
register_service_agent(cm, sc, conf)
bdobj = base_driver.BaseDriver(conf)
bdobj.register_agent_object_with_driver('agent', VpnaasRpcSender(sc))
except Exception as err:
msg = ("VPNaas service agent registration unsuccessful. %s"
% (str(err).capitalize()))
LOG.error(msg)
raise err
else:
msg = "VPNaas service agent registration successful."
LOG.debug(msg)
msg = "VPN as a Service Module Initialized."
LOG.info(msg)
def init_agent_complete(cm, sc, conf):
"""
Initializes periodic tasks.
"""
msg = " vpn agent init complete"
LOG.info(msg)