354 lines
14 KiB
Python
354 lines
14 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 exceptions
|
|
|
|
from gbpservice._i18n import _
|
|
from gbpservice.nfp.common import constants as nfp_constants
|
|
from gbpservice.nfp.core import log as nfp_logging
|
|
from gbpservice.nfp.lib import rest_client_over_unix as unix_rc
|
|
|
|
from neutron.common import rpc as n_rpc
|
|
from neutron_lib import context as n_context
|
|
|
|
from oslo_config import cfg
|
|
from oslo_config import cfg as oslo_config
|
|
import oslo_messaging as messaging
|
|
from oslo_serialization import jsonutils
|
|
|
|
import requests
|
|
import six
|
|
|
|
LOG = nfp_logging.getLogger(__name__)
|
|
Version = 'v1' # v1/v2/v3#
|
|
|
|
rest_opts = [
|
|
cfg.StrOpt('rest_server_address',
|
|
default='', help='Rest connection IpAddr'),
|
|
cfg.IntOpt('rest_server_port',
|
|
default=8080, help='Rest connection Port'),
|
|
]
|
|
|
|
rpc_opts = [
|
|
cfg.StrOpt('topic',
|
|
default='', help='Topic for rpc connection'),
|
|
]
|
|
|
|
oslo_config.CONF.register_opts(rest_opts, "REST")
|
|
oslo_config.CONF.register_opts(rpc_opts, "RPC")
|
|
n_rpc.init(cfg.CONF)
|
|
|
|
UNIX_REST = 'unix_rest'
|
|
TCP_REST = 'tcp_rest'
|
|
|
|
""" Common Class for restClient exceptions """
|
|
|
|
|
|
class RestClientException(exceptions.Exception):
|
|
|
|
""" RestClient Exception """
|
|
|
|
""" Common Class to handle restclient request"""
|
|
|
|
|
|
class RestApi(object):
|
|
|
|
def __init__(self, rest_server_address, rest_server_port):
|
|
self.rest_server_address = rest_server_address
|
|
self.rest_server_port = rest_server_port
|
|
self.url = "http://%s:%s/v1/nfp/%s"
|
|
|
|
def _response(self, resp, url):
|
|
success_code = [200, 201, 202, 204]
|
|
# Evaluate responses into success and failures.
|
|
# Raise exception for failure cases which needs
|
|
# to be handled in caller function.
|
|
if success_code.__contains__(resp.status_code):
|
|
return resp
|
|
elif resp.status_code == 400:
|
|
raise RestClientException(_("HTTPBadRequest: %s") % resp.reason)
|
|
elif resp.status_code == 401:
|
|
raise RestClientException(_("HTTPUnauthorized: %s") % resp.reason)
|
|
elif resp.status_code == 403:
|
|
raise RestClientException(_("HTTPForbidden: %s") % resp.reason)
|
|
elif resp.status_code == 404:
|
|
raise RestClientException(_("HttpNotFound: %s") % resp.reason)
|
|
elif resp.status_code == 405:
|
|
raise RestClientException(
|
|
_("HTTPMethodNotAllowed: %s") % resp.reason)
|
|
elif resp.status_code == 406:
|
|
raise RestClientException(_("HTTPNotAcceptable: %s") % resp.reason)
|
|
elif resp.status_code == 408:
|
|
raise RestClientException(
|
|
_("HTTPRequestTimeout: %s") % resp.reason)
|
|
elif resp.status_code == 409:
|
|
raise RestClientException(_("HTTPConflict: %s") % resp.reason)
|
|
elif resp.status_code == 415:
|
|
raise RestClientException(
|
|
_("HTTPUnsupportedMediaType: %s") % resp.reason)
|
|
elif resp.status_code == 417:
|
|
raise RestClientException(
|
|
_("HTTPExpectationFailed: %s") % resp.reason)
|
|
elif resp.status_code == 500:
|
|
raise RestClientException(_("HTTPServerError: %s") % resp.reason)
|
|
else:
|
|
raise RestClientException(_('Unhandled Exception code: '
|
|
'%(status)s %(reason)s') %
|
|
{'status': resp.status_code,
|
|
'reason': resp.reason})
|
|
return resp
|
|
|
|
def post(self, path, body, method_type):
|
|
"""Post restclient request handler
|
|
Return:Http response
|
|
"""
|
|
url = self.url % (
|
|
self.rest_server_address,
|
|
self.rest_server_port, path)
|
|
data = jsonutils.dumps(body)
|
|
try:
|
|
# Method-Type needs to be added here,as DELETE/CREATE
|
|
# both case are handled by post as delete also needs
|
|
# to send data to the rest-server.
|
|
headers = {"content-type": "application/json",
|
|
"method-type": method_type}
|
|
resp = requests.post(url, data,
|
|
headers=headers)
|
|
message = "POST url %s %d" % (url, resp.status_code)
|
|
LOG.info(message)
|
|
return self._response(resp, url)
|
|
except RestClientException as rce:
|
|
message = "Rest API %s - Failed. Reason: %s" % (
|
|
url, rce)
|
|
LOG.error(message)
|
|
|
|
def put(self, path, body):
|
|
"""Put restclient request handler
|
|
Return:Http response
|
|
"""
|
|
url = self.url % (
|
|
self.rest_server_address,
|
|
self.rest_server_port, path)
|
|
data = jsonutils.dumps(body)
|
|
try:
|
|
headers = {"content-type": "application/json"}
|
|
resp = requests.put(url, data,
|
|
headers=headers)
|
|
message = "PUT url %s %d" % (url, resp.status_code)
|
|
LOG.info(message)
|
|
return self._response(resp, url)
|
|
except RestClientException as rce:
|
|
message = "Rest API %s - Failed. Reason: %s" % (
|
|
url, rce)
|
|
LOG.error(message)
|
|
|
|
def get(self, path):
|
|
"""Get restclient request handler
|
|
Return:Http response
|
|
"""
|
|
url = self.url % (
|
|
self.rest_server_address,
|
|
self.rest_server_port, path)
|
|
try:
|
|
headers = {"content-type": "application/json"}
|
|
resp = requests.get(url,
|
|
headers=headers)
|
|
message = "GET url %s %d" % (url, resp.status_code)
|
|
LOG.info(message)
|
|
return self._response(resp, url)
|
|
except RestClientException as rce:
|
|
message = "Rest API %s - Failed. Reason: %s" % (
|
|
url, rce)
|
|
LOG.error(message)
|
|
|
|
""" Common Class to handle rpcclient request"""
|
|
|
|
|
|
class RPCClient(object):
|
|
API_VERSION = '1.0'
|
|
|
|
def __init__(self, topic):
|
|
self.topic = topic
|
|
target = messaging.Target(topic=self.topic,
|
|
version=self.API_VERSION)
|
|
self.client = n_rpc.get_client(target)
|
|
self.cctxt = self.client.prepare(version=self.API_VERSION,
|
|
topic=self.topic)
|
|
|
|
|
|
def send_request_to_configurator(conf, context, body,
|
|
method_type, device_config=False,
|
|
network_function_event=False):
|
|
"""Common function to handle (create, delete) request for configurator.
|
|
Send create/delete to configurator rest-server.
|
|
Return:Http Response
|
|
"""
|
|
# This function reads configuration data and decides
|
|
# method (tcp_rest/rpc) for sending request to configurator.
|
|
if device_config:
|
|
method_name = method_type.lower() + '_network_function_device_config'
|
|
body['info']['context'].update({'neutron_context': context.to_dict()})
|
|
elif network_function_event:
|
|
method_name = 'network_function_event'
|
|
else:
|
|
if (body['config'][0]['resource'] in
|
|
nfp_constants.CONFIG_TAG_RESOURCE_MAP.values()):
|
|
body['config'][0]['resource_data'].update(
|
|
{'neutron_context': context.to_dict()})
|
|
body['info']['context'].update(
|
|
{'neutron_context': context.to_dict()})
|
|
method_name = method_type.lower() + '_network_function_config'
|
|
|
|
if conf.backend == TCP_REST:
|
|
try:
|
|
rc = RestApi(conf.REST.rest_server_address,
|
|
conf.REST.rest_server_port)
|
|
if method_type.lower() in [nfp_constants.CREATE,
|
|
nfp_constants.DELETE]:
|
|
resp = rc.post(method_name, body, method_type.upper())
|
|
message = "%s -> POST response: (%s) body: %s " % (method_name,
|
|
resp, body)
|
|
LOG.debug(message)
|
|
elif method_type.lower() in [nfp_constants.UPDATE]:
|
|
resp = rc.put(method_name, body)
|
|
message = "%s -> PUT response: (%s) body: %s " % (method_name,
|
|
resp, body)
|
|
LOG.debug(message)
|
|
else:
|
|
message = ("%s api not supported" % (method_name))
|
|
LOG.error(message)
|
|
except RestClientException as rce:
|
|
message = "%s -> POST request failed.Reason: %s" % (
|
|
method_name, rce)
|
|
LOG.error(message)
|
|
|
|
elif conf.backend == UNIX_REST:
|
|
try:
|
|
if method_type.lower() in [nfp_constants.CREATE,
|
|
nfp_constants.DELETE]:
|
|
resp, content = unix_rc.post(method_name,
|
|
body=body)
|
|
message = ("%s -> POST response: (%s) body : %s " %
|
|
(method_name, content, body))
|
|
LOG.debug(message)
|
|
elif method_type.lower() in [nfp_constants.UPDATE]:
|
|
resp, content = unix_rc.put(method_name,
|
|
body=body)
|
|
message = ("%s -> PUT response: (%s) body : %s " %
|
|
(method_name, content, body))
|
|
LOG.debug(message)
|
|
else:
|
|
message = ("%s api not supported" % (method_name))
|
|
LOG.error(message)
|
|
except unix_rc.RestClientException as rce:
|
|
message = "%s -> request failed . Reason %s " % (
|
|
method_name, rce)
|
|
LOG.error(message)
|
|
|
|
else:
|
|
message = ("%s -> RPC request sent. " % (method_name))
|
|
LOG.info(message)
|
|
rpcClient = RPCClient(conf.RPC.topic)
|
|
rpcClient.cctxt.cast(context, method_name,
|
|
body=body)
|
|
|
|
|
|
def get_response_from_configurator(conf):
|
|
"""Common function to handle get request for configurator.
|
|
Get notification http response from configurator rest server.
|
|
Return:Http Response
|
|
response_data = [
|
|
{'receiver': <neutron/device_orchestrator/service_orchestrator>,
|
|
'resource': <firewall/vpn/loadbalancer/orchestrator>,
|
|
'method': <notification method name>,
|
|
'kwargs': <notification method arguments>
|
|
},
|
|
]
|
|
"""
|
|
# This function reads configuration data and decides
|
|
# method (tcp_rest/ unix_rest/ rpc) for get response from configurator.
|
|
if conf.backend == TCP_REST:
|
|
try:
|
|
rc = RestApi(conf.REST.rest_server_address,
|
|
conf.REST.rest_server_port)
|
|
resp = rc.get('get_notifications')
|
|
rpc_cbs_data = jsonutils.loads(resp.content)
|
|
return rpc_cbs_data
|
|
except RestClientException as rce:
|
|
message = ("get_notification ->"
|
|
"GET request failed. Reason : %s" % (rce))
|
|
LOG.error(message)
|
|
return "get_notification -> GET request failed. Reason : %s" % (
|
|
rce)
|
|
except Exception as e:
|
|
message = ("get_notification ->"
|
|
"GET request failed. Reason : %s" % (e))
|
|
LOG.error(message)
|
|
return "get_notification -> GET request failed. Reason : %s" % (
|
|
e)
|
|
|
|
elif conf.backend == UNIX_REST:
|
|
try:
|
|
resp, content = unix_rc.get('get_notifications')
|
|
content = jsonutils.loads(content)
|
|
if content:
|
|
message = ("get_notification ->"
|
|
"GET response: (%s)" % (content))
|
|
LOG.debug(message)
|
|
return content
|
|
except unix_rc.RestClientException as rce:
|
|
message = ("get_notification ->"
|
|
"GET request failed. Reason : %s" % (
|
|
rce))
|
|
LOG.error(message)
|
|
return "get_notification -> GET request failed. Reason : %s" % (
|
|
rce)
|
|
except Exception as e:
|
|
message = ("get_notification ->"
|
|
"GET request failed. Reason : %s" % (
|
|
e))
|
|
LOG.error(message)
|
|
return "get_notification -> GET request failed. Reason : %s" % (
|
|
e)
|
|
|
|
else:
|
|
rpc_cbs_data = []
|
|
try:
|
|
rpcClient = RPCClient(conf.RPC.topic)
|
|
context = n_context.Context(
|
|
'config_agent_user', 'config_agent_tenant')
|
|
rpc_cbs_data = rpcClient.cctxt.call(context,
|
|
'get_notifications')
|
|
return rpc_cbs_data
|
|
except Exception as e:
|
|
message = "Exception while processing %s" % e
|
|
LOG.error(message)
|
|
return "get_notification -> GET request failed. Reason : %s" % (
|
|
e)
|
|
|
|
|
|
def parse_service_flavor_string(service_flavor_str):
|
|
"""Parse service_flavour string to service details dictionary.
|
|
Return: Service Details Dictionary
|
|
"""
|
|
service_details = {}
|
|
if ',' not in service_flavor_str:
|
|
service_details['device_type'] = 'nova'
|
|
service_details['service_vendor'] = service_flavor_str
|
|
else:
|
|
service_flavor_dict = dict(item.split('=') for item
|
|
in service_flavor_str.split(','))
|
|
service_details = {key.strip(): value.strip() for key, value
|
|
in six.iteritems(service_flavor_dict)}
|
|
return service_details
|