Improve plugin flexibility

The monitoring plugin such as prometheus plugin or
fault notification have been modified
to be replaceable according to the configuration.

Therefore, operators can use their original monitoring
functions without changing tacker source code.

Implements: blueprint support-auto-lcm
Implements: blueprint support-autoheal-queue
Change-Id: Ifcdf12c4be2d307c78b38703fe6db4e5aac236ea
This commit is contained in:
Koji Shimizu 2022-10-19 00:07:19 +00:00
parent a97899864c
commit c297b4300d
14 changed files with 197 additions and 41 deletions

View File

@ -118,17 +118,17 @@ The ``additionalParams`` must be set when using FaultNotification.
- Cardinality
- Description
* - additionalParams
- 0..1
- KeyValuePairs (inlined)
- 0..1
- Additional input parameters for the instantiation process,
specific to the VNF being instantiated.
* - >ServerNotifierUri
- 1
- String
- 1
- Base Uri for ServerNotifier.
* - >ServerNotifierFaultID
- 1..N
- String
- 1..N
- List of string that indicates which type of alarms to detect.
The value of ``ServerNotifierUri`` and ``ServerNotifierFaultID`` are stored
@ -162,6 +162,28 @@ whether AutoHealing should be performed. In case of performing
AutoHealing, VMs are deleted and created via Heat. The client is
no need to handle healing.
Using Vendor Specific Plugin
----------------------------
ServerNotification plugin can be replaced with a vendor specific function.
To replace a plugin, change the configurations below.
The replaced class must be a subclass of
tacker.sol_refactored.common.monitoring_plugin_base.MonitoringPlugin.
.. list-table::
:header-rows: 1
:widths: 40 40 40
* - Configuration
- Default
- Description
* - ``CONF.server_notification.server_notification_package``
- tacker.sol_refactored.common.server_notification
- Package name for server notification.
* - ``CONF.server_notification.server_notification_class``
- ServerNotification
- Class name for server notification.
References
==========

View File

@ -334,3 +334,37 @@ rule file directly. Below is example of alert rule.
auto_scale_type: SCALE_OUT,
aspect_id: VDU1_aspect
annotations:
Using Vendor Specific Plugin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Prometheus Plugin can be replaced with a vendor specific function.
To replace a plugin, change the configurations below.
The replaced class must be a subclass of
tacker.sol_refactored.common.monitoring_plugin_base.MonitoringPlugin.
.. list-table::
:header-rows: 1
:widths: 40 40 40
* - Configuration
- Default
- Description
* - ``CONF.prometheus_plugin.performance_management_package``
- tacker.sol_refactored.common.prometheus_plugin
- Package name for performance management.
* - ``CONF.prometheus_plugin.performance_management_class``
- PrometheusPluginPm
- Class name for performance management.
* - ``CONF.prometheus_plugin.fault_management_package``
- tacker.sol_refactored.common.prometheus_plugin
- Package name for fault management.
* - ``CONF.prometheus_plugin.fault_management_class``
- PrometheusPluginFm
- Class name for fault management.
* - ``CONF.prometheus_plugin.auto_scaling_package``
- tacker.sol_refactored.common.prometheus_plugin
- Package name for auto scaling.
* - ``CONF.prometheus_plugin.auto_scaling_class``
- PrometheusPluginAutoScaling
- Class name for auto scaling.

View File

