Merge "Reduce duplication under SOL v2 API framework"

This commit is contained in:
Zuul 2024-03-17 17:34:34 +00:00 committed by Gerrit Code Review
commit a0eb01ee44
28 changed files with 148 additions and 576 deletions

View File

@ -90,19 +90,19 @@ paste.app_factory = tacker.sol_refactored.api.router:VnflcmVersions.factory
paste.app_factory = tacker.sol_refactored.api.router:VnffmAPIRouterV1.factory
[app:prometheus_auto_scaling]
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:AutoScalingRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:AutoScalingRouter.factory
[app:prometheus_auto_healing]
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:AutoHealingRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:AutoHealingRouter.factory
[app:prometheus_fm]
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:FmAlertRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:FmAlertRouter.factory
[app:prometheus_pm]
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:PmEventRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:PmEventRouter.factory
[app:prometheus_threshold]
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:PmThresholdRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:PmThresholdRouter.factory
[app:server_notification]
paste.app_factory = tacker.sol_refactored.api.server_notification_router:ServerNotificationRouter.factory
paste.app_factory = tacker.sol_refactored.api.router:ServerNotificationRouter.factory

View File

@ -1,54 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 tacker.sol_refactored.api.policies import vnffm_v1 as vnffm_policy_v1
from tacker.sol_refactored.api.policies import vnfpm_v2 as vnfpm_policy_v2
from tacker.sol_refactored.api import prometheus_plugin_wsgi as prom_wsgi
from tacker.sol_refactored.controller import prometheus_plugin_controller
class PmEventRouter(prom_wsgi.PrometheusPluginAPIRouter):
controller = prom_wsgi.PrometheusPluginResource(
prometheus_plugin_controller.PmEventController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "pm_event"})]
class PmThresholdRouter(prom_wsgi.PrometheusPluginAPIRouter):
controller = prom_wsgi.PrometheusPluginResource(
prometheus_plugin_controller.PmThresholdController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "pm_threshold"})]
class FmAlertRouter(prom_wsgi.PrometheusPluginAPIRouter):
controller = prom_wsgi.PrometheusPluginResource(
prometheus_plugin_controller.FmAlertController(),
policy_name=vnffm_policy_v1.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "alert"})]
class AutoHealingRouter(prom_wsgi.PrometheusPluginAPIRouter):
controller = prom_wsgi.PrometheusPluginResource(
prometheus_plugin_controller.AutoHealingController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "auto_healing"})]
class AutoScalingRouter(prom_wsgi.PrometheusPluginAPIRouter):
controller = prom_wsgi.PrometheusPluginResource(
prometheus_plugin_controller.AutoScalingController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "auto_scaling"})]

View File

@ -1,46 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 functools
from tacker.api.validation import validators
from tacker.common import exceptions as tacker_ex
from tacker.sol_refactored.common import exceptions as sol_ex
class PrometheusPluginSchemaValidator(validators._SchemaValidator):
def validate(self, *args, **kwargs):
try:
super(PrometheusPluginSchemaValidator, self).validate(
*args, **kwargs)
except tacker_ex.ValidationError as ex:
raise sol_ex.PrometheusPluginValidationError(detail=str(ex))
def schema(request_body_schema):
def add_validator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'body' not in kwargs:
raise sol_ex.PrometheusPluginValidationError(
detail="body is missing.")
schema_validator = PrometheusPluginSchemaValidator(
request_body_schema)
schema_validator.validate(kwargs['body'])
return func(*args, **kwargs)
return wrapper
return add_validator

View File

