nfv/nova-api-proxy/nova-api-proxy/nova_api_proxy/apps/acceptor.py

215 lines
7.7 KiB
Python

#
# Copyright (c) 2015-2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
import json
from paste.request import construct_url
import routes
import webob.dec
import webob.exc
from oslo_config import cfg
from oslo_log import log as logging
from nova_api_proxy.apps.dispatcher import APIDispatcher
from nova_api_proxy.apps.dispatcher import Router
from nova_api_proxy.apps.proxy import Proxy
from nova_api_proxy.common.service import Middleware
from nova_api_proxy.common.service import Request
from nova_api_proxy.common import utils
LOG = logging.getLogger(__name__)
proxy_opts = [
cfg.StrOpt('nfvi_compute_listen',
default="127.0.0.1",
help='Nfvi computer REST API server for nova api proxy to '
'forward the request to'),
cfg.IntOpt('nfvi_compute_listen_port',
default=30003,
help='Nfvi computer REST API server listen port'),
cfg.BoolOpt('show_request_body',
default=False,
help='Print debugging output of the request body. It is only '
'used with debug_header filter'),
]
CONF = cfg.CONF
CONF.register_opts(proxy_opts)
class APIController(Middleware):
_actions = ['pause', 'unpause', 'suspend', 'resume', 'os-migrateLive',
'migrate', 'resize', 'confirmResize', 'revertResize',
'reboot', 'os-stop', 'os-start', 'rebuild']
def __init__(self, app, conf):
self._default_dispatcher = APIDispatcher(app)
super(APIController, self).__init__(app)
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
self._generate_log(req)
LOG.debug("Check if it is a NFVI action")
if (req.environ['REQUEST_METHOD'] == 'POST' and
self._is_nfvi_request(req)):
remote_host = CONF.nfvi_compute_listen
remote_port = CONF.nfvi_compute_listen_port
return APIDispatcher(self.application, remote_host, remote_port)
return self._default_dispatcher
def _is_nfvi_request(self, request):
body = get_jason_request_body(request)
data = json.loads(body)
for action in self._actions:
if action in list(data):
environ = request.environ
LOG.info("Forward to NFV \"%s %s\", action: (%s), val:(%s)" % (
environ['REQUEST_METHOD'], construct_url(environ),
action, data[action]))
return True
return False
def _log_message(self, environ):
remote_addr = environ.get('HTTP_X_FORWARDED_FOR',
environ['REMOTE_ADDR'])
LOG.info("%s request issued by user (%s) tenant (%s) remote address "
"(%s)"
" \"%s %s\"" % (environ['REQUEST_METHOD'],
environ['HTTP_X_USER'],
environ['HTTP_X_TENANT'],
remote_addr,
environ['REQUEST_METHOD'],
construct_url(environ)))
def _generate_log(self, req):
environ = req.environ
body = get_jason_request_body(req)
if CONF.debug and body is not None:
data = json.loads(body)
self._print_data(data)
self._log_message(environ)
def _print_data(self, obj):
if type(obj) == dict:
for k, v in obj.items():
if hasattr(v, '__iter__'):
LOG.info("%s" % k)
self._print_data(v)
else:
LOG.info('%s : %s' % (k, v))
elif type(obj) == list:
for v in obj:
if hasattr(v, '__iter__'):
self._print_data(v)
else:
LOG.info("%s" % v)
else:
LOG.info("Obj: (%s)" % obj)
class Acceptor(Router):
_paths = ['/v2/{tenant_id:.*?}/servers/{server_id}/action',
'/v2.1/{tenant_id:.*?}/servers/{server_id}/action',
'/v2.1/{project_id:.*?}/servers/{server_id}/action',
'/v2.1/servers/{server_id}/action',
'/v2.1/{server_id}/action',
# paths that added for generating logs
'/v2.1/servers',
'/v2.1/{server_id}/servers',
'/v2.1/servers/{server_id}',
'/v2.1/{tenant_id:.*?}/servers',
'/v2.1/{tenant_id:.*?}/servers/{server_id}',
'/v2.1/{project_id:.*?}/servers/{server_id}']
def __init__(self, app, conf):
self._conf = conf
mapper = routes.Mapper()
forwarder = APIDispatcher(app)
api_controller = APIController(app, conf)
for path in self._paths:
mapper.connect(path, controller=api_controller,
conditions=dict(method=['POST', 'DELETE', 'PUT']))
super(Acceptor, self).__init__(app, conf, mapper, forwarder)
class VersionController(Middleware):
def __init__(self, app, conf):
self._default_dispatcher = Proxy()
self._remote_host = CONF.osapi_compute_listen
self._remote_port = CONF.osapi_compute_listen_port
super(VersionController, self).__init__(app)
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
LOG.debug("VersionController forward the version request to remote "
"host: (%s), port: (%d)" % (self._remote_host,
self._remote_port))
utils.set_request_forward_environ(req, self._remote_host,
self._remote_port)
return self._default_dispatcher
class VersionAcceptor(Router):
API_VERSION = '/{version:.*?}'
def __init__(self, app, conf):
self._conf = conf
mapper = routes.Mapper()
api_controller = VersionController(app, conf)
mapper.connect(self.API_VERSION, controller=api_controller,
conditions=dict(method=['GET']))
super(VersionAcceptor, self).__init__(app, conf, mapper)
class DebugHeaders(Middleware):
translate_keys = {
'CONTENT_LENGTH': 'HTTP_CONTENT_LENGTH',
'CONTENT_TYPE': 'HTTP_CONTENT_TYPE',
}
def __init__(self, app, conf):
self._show_body = CONF.show_request_body
super(DebugHeaders, self).__init__(app)
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
from paste.request import construct_url
environ = req.environ
LOG.info(
'Incoming headers: (%s %s)\n' %
(environ['REQUEST_METHOD'], construct_url(environ)))
for name, value in sorted(environ.items()):
name = self.translate_keys.get(name, name)
if not name.startswith('HTTP_'):
continue
name = name[5:].replace('_', '-').title()
LOG.info(' %s: %s\n' % (name, value))
if self._show_body:
self.show_request_body(environ)
return self.application
def show_request_body(self, environ):
length = int(environ.get('CONTENT_LENGTH') or '0')
body = environ['wsgi.input'].read(length)
if body:
for line in body.splitlines():
# This way we won't print out control characters:
LOG.info(line.encode('string_escape') + '\n')
LOG.info('-' * 70 + '\n')
def get_jason_request_body(request):
content_type = request.content_type
if not content_type or content_type.startswith('text/plain'):
LOG.info("Content type null or plain text")
content_type = 'application/json'
if content_type in ('JSON', 'application/json') and \
request.body.startswith('{'):
LOG.debug("Req body: (%s)" % request.body)
return request.body