@ -116,18 +116,51 @@ PROMETHEUS_PLUGIN_OPTS = [
cfg.BoolOpt('performance_management',
default=False,
help=_('Enable prometheus plugin performance management')),
cfg.IntOpt('reporting_period_margin',
default=1,
help=_('Some margin time for PM jos\'s reportingPeriod')),
cfg.BoolOpt('fault_management',
default=False,
help=_('Enable prometheus plugin fault management')),
cfg.BoolOpt('auto_scaling',
default=False,
help=_('Enable prometheus plugin autoscaling')),
cfg.StrOpt('performance_management_package',
default='tacker.sol_refactored.common.prometheus_plugin',
help=_('Package name for performance management. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('performance_management_class',
default='PrometheusPluginPm',
help=_('Class name for performance management. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('fault_management_package',
default='tacker.sol_refactored.common.prometheus_plugin',
help=_('Package name for fault management. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('fault_management_class',
default='PrometheusPluginFm',
help=_('Class name for fault management. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('auto_scaling_package',
default='tacker.sol_refactored.common.prometheus_plugin',
help=_('Package name for auto scaling. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('auto_scaling_class',
default='PrometheusPluginAutoScaling',
help=_('Class name for auto scaling. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
]
CONF.register_opts(PROMETHEUS_PLUGIN_OPTS, 'prometheus_plugin')
@ -136,7 +169,6 @@ SERVER_NOTIFICATION_OPTS = [
cfg.BoolOpt('server_notification',
default=False,
help=_('Enable server notification autohealing')),
cfg.StrOpt('uri_path_prefix',
default='/server_notification',
help=_('Uri path prefix string for server notification. '
@ -147,6 +179,18 @@ SERVER_NOTIFICATION_OPTS = [
default=20,
help=_('Timeout (second) of packing for multiple '
'server notification.')),
cfg.StrOpt('server_notification_package',
default='tacker.sol_refactored.common.server_notification',
help=_('Package name for server notification. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
cfg.StrOpt('server_notification_class',
default='ServerNotification',
help=_('Class name for server notification. '
'This configuration is changed in case of replacing '
'the original function with a vendor specific '
'function.')),
]
CONF.register_opts(SERVER_NOTIFICATION_OPTS, 'server_notification')

View File

@ -416,6 +416,11 @@ class ResourcesOtherOperationInProgress(SolHttpError409):
"is in progress.")
# plugin base
class MonitoringPluginClassError(SolHttpError503):
message = _("%(class_name)s is not a subclass of MonitoringPlugin.")
# prometheus plugin
class PrometheusPluginNotEnabled(SolHttpError404):
message = _("%(name)s API is not enabled.")

View File

@ -14,29 +14,20 @@
# under the License.
from importlib import import_module
from oslo_log import log as logging
module_and_class = {
'stub':
('tacker.sol_refactored.common.monitoring_plugin_base',
'MonitoringPluginStub'),
'pm_event':
('tacker.sol_refactored.common.prometheus_plugin',
'PrometheusPluginPm'),
'alert':
('tacker.sol_refactored.common.prometheus_plugin',
'PrometheusPluginFm'),
'auto_healing':
('tacker.sol_refactored.common.prometheus_plugin',
'PrometheusPluginAutoScaling'),
'server_notification':
('tacker.sol_refactored.common.server_notification',
'ServerNotification'),
}
from tacker.sol_refactored.common import exceptions as sol_ex
LOG = logging.getLogger(__name__)
def get_class(short_name):
module = import_module(module_and_class[short_name][0])
return getattr(module, module_and_class[short_name][1])
def get_class(package_name, class_name):
module = import_module(package_name)
_class = getattr(module, class_name)
if not issubclass(_class, MonitoringPlugin):
LOG.error(f"Loading plugin failed: {package_name}.{class_name}.")
raise sol_ex.MonitoringPluginClassError(class_name=class_name)
return _class
class MonitoringPlugin():

View File

@ -398,7 +398,7 @@ class PrometheusPluginPm(PrometheusPlugin, mon_base.MonitoringPlugin):
collection_period))
return rules
def get_compute_resouce_by_sub_obj(self, vnf_instance, sub_obj):
def get_compute_resource_by_sub_obj(self, vnf_instance, sub_obj):
inst = vnf_instance
if (not inst.obj_attr_is_set('instantiatedVnfInfo') or
not inst.instantiatedVnfInfo.obj_attr_is_set(
@ -440,7 +440,7 @@ class PrometheusPluginPm(PrometheusPlugin, mon_base.MonitoringPlugin):
# resource id is like 'test-test1-756757f8f-xcwmt'
# obtain 'test-test1' as deployment
# obtain 'test' as container
compute_resource = self.get_compute_resouce_by_sub_obj(
compute_resource = self.get_compute_resource_by_sub_obj(
inst, sub_obj)
if not compute_resource:
continue

View File

@ -26,7 +26,9 @@ class PmEventController(prom_wsgi.PrometheusPluginAPIController):
if not CONF.prometheus_plugin.performance_management:
raise sol_ex.PrometheusPluginNotEnabled(
name='Performance management')
cls = mon_base.get_class('pm_event')
cls = mon_base.get_class(
CONF.prometheus_plugin.performance_management_package,
CONF.prometheus_plugin.performance_management_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
@ -37,7 +39,9 @@ class FmAlertController(prom_wsgi.PrometheusPluginAPIController):
if not CONF.prometheus_plugin.fault_management:
raise sol_ex.PrometheusPluginNotEnabled(
name='Fault management')
cls = mon_base.get_class('alert')
cls = mon_base.get_class(
CONF.prometheus_plugin.fault_management_package,
CONF.prometheus_plugin.fault_management_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)
@ -48,7 +52,9 @@ class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
if not CONF.prometheus_plugin.auto_scaling:
raise sol_ex.PrometheusPluginNotEnabled(
name='Auto scaling')
cls = mon_base.get_class('auto_healing')
cls = mon_base.get_class(
CONF.prometheus_plugin.auto_scaling_package,
CONF.prometheus_plugin.auto_scaling_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, body=body)
return prom_wsgi.PrometheusPluginResponse(204, None)

View File

@ -25,7 +25,9 @@ class ServerNotificationController(sn_wsgi.ServerNotificationAPIController):
def notify(self, request, vnf_instance_id, server_id, body):
if not CONF.server_notification.server_notification:
raise sol_ex.ServerNotificationNotEnabled()
cls = mon_base.get_class('server_notification')
cls = mon_base.get_class(
CONF.server_notification.server_notification_package,
CONF.server_notification.server_notification_class)
mon_base.MonitoringPlugin.get_instance(cls).alert(
request=request, vnf_instance_id=vnf_instance_id,
server_id=server_id, body=body)

View File

@ -116,7 +116,9 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
self.nfvo_client = nfvo_client.NfvoClient()
self.endpoint = CONF.v2_vnfm.endpoint
self._pm_job_view = vnfpm_view.PmJobViewBuilder(self.endpoint)
cls = plugin.get_class('pm_event')
cls = plugin.get_class(
CONF.prometheus_plugin.performance_management_package,
CONF.prometheus_plugin.performance_management_class)
self.plugin = plugin.MonitoringPlugin.get_instance(cls)
@validator.schema(schema.CreatePmJobRequest_V210, '2.1.0')

View File

@ -147,8 +147,6 @@ class ServerNotificationTest(test_vnflcm_basic_common.CommonVnfLcmTest):
# Test notification
self.assert_notification_get(callback_url)
# check usageState of VNF Package
self._check_package_usage(is_nfvo, self.svn_pkg)
# 1. LCM-Create
# ETSI NFV SOL003 v3.3.1 5.5.2.2 VnfInstance

View File

@ -463,7 +463,7 @@ class TestPrometheusPluginPm(base.TestCase):
group='prometheus_plugin', performance_management=True)
pp = mon_base.MonitoringPlugin.get_instance(
prometheus_plugin.PrometheusPluginPm)
# noromal
# normal
job = objects.PmJobV2.from_dict(_pm_job)
pp.delete_job(context=self.context, pm_job=job)
# error

View File

@ -124,7 +124,7 @@ class TestPrometheusPlugin(db_base.SqlTestCase):
self.conductor = conductor_v2.ConductorV2()
@mock.patch.object(http_client.HttpClient, 'do_request')
def test_requst_scale(self, mock_do_request):
def test_request_scale(self, mock_do_request):
resp = webob.Response()
resp.status_code = 202
mock_do_request.return_value = resp, {}

View File

@ -145,7 +145,7 @@ _body_scale_alert1 = {
'fingerprint': '5ef77f1f8a3ecb8d'
}
# fuction_type mismatch
# function_type mismatch
_body_scale_alert2 = copy.deepcopy(_body_scale_alert1)
_body_scale_alert2['labels']['function_type'] = 'vnffm'

View File

@ -18,6 +18,7 @@ from unittest import mock
from tacker import context
from tacker.sol_refactored.common import exceptions as sol_ex
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
from tacker.sol_refactored.common import server_notification as sn_common
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
from tacker.sol_refactored.controller import server_notification
@ -77,6 +78,31 @@ _body2 = {
'error_schema': {}
}
pkg = 'tacker.tests.unit.sol_refactored.controller.test_server_notification'
class VendorSpecificMonitoringPlugin(mon_base.MonitoringPlugin):
_instance = None
@staticmethod
def instance():
if not VendorSpecificMonitoringPlugin._instance:
VendorSpecificMonitoringPlugin()
return VendorSpecificMonitoringPlugin._instance
def __init__(self):
if VendorSpecificMonitoringPlugin._instance:
raise SystemError(
"Not constructor but instance() should be used.")
VendorSpecificMonitoringPlugin._instance = self
def alert(self, **kwargs):
pass
class NotASubClassOfMonitoringPlugin():
pass
class TestServerNotification(base.TestCase):
def setUp(self):
@ -105,8 +131,7 @@ class TestServerNotification(base.TestCase):
server_id='test_server_id', body=_body)
@mock.patch.object(inst_utils, 'get_inst')
def test_notify(self,
mock_inst):
def test_notify(self, mock_inst):
self.config_fixture.config(
group='server_notification', server_notification=True)
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
@ -168,3 +193,30 @@ class TestServerNotification(base.TestCase):
self.controller.notify, request=self.request,
vnf_instance_id='test_id',
server_id='test_server_id', body=_body)
def test_vendor_specific_plugin(self):
self.config_fixture.config(
group='server_notification', server_notification=True)
self.config_fixture.config(
group='server_notification', server_notification_package=pkg)
self.config_fixture.config(
group='server_notification',
server_notification_class='VendorSpecificMonitoringPlugin')
response = self.controller.notify(
request=self.request,
vnf_instance_id='test_id',
server_id='test_server_id', body=_body)
self.assertEqual(204, response.status)
def test_vendor_specific_plugin_subclass(self):
self.config_fixture.config(
group='server_notification', server_notification=True)
self.config_fixture.config(
group='server_notification', server_notification_package=pkg)
self.config_fixture.config(
group='server_notification',
server_notification_class='NotASubClassOfMonitoringPlugin')
self.assertRaises(
sol_ex.MonitoringPluginClassError, self.controller.notify,
request=self.request, vnf_instance_id='test_id',
server_id='test_server_id', body=_body)