@ -1,68 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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_log import log as logging
from tacker.sol_refactored.api import wsgi as sol_wsgi
import webob
LOG = logging.getLogger(__name__)
class PrometheusPluginResponse(sol_wsgi.SolResponse):
allowed_headers = ['content_type']
def __init__(self, status, body, **kwargs):
self.status = status
self.body = body
self.headers = {}
for hdr in self.allowed_headers:
if hdr in kwargs:
self.headers[hdr] = kwargs[hdr]
class PrometheusPluginErrorResponse(sol_wsgi.SolErrorResponse):
pass
class PrometheusPluginResource(sol_wsgi.SolResource):
@webob.dec.wsgify(RequestClass=sol_wsgi.SolRequest)
def __call__(self, request):
LOG.info("%(method)s %(url)s", {"method": request.method,
"url": request.url})
try:
action, args, accept = self._deserialize_request(request)
self._check_policy(request, action)
result = self._dispatch(request, action, args)
response = result.serialize(accept)
except Exception as ex:
result = PrometheusPluginErrorResponse(ex, request)
try:
response = result.serialize('application/problem+json')
except Exception:
LOG.exception("Unknown error")
return webob.exc.HTTPBadRequest(explanation="Unknown error")
LOG.info("%(url)s returned with HTTP %(status)d",
{"url": request.url, "status": response.status_int})
return response
class PrometheusPluginAPIRouter(sol_wsgi.SolAPIRouter):
pass
class PrometheusPluginAPIController(sol_wsgi.SolAPIController):
pass

View File

@ -18,6 +18,8 @@ from tacker.sol_refactored.api.policies import vnffm_v1 as vnffm_policy_v1
from tacker.sol_refactored.api.policies import vnflcm_v2 as vnflcm_policy_v2
from tacker.sol_refactored.api.policies import vnfpm_v2 as vnfpm_policy_v2
from tacker.sol_refactored.api import wsgi as sol_wsgi
from tacker.sol_refactored.controller import prometheus_plugin_controller
from tacker.sol_refactored.controller import server_notification
from tacker.sol_refactored.controller import vnffm_v1
from tacker.sol_refactored.controller import vnflcm_v2
from tacker.sol_refactored.controller import vnflcm_versions
@ -90,3 +92,52 @@ class VnfPmAPIRouterV2(sol_wsgi.SolAPIRouter):
"GET": "show_threshold",
"DELETE": "delete_threshold"}),
]
# The definitions after here are of tacker original APIs.
# Although these APIs are not included in ESTI SOL specification,
# these APIs are (should be) designed as same as SOL APIs and
# use same API frameworks (i.e. modules in this directory).
class PmEventRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
prometheus_plugin_controller.PmEventController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "pm_event"})]
class PmThresholdRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
prometheus_plugin_controller.PmThresholdController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "pm_threshold"})]
class FmAlertRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
prometheus_plugin_controller.FmAlertController(),
policy_name=vnffm_policy_v1.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "alert"})]
class AutoHealingRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
prometheus_plugin_controller.AutoHealingController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "auto_healing"})]
class AutoScalingRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
prometheus_plugin_controller.AutoScalingController(),
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
route_list = [("", {"POST": "auto_scaling"})]
class ServerNotificationRouter(sol_wsgi.SolAPIRouter):
controller = sol_wsgi.SolResource(
server_notification.ServerNotificationController(),
policy_name=vnflcm_policy_v2.SERVER_NOTIFICATION_POLICY_NAME)
route_list = [
("/vnf_instances/{vnf_instance_id}/servers/{server_id}/notify",
{"POST": "notify"})
]

View File

@ -1,28 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 tacker.sol_refactored.api.policies import vnflcm_v2
from tacker.sol_refactored.api import server_notification_wsgi as sn_wsgi
from tacker.sol_refactored.controller import server_notification
class ServerNotificationRouter(sn_wsgi.ServerNotificationAPIRouter):
controller = sn_wsgi.ServerNotificationResource(
server_notification.ServerNotificationController(),
policy_name=vnflcm_v2.SERVER_NOTIFICATION_POLICY_NAME)
route_list = [
("/vnf_instances/{vnf_instance_id}/servers/{server_id}/notify",
{"POST": "notify"})
]

View File

@ -1,51 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 functools
from tacker.api.validation import validators
from tacker.common import exceptions as tacker_ex
from tacker.sol_refactored.common import exceptions as sol_ex
# TODO(shimizu-koji): `validators._SchemaValidator` is protected class,
# thus it shouldn't be referred from other modules. This refactoring
# will be done in other patches in the future.
class ServerNotificationSchemaValidator(validators._SchemaValidator):
def validate(self, *args, **kwargs):
try:
super(ServerNotificationSchemaValidator, self).validate(
*args, **kwargs)
except tacker_ex.ValidationError as ex:
raise sol_ex.ServerNotificationValidationError(detail=str(ex))
def schema(request_body_schema):
def add_validator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'body' not in kwargs:
raise sol_ex.ServerNotificationValidationError(
detail="body is missing.")
schema_validator = ServerNotificationSchemaValidator(
request_body_schema)
schema_validator.validate(kwargs['body'])
return func(*args, **kwargs)
return wrapper
return add_validator

View File

@ -1,82 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 webob
from oslo_log import log as logging
from tacker.sol_refactored.api import wsgi as sol_wsgi
LOG = logging.getLogger(__name__)
class ServerNotificationResponse(sol_wsgi.SolResponse):
allowed_headers = ['content_type']
def __init__(self, status, body, **kwargs):
self.status = status
self.body = body
self.headers = {}
for hdr in self.allowed_headers:
if hdr in kwargs:
self.headers[hdr] = kwargs[hdr]
class ServerNotificationErrorResponse(ServerNotificationResponse):
def __init__(self, ex, _):
status = ex.status if hasattr(ex, 'status') else 'error'
detail = ex.status if hasattr(ex, 'detail') else 'error'
problem_details = {'status': status, 'detail': detail}
if hasattr(ex, 'title'):
problem_details['title'] = ex.title
super(ServerNotificationErrorResponse, self).__init__(
problem_details['status'], problem_details)
class ServerNotificationResource(sol_wsgi.SolResource):
def __init__(self, controller, policy_name=None):
super(ServerNotificationResource, self).__init__(
controller, policy_name=policy_name
)
@webob.dec.wsgify(RequestClass=sol_wsgi.SolRequest)
def __call__(self, request):
LOG.info("%(method)s %(url)s", {"method": request.method,
"url": request.url})
try:
action, args, accept = self._deserialize_request(request)
self._check_policy(request, action)
result = self._dispatch(request, action, args)
response = result.serialize(accept)
except Exception as ex:
result = ServerNotificationErrorResponse(ex, request)
try:
response = result.serialize('application/problem+json')
except Exception:
LOG.exception("Unknown error")
return webob.exc.HTTPBadRequest(explanation="Unknown error")
LOG.info("%(url)s returned with HTTP %(status)d",
{"url": request.url, "status": response.status_int})
return response
class ServerNotificationAPIRouter(sol_wsgi.SolAPIRouter):
pass
class ServerNotificationAPIController(sol_wsgi.SolAPIController):
pass

View File

@ -49,3 +49,19 @@ def schema(request_body_schema, min_version, max_version=None):
return wrapper
return add_validator
def schema_nover(request_body_schema):
def add_validator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if 'body' not in kwargs:
raise sol_ex.SolValidationError(detail="body is missing")
schema_validator = SolSchemaValidator(request_body_schema)
schema_validator.validate(kwargs['body'])
return func(*args, **kwargs)
return wrapper
return add_validator

View File

@ -69,8 +69,6 @@ class SolResponse(object):
for hdr in self.allowed_headers:
if kwargs.get(hdr):
self.headers[hdr] = kwargs[hdr]
self.headers.setdefault('version', api_version.CURRENT_VERSION)
self.headers.setdefault('accept-ranges', 'none')
def serialize(self, content_type):
self.headers.setdefault('content_type', content_type)
@ -132,13 +130,11 @@ class SolResource(object):
self._check_api_version(request, action)
self._check_policy(request, action)
result = self._dispatch(request, action, args)
self.controller.set_default_to_response(result, action)
response = result.serialize(accept)
except Exception as ex:
result = SolErrorResponse(ex, request.best_match_language())
if type(self.controller).__name__ == 'VnfFmControllerV1':
result.headers['version'] = api_version.CURRENT_FM_VERSION
if type(self.controller).__name__ == 'VnfPmControllerV2':
result.headers['version'] = api_version.CURRENT_PM_VERSION
self.controller.set_default_to_response(result, action)
try:
response = result.serialize('application/problem+json')
except Exception:
@ -202,7 +198,7 @@ class SolResource(object):
return request.body_file
else:
# assume json format
# ex. 'application/json', 'application/mergepatch+json'
# ex. 'application/json', 'application/merge-patch+json'
try:
return request.json
except Exception:
@ -275,10 +271,10 @@ class SolAPIController(object):
raise sol_ex.MethodNotAllowed(method=request.method)
def supported_api_versions(self, action):
# NOTE: support v2 API by default. if a contorller supports
# and/or v1 API, or depending on action, override this method
# in the subclass.
return api_version.v2_versions
# NOTE: if a contorller supports versions header, override
# this method in the subclass. return None means version is
# not checked.
return None
def allowed_content_types(self, action):
# NOTE: if other than 'application/json' is expected depending
@ -291,3 +287,6 @@ class SolAPIController(object):
# NOTE: if other than 'application/json' is expected depending
# on action, override this method in the subclass.
return ['application/json']
def set_default_to_response(self, result, action):
pass

View File

@ -448,10 +448,6 @@ class PrometheusPluginSkipped(Exception):
pass
class PrometheusPluginValidationError(SolValidationError):
pass
class PrometheusSettingFailed(SolHttpError503):
message = _("Setting PM job on External Monitoring Tool failed.")
@ -459,7 +455,3 @@ class PrometheusSettingFailed(SolHttpError503):
# server_notification
class ServerNotificationNotEnabled(SolHttpError404):
message = _("ServerNotification API is not enabled.")
class ServerNotificationValidationError(SolValidationError):
message = _("%(detail)s")

View File

@ -26,8 +26,8 @@ from keystoneauth1 import exceptions as ks_exc
from oslo_log import log as logging
from oslo_utils import uuidutils
from tacker.common import utils
from tacker.sol_refactored.api import prometheus_plugin_validator as validator
from tacker.sol_refactored.api.schemas import prometheus_plugin_schemas
from tacker.sol_refactored.api import validator
from tacker.sol_refactored.common import config as cfg
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import fm_alarm_utils
@ -505,7 +505,7 @@ class PrometheusPluginPm(PrometheusPluginPmBase, mon_base.MonitoringPlugin):
f"doesn't match pmJob.")
raise sol_ex.PrometheusPluginSkipped()
@validator.schema(prometheus_plugin_schemas.AlertMessage)
@validator.schema_nover(prometheus_plugin_schemas.AlertMessage)
def _alert(self, request, body):
result = []
context = request.context
@ -762,7 +762,7 @@ class PrometheusPluginThreshold(PrometheusPluginPmBase,
crossing_direction = "DOWN"
return crossing_direction
@validator.schema(prometheus_plugin_schemas.AlertMessage)
@validator.schema_nover(prometheus_plugin_schemas.AlertMessage)
def _alert(self, request, body):
result = []
context = request.context
@ -1100,7 +1100,7 @@ class PrometheusPluginFm(PrometheusPlugin, mon_base.MonitoringPlugin):
return [new_alarm]
raise sol_ex.PrometheusPluginSkipped()
@validator.schema(prometheus_plugin_schemas.AlertMessage)
@validator.schema_nover(prometheus_plugin_schemas.AlertMessage)
def _alert(self, request, body):
now = datetime.datetime.now(datetime.timezone.utc)
result = []
@ -1153,7 +1153,7 @@ class PrometheusPluginAutoHealing(PrometheusPlugin, mon_base.MonitoringPlugin):
self.rpc.enqueue_auto_heal_instance(
context, vnf_instance_id, vnfc_info_id)
@validator.schema(prometheus_plugin_schemas.AlertMessage)
@validator.schema_nover(prometheus_plugin_schemas.AlertMessage)
def _alert(self, request, body):
context = request.context
alerts = (alert for alert in body['alerts'] if
@ -1225,7 +1225,7 @@ class PrometheusPluginAutoScaling(PrometheusPlugin, mon_base.MonitoringPlugin):
def default_callback(self, context, vnf_instance_id, scaling_param):
self.rpc.trigger_scale(context, vnf_instance_id, scaling_param)
@validator.schema(prometheus_plugin_schemas.AlertMessage)
@validator.schema_nover(prometheus_plugin_schemas.AlertMessage)
def _alert(self, request, body):
context = request.context
alerts = (alert for alert in body['alerts'] if

View File

@ -15,8 +15,7 @@
from oslo_log import log as logging
from tacker.sol_refactored.api.schemas import server_notification_schemas
from tacker.sol_refactored.api import server_notification_validator\
as validator
from tacker.sol_refactored.api import validator
from tacker.sol_refactored.common import config as cfg
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
@ -69,11 +68,11 @@ class ServerNotification(mon_base.MonitoringPlugin):
'vnfcResourceInfo') or
not vnf_instance.instantiatedVnfInfo.obj_attr_is_set(
'vnfcInfo')):
raise sol_ex.ServerNotificationValidationError(
raise sol_ex.SolValidationError(
detail="access info not found in the vnf instance.")
if fault_id not in vnf_instance.instantiatedVnfInfo.metadata.get(
'ServerNotifierFaultID', []):
raise sol_ex.ServerNotificationValidationError(
raise sol_ex.SolValidationError(
detail="fault_id does not match.")
# Get the list of instantiatedVnfInfo.vnfcInfo[x].id where
@ -91,16 +90,16 @@ class ServerNotification(mon_base.MonitoringPlugin):
vnfc_ids = list(map(lambda x: x.id, vnfc_info))
if len(vnfc_ids) == 0:
raise sol_ex.ServerNotificationValidationError(
raise sol_ex.SolValidationError(
detail="target vnfc not found.")
return vnfc_ids
@validator.schema(server_notification_schemas.ServerNotification)
@validator.schema_nover(server_notification_schemas.ServerNotification)
def notify(self, request, vnf_instance_id, body):
context = request.context
vnf_instance = inst_utils.get_inst(context, vnf_instance_id)
if not vnf_instance:
raise sol_ex.ServerNotificationValidationError(
raise sol_ex.SolValidationError(
detail="target vnf instance not found.")
if (not vnf_instance.obj_attr_is_set(
'vnfConfigurableProperties') or

View File

@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from tacker.sol_refactored.api import prometheus_plugin_wsgi as prom_wsgi
from tacker.sol_refactored.api import wsgi as sol_wsgi
from tacker.sol_refactored.common import config as cfg
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
@ -21,7 +21,7 @@ from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
CONF = cfg.CONF
class PmEventController(prom_wsgi.PrometheusPluginAPIController):
class PmEventController(sol_wsgi.SolAPIController):
def pm_event(self, request, body):
if not CONF.prometheus_plugin.performance_management:
raise sol_ex.PrometheusPluginNotEnabled(
@ -31,10 +31,10 @@ class PmEventController(prom_wsgi.PrometheusPluginAPIController):
CONF.prometheus_plugin.performance_management_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
return sol_wsgi.SolResponse(204, None)
class PmThresholdController(prom_wsgi.PrometheusPluginAPIController):
class PmThresholdController(sol_wsgi.SolAPIController):
def pm_threshold(self, request, body):
if not CONF.prometheus_plugin.performance_management:
raise sol_ex.PrometheusPluginNotEnabled(
@ -44,10 +44,10 @@ class PmThresholdController(prom_wsgi.PrometheusPluginAPIController):
CONF.prometheus_plugin.performance_management_threshold_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
return sol_wsgi.SolResponse(204, None)
class FmAlertController(prom_wsgi.PrometheusPluginAPIController):
class FmAlertController(sol_wsgi.SolAPIController):
def alert(self, request, body):
if not CONF.prometheus_plugin.fault_management:
raise sol_ex.PrometheusPluginNotEnabled(
@ -57,10 +57,10 @@ class FmAlertController(prom_wsgi.PrometheusPluginAPIController):
CONF.prometheus_plugin.fault_management_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
return sol_wsgi.SolResponse(204, None)
class AutoHealingController(prom_wsgi.PrometheusPluginAPIController):
class AutoHealingController(sol_wsgi.SolAPIController):
def auto_healing(self, request, body):
if not CONF.prometheus_plugin.auto_healing:
raise sol_ex.PrometheusPluginNotEnabled(
@ -70,10 +70,10 @@ class AutoHealingController(prom_wsgi.PrometheusPluginAPIController):
CONF.prometheus_plugin.auto_healing_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
return sol_wsgi.SolResponse(204, None)
class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
class AutoScalingController(sol_wsgi.SolAPIController):
def auto_scaling(self, request, body):
if not CONF.prometheus_plugin.auto_scaling:
raise sol_ex.PrometheusPluginNotEnabled(
@ -83,4 +83,4 @@ class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
CONF.prometheus_plugin.auto_scaling_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
return sol_wsgi.SolResponse(204, None)

View File

@ -13,7 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
from tacker.sol_refactored.api import server_notification_wsgi as sn_wsgi
from tacker.sol_refactored.api import wsgi as sol_wsgi
from tacker.sol_refactored.common import config as cfg
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
@ -21,7 +21,7 @@ from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
CONF = cfg.CONF
class ServerNotificationController(sn_wsgi.ServerNotificationAPIController):
class ServerNotificationController(sol_wsgi.SolAPIController):
def notify(self, request, vnf_instance_id, server_id, body):
if not CONF.server_notification.server_notification:
raise sol_ex.ServerNotificationNotEnabled()
@ -31,4 +31,4 @@ class ServerNotificationController(sn_wsgi.ServerNotificationAPIController):
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, vnf_instance_id=vnf_instance_id,
server_id=server_id, body=body)
return sn_wsgi.ServerNotificationResponse(204, None)
return sol_wsgi.SolResponse(204, None)

View File

@ -53,10 +53,10 @@ class VnfFmControllerV1(sol_wsgi.SolAPIController):
def allowed_content_types(self, action):
if action == 'update':
# Content-Type of Modify request shall be
# 'application/mergepatch+json' according to SOL spec.
# 'application/merge-patch+json' according to SOL spec.
# But 'application/json' and 'text/plain' is OK for backward
# compatibility.
return ['application/mergepatch+json', 'application/json',
return ['application/merge-patch+json', 'application/json',
'text/plain']
return ['application/json', 'text/plain']
@ -176,3 +176,7 @@ class VnfFmControllerV1(sol_wsgi.SolAPIController):
return sol_wsgi.SolResponse(204, None,
version=api_version.CURRENT_FM_VERSION)
def set_default_to_response(self, result, action):
result.headers.setdefault('version', api_version.CURRENT_FM_VERSION)
result.headers.setdefault('accept-ranges', 'none')

View File

@ -650,10 +650,14 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
def allowed_content_types(self, action):
if action == 'update':
# Content-Type of Modify request shall be
# 'application/mergepatch+json' according to SOL spec.
# 'application/merge-patch+json' according to SOL spec.
# But 'application/json' and 'text/plain' is OK for backward
# compatibility.
return ['application/mergepatch+json', 'application/json',
return ['application/merge-patch+json', 'application/json',
'text/plain']
else:
return ['application/json', 'text/plain']
def set_default_to_response(self, result, action):
result.headers.setdefault('version', api_version.CURRENT_VERSION)
result.headers.setdefault('accept-ranges', 'none')

View File

@ -29,3 +29,7 @@ class VnfLcmVersionsController(sol_wsgi.SolAPIController):
def supported_api_versions(self, action):
# support all versions and it is OK there is no Version header.
return None
def set_default_to_response(self, result, action):
result.headers.setdefault('version', api_version.CURRENT_VERSION)
result.headers.setdefault('accept-ranges', 'none')

View File

@ -278,15 +278,15 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
def allowed_content_types(self, action):
if action in {'update', 'update_threshold'}:
# Content-Type of Modify request shall be
# 'application/mergepatch+json' according to SOL spec.
# 'application/merge-patch+json' according to SOL spec.
# But 'application/json' and 'text/plain' is OK for backward
# compatibility.
return ['application/mergepatch+json', 'application/json',
return ['application/merge-patch+json', 'application/json',
'text/plain']
return ['application/json', 'text/plain']
def allowed_accept(self, action):
return ['application/json', 'application/mergepatch+json',
return ['application/json', 'application/merge-patch+json',
'text/plain']
def supported_api_versions(self, action):
@ -441,3 +441,7 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
return sol_wsgi.SolResponse(204, None,
version=api_version.CURRENT_PM_VERSION)
def set_default_to_response(self, result, action):
result.headers.setdefault('version', api_version.CURRENT_PM_VERSION)
result.headers.setdefault('accept-ranges', 'none')

View File

@ -155,7 +155,7 @@ class BaseVnfLcmKubernetesV2Test(base_v2.BaseTackerTestV2):
path = f"/vnfpm/v2/thresholds/{pm_threshold_id}"
return self.tacker_client.do_request(
path, "PATCH", body=req_body, version=VNFPM_V2_VERSION,
content_type="application/mergepatch+json")
content_type="application/merge-patch+json")
def pm_threshold(self, req_body):
path = "/pm_threshold"

View File

@ -1,88 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 tacker import context
from tacker.sol_refactored.api import prometheus_plugin_wsgi as pp_wsgi
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored import objects
from tacker.tests.unit import base
from unittest import mock
class TestPrometheusPlugin(base.TestCase):
def setUp(self):
super(TestPrometheusPlugin, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
self.request = mock.Mock()
self.request.context = self.context
@mock.patch.object(pp_wsgi.PrometheusPluginErrorResponse, 'serialize')
def test_response(self, mock_serialize_pp):
class _Test():
def __init__(self, ctx, title):
self.status = 200
self.detail = 'detail'
self.title = title
self.method = 'GET'
self.url = 'url'
self.environ = None
self.body = {}
self.context = ctx
self.status_int = 200
def best_match_content_type(self):
return 'application/json'
def serialize(self, accept):
if self.title == 'error':
raise sol_ex.SolValidationError(
detail='test error')
return self
def test(*args, **kwargs):
return (None, None, None)
def test2(*args, **kwargs):
return _Test(None, None)
def test3(*args, **kwargs):
return _Test(None, 'error')
# make responses
pp_wsgi.PrometheusPluginResponse(
200, {}, content_type='content_type')
pp_wsgi.PrometheusPluginErrorResponse(
_Test(self.context, None), None)
pp_wsgi.PrometheusPluginErrorResponse(
_Test(self.context, 'title'), None)
# no error
p = pp_wsgi.PrometheusPluginResource(
None, 'tacker_prometheus_plugin_api:prometheus_plugin:alert')
p(_Test(self.context, None))
# raise unknown error
p = pp_wsgi.PrometheusPluginResource(
None, 'tacker_prometheus_plugin_api:prometheus_plugin:alert')
p._deserialize_request = test
p._check_policy = test
p._dispatch = test2
p(_Test(self.context, None))
mock_serialize_pp.side_effect = _Test(self.context, 'error')
p._dispatch = test3
p(_Test(self.context, 'error'))

View File

@ -1,87 +0,0 @@
# Copyright (C) 2022 Fujitsu
# 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 tacker import context
from tacker.sol_refactored.api import server_notification_wsgi as sn_wsgi
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored import objects
from tacker.tests.unit import base
from unittest import mock
class TestServerNotification(base.TestCase):
def setUp(self):
super(TestServerNotification, self).setUp()
objects.register_all()
self.context = context.get_admin_context()
self.request = mock.Mock()
self.request.context = self.context
@mock.patch.object(sn_wsgi.ServerNotificationErrorResponse, 'serialize')
def test_response(self, mock_serialize_pp):
class _Test():
def __init__(self, ctx, title):
self.status = 200
self.detail = 'detail'
self.title = title
self.method = 'GET'
self.url = 'url'
self.environ = None
self.body = {}
self.context = ctx
self.status_int = 200
def best_match_content_type(self):
return 'application/json'
def serialize(self, _):
if self.title == 'error':
raise sol_ex.SolValidationError(
detail='test error')
return self
def test(*args, **kwargs):
return (None, None, None)
def test2(*args, **kwargs):
return _Test(None, None)
def test3(*args, **kwargs):
return _Test(None, 'error')
# make responses
sn_wsgi.ServerNotificationResponse(
200, {}, content_type='content_type')
sn_wsgi.ServerNotificationErrorResponse(
_Test(self.context, None), None)
sn_wsgi.ServerNotificationErrorResponse(
_Test(self.context, 'title'), None)
# no error
p = sn_wsgi.ServerNotificationResource(
None, 'tacker_server_notification_api:server_notification:notify')
p(_Test(self.context, None))
# raise unknown error
p = sn_wsgi.ServerNotificationResource(
None, 'tacker_server_notification_api:server_notification:notify')
p._deserialize_request = test
p._check_policy = test
p._dispatch = test2
p(_Test(self.context, None))
mock_serialize_pp.side_effect = _Test(self.context, 'error')
p._dispatch = test3
p(_Test(self.context, 'error'))

View File

@ -46,7 +46,9 @@ class TestWsgi(base.TestCase):
self.assertEqual(problem_details, response.body)
def test_check_api_version_no_version(self):
resource = sol_wsgi.SolResource(sol_wsgi.SolAPIController())
controller = sol_wsgi.SolAPIController()
controller.supported_api_versions = mock.Mock(return_value=['1.0'])
resource = sol_wsgi.SolResource(controller)
request = mock.Mock()
request.headers = {}
self.assertRaises(sol_ex.APIVersionMissing,

View File

@ -1425,13 +1425,13 @@ class TestPrometheusPluginFm(base.TestCase):
prometheus_plugin.PrometheusPluginFm)
self.assertIsInstance(pp._instance, mon_base.MonitoringPluginStub)
def test_pm_no_body(self):
def test_fm_no_body(self):
self.config_fixture.config(
group='prometheus_plugin', performance_management=True)
group='prometheus_plugin', fault_management=True)
pp = mon_base.MonitoringPlugin.get_instance(
prometheus_plugin.PrometheusPluginPm)
prometheus_plugin.PrometheusPluginFm)
self.assertRaises(
sol_ex.PrometheusPluginValidationError,
sol_ex.SolValidationError,
pp._alert, self.request)

View File

@ -124,7 +124,7 @@ class TestServerNotification(base.TestCase):
sn = mon_base.MonitoringPlugin.get_instance(
server_notification.ServerNotification)
self.assertRaises(
sol_ex.ServerNotificationValidationError,
sol_ex.SolValidationError,
sn.notify, self.request, 'test_id')
def test_constructor_error(self):

View File

@ -152,7 +152,7 @@ class TestServerNotification(base.TestCase):
self.config_fixture.config(
group='server_notification', server_notification=True)
self.assertRaises(
sol_ex.ServerNotificationValidationError,
sol_ex.SolValidationError,
self.controller.notify,
request=self.request,
vnf_instance_id='test_id',
@ -169,7 +169,7 @@ class TestServerNotification(base.TestCase):
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst)
self.assertRaises(
sol_ex.ServerNotificationValidationError,
sol_ex.SolValidationError,
self.controller.notify, request=self.request,
vnf_instance_id='test_id',
server_id='test_server_id', body=_body)
@ -185,7 +185,7 @@ class TestServerNotification(base.TestCase):
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst)
self.assertRaises(
sol_ex.ServerNotificationValidationError,
sol_ex.SolValidationError,
self.controller.notify, request=self.request,
vnf_instance_id='test_id',
server_id='test_server_id', body=_body)
@ -196,7 +196,7 @@ class TestServerNotification(base.TestCase):
group='server_notification', server_notification=True)
mock_inst.return_value = None
self.assertRaises(
sol_ex.ServerNotificationValidationError,
sol_ex.SolValidationError,
self.controller.notify, request=self.request,
vnf_instance_id='test_id',
server_id='test_server_id', body=_body)

View File

@ -59,7 +59,7 @@ class TestVnffmV1(base.BaseTestCase):
self.assertEqual(['application/json', 'text/plain'], result)
result = self.controller.allowed_content_types('update')
self.assertEqual(['application/mergepatch+json', 'application/json',
self.assertEqual(['application/merge-patch+json', 'application/json',
'text/plain'], result)
@mock.patch.object(alarm_utils, 'get_alarms_all')

View File

@ -388,7 +388,8 @@ class TestVnfpmV2(base.BaseTestCase):
def test_allowed_content_types(self):
result = self.controller.allowed_content_types('update')
top = ['application/mergepatch+json', 'application/json', 'text/plain']
top = ['application/merge-patch+json', 'application/json',
'text/plain']
self.assertEqual(top, result)
result = self.controller.allowed_content_types('create')