Add support VNFM auto heal and scale
Added two new interfaces to Prometheus Plugin. Tacker as a VNFM can support AutoHeal and AutoScale operations for VNFs and CNFs through External Monitoring Tools, without NFVO. Implements: blueprint support-auto-lcm Change-Id: Ib0b5fd9264d80b9666ce69190e0ee41bbde23fac
This commit is contained in:
parent
d457da69d9
commit
96d4ac6353
@ -299,6 +299,9 @@
|
|||||||
$TACKER_CONF:
|
$TACKER_CONF:
|
||||||
server_notification:
|
server_notification:
|
||||||
server_notification: true
|
server_notification: true
|
||||||
|
prometheus_plugin:
|
||||||
|
auto_scaling: true
|
||||||
|
auto_healing: true
|
||||||
tox_envlist: dsvm-functional-sol-v2
|
tox_envlist: dsvm-functional-sol-v2
|
||||||
|
|
||||||
- job:
|
- job:
|
||||||
@ -608,6 +611,7 @@
|
|||||||
fault_management: true
|
fault_management: true
|
||||||
performance_management: true
|
performance_management: true
|
||||||
auto_scaling: true
|
auto_scaling: true
|
||||||
|
auto_healing: true
|
||||||
test_rule_with_promtool: true
|
test_rule_with_promtool: true
|
||||||
tox_envlist: dsvm-functional-sol-kubernetes-v2
|
tox_envlist: dsvm-functional-sol-kubernetes-v2
|
||||||
vars:
|
vars:
|
||||||
|
@ -8,7 +8,8 @@ use = egg:Paste#urlmap
|
|||||||
/vnflcm/v2: vnflcm_v2
|
/vnflcm/v2: vnflcm_v2
|
||||||
/vnffm/v1: vnffm_v1
|
/vnffm/v1: vnffm_v1
|
||||||
/vnfpm/v2: vnfpm_v2
|
/vnfpm/v2: vnfpm_v2
|
||||||
/alert/vnf_instances: prometheus_auto_scaling
|
/alert/auto_scaling: prometheus_auto_scaling
|
||||||
|
/alert/auto_healing: prometheus_auto_healing
|
||||||
/alert: prometheus_fm
|
/alert: prometheus_fm
|
||||||
/pm_event: prometheus_pm
|
/pm_event: prometheus_pm
|
||||||
/server_notification: server_notification
|
/server_notification: server_notification
|
||||||
@ -93,6 +94,9 @@ paste.app_factory = tacker.sol_refactored.api.router:VnffmAPIRouterV1.factory
|
|||||||
[app:prometheus_auto_scaling]
|
[app:prometheus_auto_scaling]
|
||||||
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:AutoScalingRouter.factory
|
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:AutoScalingRouter.factory
|
||||||
|
|
||||||
|
[app:prometheus_auto_healing]
|
||||||
|
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:AutoHealingRouter.factory
|
||||||
|
|
||||||
[app:prometheus_fm]
|
[app:prometheus_fm]
|
||||||
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:FmAlertRouter.factory
|
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:FmAlertRouter.factory
|
||||||
|
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
In Prometheus Plugin, two new interfaces are added to implement AutoHeal
|
||||||
|
and AutoScale for VNF and CNF.
|
||||||
|
As a VNFM, Tacker can decide whether to perform heal and scale operations
|
||||||
|
through the received alarms sent by External Monitoring Tools, without
|
||||||
|
NFVO.
|
||||||
|
Added new user guide to help users understand the function.
|
@ -27,7 +27,8 @@ REPORT_GET = '/vnfpm/v2/pm_jobs/{id}/reports/{report_id}'
|
|||||||
|
|
||||||
POLICY_NAME_PROM_PLUGIN = 'tacker_PROM_PLUGIN_api:PROM_PLUGIN:{}'
|
POLICY_NAME_PROM_PLUGIN = 'tacker_PROM_PLUGIN_api:PROM_PLUGIN:{}'
|
||||||
PROM_PLUGIN_PM_PATH = '/pm_event'
|
PROM_PLUGIN_PM_PATH = '/pm_event'
|
||||||
PROM_PLUGIN_AUTO_SCALING_PATH = '/alert/vnf_instances'
|
PROM_PLUGIN_AUTO_HEALING_PATH = '/alert/auto_healing'
|
||||||
|
PROM_PLUGIN_AUTO_SCALING_PATH = '/alert/auto_scaling'
|
||||||
|
|
||||||
rules = [
|
rules = [
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
@ -107,6 +108,15 @@ rules = [
|
|||||||
'path': PROM_PLUGIN_PM_PATH}
|
'path': PROM_PLUGIN_PM_PATH}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name=POLICY_NAME_PROM_PLUGIN.format('auto_healing'),
|
||||||
|
check_str=RULE_ANY,
|
||||||
|
description="auto_healing",
|
||||||
|
operations=[
|
||||||
|
{'method': 'POST',
|
||||||
|
'path': PROM_PLUGIN_AUTO_HEALING_PATH}
|
||||||
|
]
|
||||||
|
),
|
||||||
policy.DocumentedRuleDefault(
|
policy.DocumentedRuleDefault(
|
||||||
name=POLICY_NAME_PROM_PLUGIN.format('auto_scaling'),
|
name=POLICY_NAME_PROM_PLUGIN.format('auto_scaling'),
|
||||||
check_str=RULE_ANY,
|
check_str=RULE_ANY,
|
||||||
@ -115,15 +125,6 @@ rules = [
|
|||||||
{'method': 'POST',
|
{'method': 'POST',
|
||||||
'path': PROM_PLUGIN_AUTO_SCALING_PATH}
|
'path': PROM_PLUGIN_AUTO_SCALING_PATH}
|
||||||
]
|
]
|
||||||
),
|
|
||||||
policy.DocumentedRuleDefault(
|
|
||||||
name=POLICY_NAME_PROM_PLUGIN.format('auto_scaling_id'),
|
|
||||||
check_str=RULE_ANY,
|
|
||||||
description="auto_scaling_id",
|
|
||||||
operations=[
|
|
||||||
{'method': 'POST',
|
|
||||||
'path': PROM_PLUGIN_AUTO_SCALING_PATH + '/{vnfInstanceId}'}
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -33,11 +33,15 @@ class FmAlertRouter(prom_wsgi.PrometheusPluginAPIRouter):
|
|||||||
route_list = [("", {"POST": "alert"})]
|
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):
|
class AutoScalingRouter(prom_wsgi.PrometheusPluginAPIRouter):
|
||||||
controller = prom_wsgi.PrometheusPluginResource(
|
controller = prom_wsgi.PrometheusPluginResource(
|
||||||
prometheus_plugin_controller.AutoScalingController(),
|
prometheus_plugin_controller.AutoScalingController(),
|
||||||
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
|
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
|
||||||
route_list = [
|
route_list = [("", {"POST": "auto_scaling"})]
|
||||||
("", {"POST": "auto_scaling"}),
|
|
||||||
("/{id}", {"POST": "auto_scaling"})
|
|
||||||
]
|
|
||||||
|
@ -34,11 +34,12 @@ Alert = {
|
|||||||
},
|
},
|
||||||
'function_type': {
|
'function_type': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'enum': ['vnffm', 'vnfpm', 'auto_scale']
|
'enum': ['vnffm', 'vnfpm', 'auto_scale', 'auto_heal']
|
||||||
},
|
},
|
||||||
'job_id': {'type': 'string'},
|
'job_id': {'type': 'string'},
|
||||||
'object_instance_id': {'type': 'string'},
|
'object_instance_id': {'type': 'string'},
|
||||||
'vnf_instance_id': {'type': 'string'},
|
'vnf_instance_id': {'type': 'string'},
|
||||||
|
'vnfc_info_id': {'type': 'string'},
|
||||||
'node': {'type': 'string'},
|
'node': {'type': 'string'},
|
||||||
'perceived_severity': {
|
'perceived_severity': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
|
@ -162,6 +162,9 @@ PROMETHEUS_PLUGIN_OPTS = [
|
|||||||
cfg.BoolOpt('fault_management',
|
cfg.BoolOpt('fault_management',
|
||||||
default=False,
|
default=False,
|
||||||
help=_('Enable prometheus plugin fault management')),
|
help=_('Enable prometheus plugin fault management')),
|
||||||
|
cfg.BoolOpt('auto_healing',
|
||||||
|
default=False,
|
||||||
|
help=_('Enable prometheus plugin autohealing')),
|
||||||
cfg.BoolOpt('auto_scaling',
|
cfg.BoolOpt('auto_scaling',
|
||||||
default=False,
|
default=False,
|
||||||
help=_('Enable prometheus plugin autoscaling')),
|
help=_('Enable prometheus plugin autoscaling')),
|
||||||
@ -189,6 +192,22 @@ PROMETHEUS_PLUGIN_OPTS = [
|
|||||||
'This configuration is changed in case of replacing '
|
'This configuration is changed in case of replacing '
|
||||||
'the original function with a vendor specific '
|
'the original function with a vendor specific '
|
||||||
'function.')),
|
'function.')),
|
||||||
|
cfg.StrOpt('auto_healing_package',
|
||||||
|
default='tacker.sol_refactored.common.prometheus_plugin',
|
||||||
|
help=_('Package name for auto healing. '
|
||||||
|
'This configuration is changed in case of replacing '
|
||||||
|
'the original function with a vendor specific '
|
||||||
|
'function.')),
|
||||||
|
cfg.StrOpt('auto_healing_class',
|
||||||
|
default='PrometheusPluginAutoHealing',
|
||||||
|
help=_('Class name for auto healing. '
|
||||||
|
'This configuration is changed in case of replacing '
|
||||||
|
'the original function with a vendor specific '
|
||||||
|
'function.')),
|
||||||
|
cfg.IntOpt('timer_interval',
|
||||||
|
default=20,
|
||||||
|
help=_('Timeout (second) of packing for multiple '
|
||||||
|
'auto healing.')),
|
||||||
cfg.StrOpt('auto_scaling_package',
|
cfg.StrOpt('auto_scaling_package',
|
||||||
default='tacker.sol_refactored.common.prometheus_plugin',
|
default='tacker.sol_refactored.common.prometheus_plugin',
|
||||||
help=_('Package name for auto scaling. '
|
help=_('Package name for auto scaling. '
|
||||||
|
@ -145,7 +145,7 @@ class PrometheusPluginPm(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
self._alert(kwargs['request'], body=kwargs['body'])
|
self._alert(kwargs['request'], body=kwargs['body'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# All exceptions is ignored here and 204 response will always
|
# All exceptions is ignored here and 204 response will always
|
||||||
# be returned. Because when tacker responds error to alertmanager,
|
# be returned because when tacker responds error to alertmanager,
|
||||||
# alertmanager may repeat the same reports.
|
# alertmanager may repeat the same reports.
|
||||||
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
||||||
|
|
||||||
@ -237,16 +237,16 @@ class PrometheusPluginPm(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
result = []
|
result = []
|
||||||
context = request.context
|
context = request.context
|
||||||
datetime_now = datetime.datetime.now(datetime.timezone.utc)
|
datetime_now = datetime.datetime.now(datetime.timezone.utc)
|
||||||
for alt in body['alerts']:
|
for alert in body['alerts']:
|
||||||
if alt['labels']['function_type'] != 'vnfpm':
|
if alert['labels']['function_type'] != 'vnfpm':
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
pm_job_id = alt['labels']['job_id']
|
pm_job_id = alert['labels']['job_id']
|
||||||
object_instance_id = alt['labels']['object_instance_id']
|
object_instance_id = alert['labels']['object_instance_id']
|
||||||
metric = alt['labels']['metric']
|
metric = alert['labels']['metric']
|
||||||
sub_object_instance_id = alt['labels'].get(
|
sub_object_instance_id = alert['labels'].get(
|
||||||
'sub_object_instance_id')
|
'sub_object_instance_id')
|
||||||
value = alt['annotations']['value']
|
value = alert['annotations']['value']
|
||||||
|
|
||||||
pm_job = pm_job_utils.get_pm_job(context, pm_job_id)
|
pm_job = pm_job_utils.get_pm_job(context, pm_job_id)
|
||||||
self.filter_alert_by_time(context, pm_job, datetime_now,
|
self.filter_alert_by_time(context, pm_job, datetime_now,
|
||||||
@ -700,7 +700,7 @@ class PrometheusPluginFm(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
self._alert(kwargs['request'], body=kwargs['body'])
|
self._alert(kwargs['request'], body=kwargs['body'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# All exceptions is ignored here and 204 response will always
|
# All exceptions is ignored here and 204 response will always
|
||||||
# be returned. Because when tacker responds error to alertmanager,
|
# be returned because when tacker responds error to alertmanager,
|
||||||
# alertmanager may repeat the same reports.
|
# alertmanager may repeat the same reports.
|
||||||
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
||||||
|
|
||||||
@ -831,18 +831,91 @@ class PrometheusPluginFm(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
def _alert(self, request, body):
|
def _alert(self, request, body):
|
||||||
now = datetime.datetime.now(datetime.timezone.utc)
|
now = datetime.datetime.now(datetime.timezone.utc)
|
||||||
result = []
|
result = []
|
||||||
for alt in body['alerts']:
|
for alert in body['alerts']:
|
||||||
if alt['labels']['function_type'] != 'vnffm':
|
if alert['labels']['function_type'] != 'vnffm':
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
alarms = self.create_or_update_alarm(
|
alarms = self.create_or_update_alarm(
|
||||||
request.context, alt, now)
|
request.context, alert, now)
|
||||||
result.extend(alarms)
|
result.extend(alarms)
|
||||||
except sol_ex.PrometheusPluginSkipped:
|
except sol_ex.PrometheusPluginSkipped:
|
||||||
pass
|
pass
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class PrometheusPluginAutoHealing(PrometheusPlugin, mon_base.MonitoringPlugin):
|
||||||
|
_instance = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def instance():
|
||||||
|
if PrometheusPluginAutoHealing._instance is None:
|
||||||
|
if not CONF.prometheus_plugin.auto_healing:
|
||||||
|
stub = mon_base.MonitoringPluginStub.instance()
|
||||||
|
PrometheusPluginAutoHealing._instance = stub
|
||||||
|
else:
|
||||||
|
PrometheusPluginAutoHealing()
|
||||||
|
return PrometheusPluginAutoHealing._instance
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if PrometheusPluginAutoHealing._instance:
|
||||||
|
raise SystemError(
|
||||||
|
"Not constructor but instance() should be used.")
|
||||||
|
super(PrometheusPluginAutoHealing, self).__init__()
|
||||||
|
self.set_callback(self.default_callback)
|
||||||
|
PrometheusPluginAutoHealing._instance = self
|
||||||
|
|
||||||
|
def set_callback(self, notification_callback):
|
||||||
|
self.notification_callback = notification_callback
|
||||||
|
|
||||||
|
def alert(self, **kwargs):
|
||||||
|
try:
|
||||||
|
self._alert(kwargs['request'], body=kwargs['body'])
|
||||||
|
except Exception as e:
|
||||||
|
# All exceptions is ignored here and 204 response will always
|
||||||
|
# be returned because when tacker responds error to alertmanager,
|
||||||
|
# alertmanager may repeat the same reports.
|
||||||
|
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
||||||
|
|
||||||
|
def default_callback(self, context, vnf_instance_id, vnfc_info_id):
|
||||||
|
self.rpc.enqueue_auto_heal_instance(
|
||||||
|
context, vnf_instance_id, vnfc_info_id)
|
||||||
|
|
||||||
|
@validator.schema(prometheus_plugin_schemas.AlertMessage)
|
||||||
|
def _alert(self, request, body):
|
||||||
|
context = request.context
|
||||||
|
alerts = (alert for alert in body['alerts'] if
|
||||||
|
alert['status'] == 'firing' and
|
||||||
|
alert['labels']['receiver_type'] == 'tacker' and
|
||||||
|
alert['labels']['function_type'] == 'auto_heal')
|
||||||
|
|
||||||
|
for alert in alerts:
|
||||||
|
vnf_instance_id = alert['labels']['vnf_instance_id']
|
||||||
|
try:
|
||||||
|
inst = inst_utils.get_inst(context, vnf_instance_id)
|
||||||
|
except sol_ex.VnfInstanceNotFound:
|
||||||
|
continue
|
||||||
|
if inst.instantiationState != 'INSTANTIATED':
|
||||||
|
self.rpc.dequeue_auto_heal_instance(
|
||||||
|
None, vnf_instance_id)
|
||||||
|
|
||||||
|
if (not inst.obj_attr_is_set('vnfConfigurableProperties')
|
||||||
|
or not inst.vnfConfigurableProperties.get(
|
||||||
|
'isAutohealEnabled')):
|
||||||
|
continue
|
||||||
|
|
||||||
|
vnfc_info_id = alert['labels']['vnfc_info_id']
|
||||||
|
result = {
|
||||||
|
vnfcInfo for vnfcInfo in inst.instantiatedVnfInfo.vnfcInfo
|
||||||
|
if vnfcInfo.id == vnfc_info_id
|
||||||
|
}
|
||||||
|
if not result:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if self.notification_callback:
|
||||||
|
self.notification_callback(
|
||||||
|
context, vnf_instance_id, vnfc_info_id)
|
||||||
|
|
||||||
|
|
||||||
class PrometheusPluginAutoScaling(PrometheusPlugin, mon_base.MonitoringPlugin):
|
class PrometheusPluginAutoScaling(PrometheusPlugin, mon_base.MonitoringPlugin):
|
||||||
_instance = None
|
_instance = None
|
||||||
|
|
||||||
@ -861,7 +934,7 @@ class PrometheusPluginAutoScaling(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
raise SystemError(
|
raise SystemError(
|
||||||
"Not constructor but instance() should be used.")
|
"Not constructor but instance() should be used.")
|
||||||
super(PrometheusPluginAutoScaling, self).__init__()
|
super(PrometheusPluginAutoScaling, self).__init__()
|
||||||
self.notification_callback = self.default_callback
|
self.set_callback(self.default_callback)
|
||||||
PrometheusPluginAutoScaling._instance = self
|
PrometheusPluginAutoScaling._instance = self
|
||||||
|
|
||||||
def set_callback(self, notification_callback):
|
def set_callback(self, notification_callback):
|
||||||
@ -872,46 +945,46 @@ class PrometheusPluginAutoScaling(PrometheusPlugin, mon_base.MonitoringPlugin):
|
|||||||
self._alert(kwargs['request'], body=kwargs['body'])
|
self._alert(kwargs['request'], body=kwargs['body'])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
# All exceptions is ignored here and 204 response will always
|
# All exceptions is ignored here and 204 response will always
|
||||||
# be returned. Because when tacker responds error to alertmanager,
|
# be returned because when tacker responds error to alertmanager,
|
||||||
# alertmanager may repeat the same reports.
|
# alertmanager may repeat the same reports.
|
||||||
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
||||||
|
|
||||||
def default_callback(self, context, vnf_instance_id, scaling_param):
|
def default_callback(self, context, vnf_instance_id, scaling_param):
|
||||||
self.rpc.request_scale(context, vnf_instance_id, scaling_param)
|
self.rpc.trigger_scale(context, vnf_instance_id, scaling_param)
|
||||||
|
|
||||||
def skip_if_auto_scale_not_enabled(self, vnf_instance):
|
|
||||||
if (not vnf_instance.obj_attr_is_set('vnfConfigurableProperties') or
|
|
||||||
not vnf_instance.vnfConfigurableProperties.get(
|
|
||||||
'isAutoscaleEnabled')):
|
|
||||||
raise sol_ex.PrometheusPluginSkipped()
|
|
||||||
|
|
||||||
def process_auto_scale(self, request, vnf_instance_id, auto_scale_type,
|
|
||||||
aspect_id):
|
|
||||||
scaling_param = {
|
|
||||||
'type': auto_scale_type,
|
|
||||||
'aspectId': aspect_id,
|
|
||||||
}
|
|
||||||
context = request.context
|
|
||||||
if self.notification_callback:
|
|
||||||
self.notification_callback(context, vnf_instance_id, scaling_param)
|
|
||||||
|
|
||||||
@validator.schema(prometheus_plugin_schemas.AlertMessage)
|
@validator.schema(prometheus_plugin_schemas.AlertMessage)
|
||||||
def _alert(self, request, body):
|
def _alert(self, request, body):
|
||||||
result = []
|
|
||||||
for alt in body['alerts']:
|
|
||||||
if alt['labels']['function_type'] != 'auto_scale':
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
vnf_instance_id = alt['labels']['vnf_instance_id']
|
|
||||||
auto_scale_type = alt['labels']['auto_scale_type']
|
|
||||||
aspect_id = alt['labels']['aspect_id']
|
|
||||||
context = request.context
|
context = request.context
|
||||||
|
alerts = (alert for alert in body['alerts'] if
|
||||||
|
alert['status'] == 'firing' and
|
||||||
|
alert['labels']['receiver_type'] == 'tacker' and
|
||||||
|
alert['labels']['function_type'] == 'auto_scale')
|
||||||
|
|
||||||
|
for alert in alerts:
|
||||||
|
vnf_instance_id = alert['labels']['vnf_instance_id']
|
||||||
|
try:
|
||||||
inst = inst_utils.get_inst(context, vnf_instance_id)
|
inst = inst_utils.get_inst(context, vnf_instance_id)
|
||||||
self.skip_if_auto_scale_not_enabled(inst)
|
except sol_ex.VnfInstanceNotFound:
|
||||||
self.process_auto_scale(
|
continue
|
||||||
request, vnf_instance_id, auto_scale_type, aspect_id)
|
if (inst.instantiationState != 'INSTANTIATED' or
|
||||||
result.append((vnf_instance_id, auto_scale_type, aspect_id))
|
not inst.obj_attr_is_set('vnfConfigurableProperties') or
|
||||||
except sol_ex.PrometheusPluginSkipped:
|
not inst.vnfConfigurableProperties.get(
|
||||||
pass
|
'isAutoscaleEnabled')):
|
||||||
return result
|
continue
|
||||||
|
|
||||||
|
aspect_id = alert['labels']['aspect_id']
|
||||||
|
result = {
|
||||||
|
scaleStatus for scaleStatus in
|
||||||
|
inst.instantiatedVnfInfo.scaleStatus
|
||||||
|
if scaleStatus.aspectId == aspect_id
|
||||||
|
}
|
||||||
|
if not result:
|
||||||
|
continue
|
||||||
|
|
||||||
|
scaling_param = {
|
||||||
|
'type': alert['labels']['auto_scale_type'],
|
||||||
|
'aspectId': aspect_id,
|
||||||
|
}
|
||||||
|
if self.notification_callback:
|
||||||
|
self.notification_callback(
|
||||||
|
context, vnf_instance_id, scaling_param)
|
||||||
|
142
tacker/sol_refactored/common/vnflcm_utils.py
Normal file
142
tacker/sol_refactored/common/vnflcm_utils.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# Copyright (C) 2023 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 datetime import datetime
|
||||||
|
|
||||||
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
|
from tacker.sol_refactored.common import coordinate
|
||||||
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
|
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
|
||||||
|
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||||
|
from tacker.sol_refactored.conductor import conductor_rpc_v2
|
||||||
|
from tacker.sol_refactored import objects
|
||||||
|
from tacker.sol_refactored.objects.v2 import fields as v2fields
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(fengyi): The code of this function is all copied from the
|
||||||
|
# controller, which is not friendly to future development and
|
||||||
|
# maintenance, and may be refactored in the future. After
|
||||||
|
# refactoring, only the validation of the req body is left,
|
||||||
|
# and the creation of lcmocc and the call to start_lcm_op are
|
||||||
|
# all executed by the controller, notification driver, etc.
|
||||||
|
@coordinate.lock_vnf_instance('{vnf_instance_id}')
|
||||||
|
def heal(context, vnf_instance_id, body):
|
||||||
|
inst = inst_utils.get_inst(context, vnf_instance_id)
|
||||||
|
|
||||||
|
if inst.instantiationState != 'INSTANTIATED':
|
||||||
|
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)
|
||||||
|
|
||||||
|
lcmocc_utils.check_lcmocc_in_progress(context, vnf_instance_id)
|
||||||
|
|
||||||
|
# check parameter for later use
|
||||||
|
is_all = body.get('additionalParams', {}).get('all', False)
|
||||||
|
if not isinstance(is_all, bool):
|
||||||
|
raise sol_ex.SolValidationError(
|
||||||
|
detail="additionalParams['all'] must be bool.")
|
||||||
|
|
||||||
|
if 'vnfcInstanceId' in body:
|
||||||
|
inst_info = inst.instantiatedVnfInfo
|
||||||
|
vnfc_id = []
|
||||||
|
if inst_info.obj_attr_is_set('vnfcInfo'):
|
||||||
|
vnfc_id = [vnfc.id for vnfc in inst_info.vnfcInfo]
|
||||||
|
for req_vnfc_id in body['vnfcInstanceId']:
|
||||||
|
if req_vnfc_id not in vnfc_id:
|
||||||
|
raise sol_ex.SolValidationError(
|
||||||
|
detail="vnfcInstanceId(%s) does not exist."
|
||||||
|
% req_vnfc_id)
|
||||||
|
|
||||||
|
lcmocc = new_lcmocc(vnf_instance_id, v2fields.LcmOperationType.HEAL, body)
|
||||||
|
lcmocc.create(context)
|
||||||
|
|
||||||
|
rpc = conductor_rpc_v2.VnfLcmRpcApiV2()
|
||||||
|
rpc.start_lcm_op(context, lcmocc.id)
|
||||||
|
return lcmocc
|
||||||
|
|
||||||
|
|
||||||
|
# TODO(fengyi): The code of this function is all copied from the
|
||||||
|
# controller, which is not friendly to future development and
|
||||||
|
# maintenance, and may be refactored in the future. After
|
||||||
|
# refactoring, only the validation of the req body is left,
|
||||||
|
# and the creation of lcmocc and the call to start_lcm_op are
|
||||||
|
# all executed by the controller, notification driver, etc.
|
||||||
|
@coordinate.lock_vnf_instance('{vnf_instance_id}')
|
||||||
|
def scale(context, vnf_instance_id, body):
|
||||||
|
inst = inst_utils.get_inst(context, vnf_instance_id)
|
||||||
|
|
||||||
|
if inst.instantiationState != 'INSTANTIATED':
|
||||||
|
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=vnf_instance_id)
|
||||||
|
|
||||||
|
lcmocc_utils.check_lcmocc_in_progress(context, vnf_instance_id)
|
||||||
|
|
||||||
|
# check parameters
|
||||||
|
aspect_id = body['aspectId']
|
||||||
|
if 'numberOfSteps' not in body:
|
||||||
|
# set default value (1) defined by SOL specification for
|
||||||
|
# the convenience of the following methods.
|
||||||
|
body['numberOfSteps'] = 1
|
||||||
|
|
||||||
|
scale_level = _get_current_scale_level(inst, aspect_id)
|
||||||
|
max_scale_level = _get_max_scale_level(inst, aspect_id)
|
||||||
|
if scale_level is None or max_scale_level is None:
|
||||||
|
raise sol_ex.InvalidScaleAspectId(aspect_id=aspect_id)
|
||||||
|
|
||||||
|
num_steps = body['numberOfSteps']
|
||||||
|
if body['type'] == 'SCALE_IN':
|
||||||
|
num_steps *= -1
|
||||||
|
scale_level += num_steps
|
||||||
|
if scale_level < 0 or scale_level > max_scale_level:
|
||||||
|
raise sol_ex.InvalidScaleNumberOfSteps(
|
||||||
|
num_steps=body['numberOfSteps'])
|
||||||
|
|
||||||
|
lcmocc = new_lcmocc(vnf_instance_id, v2fields.LcmOperationType.SCALE, body)
|
||||||
|
lcmocc.create(context)
|
||||||
|
|
||||||
|
rpc = conductor_rpc_v2.VnfLcmRpcApiV2()
|
||||||
|
rpc.start_lcm_op(context, lcmocc.id)
|
||||||
|
return lcmocc
|
||||||
|
|
||||||
|
|
||||||
|
def _get_current_scale_level(inst, aspect_id):
|
||||||
|
if (inst.obj_attr_is_set('instantiatedVnfInfo') and
|
||||||
|
inst.instantiatedVnfInfo.obj_attr_is_set('scaleStatus')):
|
||||||
|
for scale_info in inst.instantiatedVnfInfo.scaleStatus:
|
||||||
|
if scale_info.aspectId == aspect_id:
|
||||||
|
return scale_info.scaleLevel
|
||||||
|
|
||||||
|
|
||||||
|
def _get_max_scale_level(inst, aspect_id):
|
||||||
|
if (inst.obj_attr_is_set('instantiatedVnfInfo') and
|
||||||
|
inst.instantiatedVnfInfo.obj_attr_is_set('maxScaleLevels')):
|
||||||
|
for scale_info in inst.instantiatedVnfInfo.maxScaleLevels:
|
||||||
|
if scale_info.aspectId == aspect_id:
|
||||||
|
return scale_info.scaleLevel
|
||||||
|
|
||||||
|
|
||||||
|
def new_lcmocc(inst_id, operation, req_body,
|
||||||
|
op_state=v2fields.LcmOperationStateType.STARTING):
|
||||||
|
now = datetime.utcnow()
|
||||||
|
lcmocc = objects.VnfLcmOpOccV2(
|
||||||
|
id=uuidutils.generate_uuid(),
|
||||||
|
operationState=op_state,
|
||||||
|
stateEnteredTime=now,
|
||||||
|
startTime=now,
|
||||||
|
vnfInstanceId=inst_id,
|
||||||
|
operation=operation,
|
||||||
|
isAutomaticInvocation=False,
|
||||||
|
isCancelPending=False,
|
||||||
|
operationParams=req_body)
|
||||||
|
|
||||||
|
return lcmocc
|
@ -95,5 +95,15 @@ class PrometheusPluginConductor(object):
|
|||||||
def store_job_info(self, context, report):
|
def store_job_info(self, context, report):
|
||||||
self.cast(context, 'store_job_info', report=report)
|
self.cast(context, 'store_job_info', report=report)
|
||||||
|
|
||||||
def request_scale(self, context, id, scale_req):
|
def trigger_scale(self, context, id, scale_req):
|
||||||
self.cast(context, 'request_scale', id=id, scale_req=scale_req)
|
self.cast(context, 'trigger_scale', id=id, scale_req=scale_req)
|
||||||
|
|
||||||
|
def enqueue_auto_heal_instance(
|
||||||
|
self, context, vnf_instance_id, vnfc_info_id):
|
||||||
|
self.cast(context, 'enqueue_auto_heal_instance',
|
||||||
|
vnf_instance_id=vnf_instance_id,
|
||||||
|
vnfc_info_id=vnfc_info_id)
|
||||||
|
|
||||||
|
def dequeue_auto_heal_instance(self, context, vnf_instance_id):
|
||||||
|
self.cast(context, 'dequeue_auto_heal_instance',
|
||||||
|
vnf_instance_id=vnf_instance_id)
|
||||||
|
@ -394,8 +394,17 @@ class ConductorV2(object):
|
|||||||
self.vnfpm_driver.store_job_info(context, report)
|
self.vnfpm_driver.store_job_info(context, report)
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def request_scale(self, context, id, scale_req):
|
def trigger_scale(self, context, id, scale_req):
|
||||||
self.prom_driver.request_scale(context, id, scale_req)
|
self.prom_driver.trigger_scale(context, id, scale_req)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def enqueue_auto_heal_instance(
|
||||||
|
self, context, vnf_instance_id, vnfc_info_id):
|
||||||
|
self.prom_driver.enqueue_heal(context, vnf_instance_id, vnfc_info_id)
|
||||||
|
|
||||||
|
@log.log
|
||||||
|
def dequeue_auto_heal_instance(self, context, vnf_instance_id):
|
||||||
|
self.prom_driver.dequeue_heal(vnf_instance_id)
|
||||||
|
|
||||||
@log.log
|
@log.log
|
||||||
def server_notification_notify(
|
def server_notification_notify(
|
||||||
|
@ -13,17 +13,66 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import threading
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from tacker.sol_refactored.common import config as cfg
|
from tacker.sol_refactored.common import config as cfg
|
||||||
from tacker.sol_refactored.common import http_client
|
from tacker.sol_refactored.common import vnflcm_utils
|
||||||
|
from tacker.sol_refactored import objects
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
|
||||||
|
class VnfmAutoHealTimer():
|
||||||
|
def __init__(self, context, vnf_instance_id,
|
||||||
|
expiration_time, expiration_handler):
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.expired = False
|
||||||
|
self.queue = set()
|
||||||
|
self.context = context
|
||||||
|
self.vnf_instance_id = vnf_instance_id
|
||||||
|
self.expiration_handler = expiration_handler
|
||||||
|
self.timer = threading.Timer(expiration_time, self.expire)
|
||||||
|
self.timer.start()
|
||||||
|
|
||||||
|
def expire(self):
|
||||||
|
_expired = False
|
||||||
|
with self.lock:
|
||||||
|
if not self.expired:
|
||||||
|
self._cancel()
|
||||||
|
_expired = True
|
||||||
|
if _expired:
|
||||||
|
self.expiration_handler(
|
||||||
|
self.context, self.vnf_instance_id, list(self.queue))
|
||||||
|
|
||||||
|
def add_vnfc_info_id(self, vnfc_info_id):
|
||||||
|
with self.lock:
|
||||||
|
if not self.expired:
|
||||||
|
self.queue.add(vnfc_info_id)
|
||||||
|
|
||||||
|
def _cancel(self):
|
||||||
|
self.timer.cancel()
|
||||||
|
self.expired = True
|
||||||
|
|
||||||
|
def cancel(self):
|
||||||
|
with self.lock:
|
||||||
|
if not self.expired:
|
||||||
|
self._cancel()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.cancel()
|
||||||
|
|
||||||
|
|
||||||
class PrometheusPluginDriverStub():
|
class PrometheusPluginDriverStub():
|
||||||
def request_scale(self, context, vnf_instance_id, scale_req):
|
def trigger_scale(self, context, vnf_instance_id, scale_req):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def enqueue_heal(self, context, vnf_instance_id, vnfc_info_id):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def dequeue_heal(self, vnf_instance_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@ -34,6 +83,7 @@ class PrometheusPluginDriver():
|
|||||||
def instance():
|
def instance():
|
||||||
if PrometheusPluginDriver._instance is None:
|
if PrometheusPluginDriver._instance is None:
|
||||||
if (CONF.prometheus_plugin.auto_scaling or
|
if (CONF.prometheus_plugin.auto_scaling or
|
||||||
|
CONF.prometheus_plugin.auto_healing or
|
||||||
CONF.prometheus_plugin.fault_management or
|
CONF.prometheus_plugin.fault_management or
|
||||||
CONF.prometheus_plugin.performance_management):
|
CONF.prometheus_plugin.performance_management):
|
||||||
PrometheusPluginDriver()
|
PrometheusPluginDriver()
|
||||||
@ -45,19 +95,35 @@ class PrometheusPluginDriver():
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
if PrometheusPluginDriver._instance:
|
if PrometheusPluginDriver._instance:
|
||||||
raise SystemError("Not constructor but instance() should be used.")
|
raise SystemError("Not constructor but instance() should be used.")
|
||||||
auth_handle = http_client.KeystonePasswordAuthHandle(
|
|
||||||
auth_url=CONF.keystone_authtoken.auth_url,
|
|
||||||
username=CONF.keystone_authtoken.username,
|
|
||||||
password=CONF.keystone_authtoken.password,
|
|
||||||
project_name=CONF.keystone_authtoken.project_name,
|
|
||||||
user_domain_name=CONF.keystone_authtoken.user_domain_name,
|
|
||||||
project_domain_name=CONF.keystone_authtoken.project_domain_name)
|
|
||||||
self.client = http_client.HttpClient(auth_handle)
|
|
||||||
PrometheusPluginDriver._instance = self
|
PrometheusPluginDriver._instance = self
|
||||||
|
self.timer_map = {}
|
||||||
|
self.expiration_time = CONF.prometheus_plugin.timer_interval
|
||||||
|
|
||||||
def request_scale(self, context, vnf_instance_id, scale_req):
|
def enqueue_heal(self, context, vnf_instance_id, vnfc_info_id):
|
||||||
ep = CONF.v2_vnfm.endpoint
|
if vnf_instance_id not in self.timer_map:
|
||||||
url = f'{ep}/vnflcm/v2/vnf_instances/{vnf_instance_id}/scale'
|
self.timer_map[vnf_instance_id] = VnfmAutoHealTimer(
|
||||||
resp, _ = self.client.do_request(
|
context, vnf_instance_id, self.expiration_time,
|
||||||
url, "POST", context=context, body=scale_req, version="2.0.0")
|
self._timer_expired)
|
||||||
LOG.info("AutoScaling request is processed: %d.", resp.status_code)
|
self.timer_map[vnf_instance_id].add_vnfc_info_id(vnfc_info_id)
|
||||||
|
|
||||||
|
def dequeue_heal(self, vnf_instance_id):
|
||||||
|
if vnf_instance_id in self.timer_map:
|
||||||
|
self.timer_map[vnf_instance_id].cancel()
|
||||||
|
del self.timer_map[vnf_instance_id]
|
||||||
|
|
||||||
|
def _trigger_heal(self, context, vnf_instance_id, vnfc_info_ids):
|
||||||
|
heal_req = objects.HealVnfRequest(vnfcInstanceId=vnfc_info_ids)
|
||||||
|
body = heal_req.to_dict()
|
||||||
|
LOG.info(f"VNFM AutoHealing is triggered. vnf: {vnf_instance_id}, "
|
||||||
|
f"vnfcInstanceId: {vnfc_info_ids}")
|
||||||
|
vnflcm_utils.heal(context, vnf_instance_id, body)
|
||||||
|
|
||||||
|
def _timer_expired(self, context, vnf_instance_id, vnfc_info_ids):
|
||||||
|
self.dequeue_heal(vnf_instance_id)
|
||||||
|
self._trigger_heal(context, vnf_instance_id, vnfc_info_ids)
|
||||||
|
|
||||||
|
def trigger_scale(self, context, vnf_instance_id, scale_req):
|
||||||
|
LOG.info(f"VNFM AutoScaling is triggered. vnf: {vnf_instance_id}, "
|
||||||
|
f"type: {scale_req['type']}, aspectId: "
|
||||||
|
f"{scale_req['aspectId']}")
|
||||||
|
vnflcm_utils.scale(context, vnf_instance_id, scale_req)
|
||||||
|
@ -47,6 +47,19 @@ class FmAlertController(prom_wsgi.PrometheusPluginAPIController):
|
|||||||
return prom_wsgi.PrometheusPluginResponse(204, None)
|
return prom_wsgi.PrometheusPluginResponse(204, None)
|
||||||
|
|
||||||
|
|
||||||
|
class AutoHealingController(prom_wsgi.PrometheusPluginAPIController):
|
||||||
|
def auto_healing(self, request, body):
|
||||||
|
if not CONF.prometheus_plugin.auto_healing:
|
||||||
|
raise sol_ex.PrometheusPluginNotEnabled(
|
||||||
|
name='Auto healing')
|
||||||
|
cls = mon_base.get_class(
|
||||||
|
CONF.prometheus_plugin.auto_healing_package,
|
||||||
|
CONF.prometheus_plugin.auto_healing_class)
|
||||||
|
mon_base.MonitoringPlugin.get_instance(cls).alert(
|
||||||
|
request=request, body=body)
|
||||||
|
return prom_wsgi.PrometheusPluginResponse(204, None)
|
||||||
|
|
||||||
|
|
||||||
class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
|
class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
|
||||||
def auto_scaling(self, request, body):
|
def auto_scaling(self, request, body):
|
||||||
if not CONF.prometheus_plugin.auto_scaling:
|
if not CONF.prometheus_plugin.auto_scaling:
|
||||||
@ -58,6 +71,3 @@ class AutoScalingController(prom_wsgi.PrometheusPluginAPIController):
|
|||||||
mon_base.MonitoringPlugin.get_instance(cls).alert(
|
mon_base.MonitoringPlugin.get_instance(cls).alert(
|
||||||
request=request, body=body)
|
request=request, body=body)
|
||||||
return prom_wsgi.PrometheusPluginResponse(204, None)
|
return prom_wsgi.PrometheusPluginResponse(204, None)
|
||||||
|
|
||||||
def auto_scaling_id(self, request, _, body):
|
|
||||||
return self.auto_scaling(request, body)
|
|
||||||
|
@ -14,8 +14,6 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
|
|
||||||
@ -30,6 +28,7 @@ from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
|
|||||||
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
||||||
from tacker.sol_refactored.common import vim_utils
|
from tacker.sol_refactored.common import vim_utils
|
||||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||||
|
from tacker.sol_refactored.common import vnflcm_utils
|
||||||
from tacker.sol_refactored.conductor import conductor_rpc_v2
|
from tacker.sol_refactored.conductor import conductor_rpc_v2
|
||||||
from tacker.sol_refactored.controller import vnflcm_view
|
from tacker.sol_refactored.controller import vnflcm_view
|
||||||
from tacker.sol_refactored.nfvo import nfvo_client
|
from tacker.sol_refactored.nfvo import nfvo_client
|
||||||
@ -153,22 +152,6 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
self.endpoint)
|
self.endpoint)
|
||||||
return sol_wsgi.SolResponse(204, None)
|
return sol_wsgi.SolResponse(204, None)
|
||||||
|
|
||||||
def _new_lcmocc(self, inst_id, operation, req_body,
|
|
||||||
op_state=v2fields.LcmOperationStateType.STARTING):
|
|
||||||
now = datetime.utcnow()
|
|
||||||
lcmocc = objects.VnfLcmOpOccV2(
|
|
||||||
id=uuidutils.generate_uuid(),
|
|
||||||
operationState=op_state,
|
|
||||||
stateEnteredTime=now,
|
|
||||||
startTime=now,
|
|
||||||
vnfInstanceId=inst_id,
|
|
||||||
operation=operation,
|
|
||||||
isAutomaticInvocation=False,
|
|
||||||
isCancelPending=False,
|
|
||||||
operationParams=req_body)
|
|
||||||
|
|
||||||
return lcmocc
|
|
||||||
|
|
||||||
@validator.schema(schema.VnfInfoModificationRequest_V200, '2.0.0')
|
@validator.schema(schema.VnfInfoModificationRequest_V200, '2.0.0')
|
||||||
@coordinate.lock_vnf_instance('{id}')
|
@coordinate.lock_vnf_instance('{id}')
|
||||||
def update(self, request, id, body):
|
def update(self, request, id, body):
|
||||||
@ -205,7 +188,8 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
detail="vnfcInstanceId(%s) does not exist."
|
detail="vnfcInstanceId(%s) does not exist."
|
||||||
% vnfc_mod['id'])
|
% vnfc_mod['id'])
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.MODIFY_INFO,
|
lcmocc = vnflcm_utils.new_lcmocc(
|
||||||
|
id, v2fields.LcmOperationType.MODIFY_INFO,
|
||||||
body, v2fields.LcmOperationStateType.PROCESSING)
|
body, v2fields.LcmOperationStateType.PROCESSING)
|
||||||
lcmocc.create(context)
|
lcmocc.create(context)
|
||||||
|
|
||||||
@ -226,8 +210,8 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
|
|
||||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.INSTANTIATE,
|
lcmocc = vnflcm_utils.new_lcmocc(
|
||||||
body)
|
id, v2fields.LcmOperationType.INSTANTIATE, body)
|
||||||
|
|
||||||
req_param = lcmocc.operationParams
|
req_param = lcmocc.operationParams
|
||||||
# if there is partial vimConnectionInfo check and fulfill here.
|
# if there is partial vimConnectionInfo check and fulfill here.
|
||||||
@ -260,8 +244,8 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
|
|
||||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.TERMINATE,
|
lcmocc = vnflcm_utils.new_lcmocc(
|
||||||
body)
|
id, v2fields.LcmOperationType.TERMINATE, body)
|
||||||
lcmocc.create(context)
|
lcmocc.create(context)
|
||||||
|
|
||||||
self.conductor_rpc.start_lcm_op(context, lcmocc.id)
|
self.conductor_rpc.start_lcm_op(context, lcmocc.id)
|
||||||
@ -270,93 +254,19 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
|
|
||||||
return sol_wsgi.SolResponse(202, None, location=location)
|
return sol_wsgi.SolResponse(202, None, location=location)
|
||||||
|
|
||||||
def _get_current_scale_level(self, inst, aspect_id):
|
|
||||||
if (inst.obj_attr_is_set('instantiatedVnfInfo') and
|
|
||||||
inst.instantiatedVnfInfo.obj_attr_is_set('scaleStatus')):
|
|
||||||
for scale_info in inst.instantiatedVnfInfo.scaleStatus:
|
|
||||||
if scale_info.aspectId == aspect_id:
|
|
||||||
return scale_info.scaleLevel
|
|
||||||
|
|
||||||
def _get_max_scale_level(self, inst, aspect_id):
|
|
||||||
if (inst.obj_attr_is_set('instantiatedVnfInfo') and
|
|
||||||
inst.instantiatedVnfInfo.obj_attr_is_set('maxScaleLevels')):
|
|
||||||
for scale_info in inst.instantiatedVnfInfo.maxScaleLevels:
|
|
||||||
if scale_info.aspectId == aspect_id:
|
|
||||||
return scale_info.scaleLevel
|
|
||||||
|
|
||||||
@validator.schema(schema.ScaleVnfRequest_V200, '2.0.0')
|
@validator.schema(schema.ScaleVnfRequest_V200, '2.0.0')
|
||||||
@coordinate.lock_vnf_instance('{id}')
|
|
||||||
def scale(self, request, id, body):
|
def scale(self, request, id, body):
|
||||||
context = request.context
|
context = request.context
|
||||||
inst = inst_utils.get_inst(context, id)
|
lcmocc = vnflcm_utils.scale(context, id, body)
|
||||||
|
|
||||||
if inst.instantiationState != 'INSTANTIATED':
|
|
||||||
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id)
|
|
||||||
|
|
||||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
|
||||||
|
|
||||||
# check parameters
|
|
||||||
aspect_id = body['aspectId']
|
|
||||||
if 'numberOfSteps' not in body:
|
|
||||||
# set default value (1) defined by SOL specification for
|
|
||||||
# the convenience of the following methods.
|
|
||||||
body['numberOfSteps'] = 1
|
|
||||||
|
|
||||||
scale_level = self._get_current_scale_level(inst, aspect_id)
|
|
||||||
max_scale_level = self._get_max_scale_level(inst, aspect_id)
|
|
||||||
if scale_level is None or max_scale_level is None:
|
|
||||||
raise sol_ex.InvalidScaleAspectId(aspect_id=aspect_id)
|
|
||||||
|
|
||||||
num_steps = body['numberOfSteps']
|
|
||||||
if body['type'] == 'SCALE_IN':
|
|
||||||
num_steps *= -1
|
|
||||||
scale_level += num_steps
|
|
||||||
if scale_level < 0 or scale_level > max_scale_level:
|
|
||||||
raise sol_ex.InvalidScaleNumberOfSteps(
|
|
||||||
num_steps=body['numberOfSteps'])
|
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.SCALE,
|
|
||||||
body)
|
|
||||||
lcmocc.create(context)
|
|
||||||
|
|
||||||
self.conductor_rpc.start_lcm_op(context, lcmocc.id)
|
|
||||||
|
|
||||||
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
|
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
|
||||||
|
|
||||||
return sol_wsgi.SolResponse(202, None, location=location)
|
return sol_wsgi.SolResponse(202, None, location=location)
|
||||||
|
|
||||||
@validator.schema(schema.HealVnfRequest_V200, '2.0.0')
|
@validator.schema(schema.HealVnfRequest_V200, '2.0.0')
|
||||||
@coordinate.lock_vnf_instance('{id}')
|
|
||||||
def heal(self, request, id, body):
|
def heal(self, request, id, body):
|
||||||
context = request.context
|
context = request.context
|
||||||
inst = inst_utils.get_inst(context, id)
|
lcmocc = vnflcm_utils.heal(context, id, body)
|
||||||
|
|
||||||
if inst.instantiationState != 'INSTANTIATED':
|
|
||||||
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=id)
|
|
||||||
|
|
||||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
|
||||||
|
|
||||||
# check parameter for later use
|
|
||||||
is_all = body.get('additionalParams', {}).get('all', False)
|
|
||||||
if not isinstance(is_all, bool):
|
|
||||||
raise sol_ex.SolValidationError(
|
|
||||||
detail="additionalParams['all'] must be bool.")
|
|
||||||
|
|
||||||
if 'vnfcInstanceId' in body:
|
|
||||||
inst_info = inst.instantiatedVnfInfo
|
|
||||||
vnfc_id = []
|
|
||||||
if inst_info.obj_attr_is_set('vnfcInfo'):
|
|
||||||
vnfc_id = [vnfc.id for vnfc in inst_info.vnfcInfo]
|
|
||||||
for req_vnfc_id in body['vnfcInstanceId']:
|
|
||||||
if req_vnfc_id not in vnfc_id:
|
|
||||||
raise sol_ex.SolValidationError(
|
|
||||||
detail="vnfcInstanceId(%s) does not exist."
|
|
||||||
% req_vnfc_id)
|
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.HEAL, body)
|
|
||||||
lcmocc.create(context)
|
|
||||||
|
|
||||||
self.conductor_rpc.start_lcm_op(context, lcmocc.id)
|
|
||||||
|
|
||||||
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
|
location = lcmocc_utils.lcmocc_href(lcmocc.id, self.endpoint)
|
||||||
|
|
||||||
@ -373,7 +283,7 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
|
|
||||||
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
lcmocc_utils.check_lcmocc_in_progress(context, id)
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(
|
lcmocc = vnflcm_utils.new_lcmocc(
|
||||||
id, v2fields.LcmOperationType.CHANGE_EXT_CONN, body)
|
id, v2fields.LcmOperationType.CHANGE_EXT_CONN, body)
|
||||||
lcmocc.create(context)
|
lcmocc.create(context)
|
||||||
|
|
||||||
@ -458,8 +368,8 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
raise sol_ex.SolValidationError(
|
raise sol_ex.SolValidationError(
|
||||||
detail="'lcm-kubernetes-def-files' must be specified")
|
detail="'lcm-kubernetes-def-files' must be specified")
|
||||||
|
|
||||||
lcmocc = self._new_lcmocc(id, v2fields.LcmOperationType.CHANGE_VNFPKG,
|
lcmocc = vnflcm_utils.new_lcmocc(
|
||||||
body)
|
id, v2fields.LcmOperationType.CHANGE_VNFPKG, body)
|
||||||
|
|
||||||
lcmocc.create(context)
|
lcmocc.create(context)
|
||||||
|
|
||||||
|
@ -288,6 +288,13 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
|
|||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
path, "GET", version="2.0.0")
|
path, "GET", version="2.0.0")
|
||||||
|
|
||||||
|
def list_lcmocc(self, filter_expr=None):
|
||||||
|
path = "/vnflcm/v2/vnf_lcm_op_occs"
|
||||||
|
if filter_expr:
|
||||||
|
path = "{}?{}".format(path, urllib.parse.urlencode(filter_expr))
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "GET", version="2.0.0")
|
||||||
|
|
||||||
def create_subscription(self, req_body):
|
def create_subscription(self, req_body):
|
||||||
path = "/vnffm/v1/subscriptions"
|
path = "/vnffm/v1/subscriptions"
|
||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
@ -370,7 +377,12 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
|
|||||||
path, "DELETE", version="2.1.0")
|
path, "DELETE", version="2.1.0")
|
||||||
|
|
||||||
def prometheus_auto_scaling_alert(self, req_body):
|
def prometheus_auto_scaling_alert(self, req_body):
|
||||||
path = "/alert/vnf_instances"
|
path = "/alert/auto_scaling"
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "POST", body=req_body)
|
||||||
|
|
||||||
|
def prometheus_auto_healing_alert(self, req_body):
|
||||||
|
path = "/alert/auto_healing"
|
||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
path, "POST", body=req_body)
|
path, "POST", body=req_body)
|
||||||
|
|
||||||
|
@ -739,3 +739,38 @@ def prometheus_auto_scaling_alert(inst_id):
|
|||||||
"groupKey": "{}:{}",
|
"groupKey": "{}:{}",
|
||||||
"truncatedAlerts": 0
|
"truncatedAlerts": 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def prometheus_auto_healing_alert(inst_id, vnfc_info_id):
|
||||||
|
return {
|
||||||
|
"receiver": "receiver",
|
||||||
|
"status": "firing",
|
||||||
|
"alerts": [{
|
||||||
|
"status": "firing",
|
||||||
|
"labels": {
|
||||||
|
"receiver_type": "tacker",
|
||||||
|
"function_type": "auto_heal",
|
||||||
|
"vnf_instance_id": inst_id,
|
||||||
|
"vnfc_info_id": vnfc_info_id
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
},
|
||||||
|
"startsAt": "2022-06-21T23:47:36.453Z",
|
||||||
|
"endsAt": "0001-01-01T00:00:00Z",
|
||||||
|
"generatorURL": "http://controller147:9090/graph?g0.expr="
|
||||||
|
"up%7Bjob%3D%22node%22%7D+%3D%3D+0&g0.tab=1",
|
||||||
|
"fingerprint": "5ef77f1f8a3ecb8d"
|
||||||
|
}],
|
||||||
|
"groupLabels": {},
|
||||||
|
"commonLabels": {
|
||||||
|
"alertname": "NodeInstanceDown",
|
||||||
|
"job": "node"
|
||||||
|
},
|
||||||
|
"commonAnnotations": {
|
||||||
|
"description": "sample"
|
||||||
|
},
|
||||||
|
"externalURL": "http://controller147:9093",
|
||||||
|
"version": "4",
|
||||||
|
"groupKey": "{}:{}",
|
||||||
|
"truncatedAlerts": 0
|
||||||
|
}
|
||||||
|
@ -0,0 +1,355 @@
|
|||||||
|
# Copyright (C) 2023 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 ddt
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from tacker.tests.functional.sol_kubernetes_v2 import base_v2
|
||||||
|
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
||||||
|
|
||||||
|
# Waiting time to trigger autoheal (unit: second)
|
||||||
|
WAIT_AUTO_HEAL_TIME = 23
|
||||||
|
|
||||||
|
# Waiting time to lcmocc update in DB (unit: second)
|
||||||
|
WAIT_LCMOCC_UPDATE_TIME = 3
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class PromAutoScaleHealTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(PromAutoScaleHealTest, cls).setUpClass()
|
||||||
|
|
||||||
|
cur_dir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
test_instantiate_cnf_resources_path = os.path.join(
|
||||||
|
cur_dir, "samples/test_instantiate_cnf_resources")
|
||||||
|
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
|
||||||
|
test_instantiate_cnf_resources_path)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(PromAutoScaleHealTest, cls).tearDownClass()
|
||||||
|
|
||||||
|
cls.delete_vnf_package(cls.vnf_pkg_1)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PromAutoScaleHealTest, self).setUp()
|
||||||
|
|
||||||
|
def test_vnfm_auto_heal_cnf(self):
|
||||||
|
"""Test Prometheus VNFM Auto Healing operations
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance
|
||||||
|
- 3. Show OpOcc
|
||||||
|
- 4-5. Receive Alert and Auto Heal
|
||||||
|
- 6. Show OpOcc
|
||||||
|
- 7. Receive Alert
|
||||||
|
- 8-9. Receive Alert and Auto Heal
|
||||||
|
- 10. Show OpOcc
|
||||||
|
- 11. Terminate a VNF instance
|
||||||
|
- 12. Show OpOcc
|
||||||
|
- 13. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. LCM-Create: Create a new VNF instance resource
|
||||||
|
create_req = paramgen.instantiate_cnf_resources_create(self.vnfd_id_1)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. LCM-Instantiate: Instantiate a VNF instance
|
||||||
|
vim_id = self.get_k8s_vim_id()
|
||||||
|
instantiate_req = paramgen.min_sample_instantiate(vim_id)
|
||||||
|
instantiate_req['additionalParams'][
|
||||||
|
'lcm-kubernetes-def-files'] = ['Files/kubernetes/deployment.yaml']
|
||||||
|
instantiate_req['vnfConfigurableProperties'] = {
|
||||||
|
'isAutohealEnabled': True}
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 3. LCM-Show-OpOccV2: Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('INSTANTIATE', body['operation'])
|
||||||
|
|
||||||
|
# 4-5. Send alert and auto heal
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
vnfc_info_id = (affected_vnfcs[0]['vduId'] + '-'
|
||||||
|
+ affected_vnfcs[0]['id'])
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(inst_id, vnfc_info_id)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# Since auto heal takes 20 seconds to trigger,
|
||||||
|
# wait 23 seconds here.
|
||||||
|
time.sleep(WAIT_AUTO_HEAL_TIME)
|
||||||
|
|
||||||
|
# 6. LCM-Show-OpOccV2: Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
heal_lcmocc = [
|
||||||
|
heal_lcmocc for heal_lcmocc in body
|
||||||
|
if heal_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = heal_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('HEAL', body['operation'])
|
||||||
|
self.assertEqual(2, len(affected_vnfcs))
|
||||||
|
|
||||||
|
added_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'ADDED']
|
||||||
|
self.assertEqual(1, len(added_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'REMOVED']
|
||||||
|
self.assertEqual(1, len(removed_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfc_info_id = (affected_vnfcs[0]['vduId'] + '-'
|
||||||
|
+ affected_vnfcs[0]['id'])
|
||||||
|
self.assertEqual(vnfc_info_id, removed_vnfc_info_id)
|
||||||
|
|
||||||
|
# 7. Send alert
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
vnfc_infos = body['instantiatedVnfInfo']['vnfcInfo']
|
||||||
|
vnfc_info_id_1 = vnfc_infos[0]['id']
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(
|
||||||
|
inst_id, vnfc_info_id_1)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# 8-9. Send alert and auto heal
|
||||||
|
vnfc_info_id_2 = vnfc_infos[1]['id']
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(
|
||||||
|
inst_id, vnfc_info_id_2)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# Since auto heal takes 20 seconds to trigger,
|
||||||
|
# wait 23 seconds here.
|
||||||
|
time.sleep(WAIT_AUTO_HEAL_TIME)
|
||||||
|
|
||||||
|
# 10. LCM-Show-OpOccV2: Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
heal_lcmocc = [
|
||||||
|
heal_lcmocc for heal_lcmocc in body
|
||||||
|
if heal_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = heal_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('HEAL', body['operation'])
|
||||||
|
self.assertEqual(4, len(affected_vnfcs))
|
||||||
|
|
||||||
|
added_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'ADDED']
|
||||||
|
self.assertEqual(2, len(added_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'REMOVED']
|
||||||
|
self.assertEqual(2, len(removed_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfc_info_ids = [
|
||||||
|
removed_vnfcs[0]['vduId'] + '-' + removed_vnfcs[0]['id'],
|
||||||
|
removed_vnfcs[1]['vduId'] + '-' + removed_vnfcs[1]['id']
|
||||||
|
]
|
||||||
|
self.assertCountEqual(
|
||||||
|
[vnfc_info_id_1, vnfc_info_id_2], removed_vnfc_info_ids)
|
||||||
|
|
||||||
|
# 11. LCM-Terminate: Terminate VNF
|
||||||
|
terminate_req = paramgen.terminate_vnf_min()
|
||||||
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and terminate completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 12. LCM-Show-OpOccV2: Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('TERMINATE', body['operation'])
|
||||||
|
|
||||||
|
# 13. LCM-Delete: Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# check deletion of VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_vnfm_auto_scale_cnf(self):
|
||||||
|
"""Test Prometheus VNFM Auto Scaling operations
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance
|
||||||
|
- 3. Show OpOcc
|
||||||
|
- 4-5. Receive Alert and Auto Scale out
|
||||||
|
- 6. Show OpOcc
|
||||||
|
- 7-8. Receive Alert and Auto Scale in
|
||||||
|
- 9. Show OpOcc
|
||||||
|
- 10. Terminate a VNF instance
|
||||||
|
- 11. Show OpOcc
|
||||||
|
- 12. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. LCM-Create: Create a new VNF instance resource
|
||||||
|
create_req = paramgen.instantiate_cnf_resources_create(self.vnfd_id_1)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. LCM-Instantiate: Instantiate a VNF instance
|
||||||
|
vim_id = self.get_k8s_vim_id()
|
||||||
|
instantiate_req = paramgen.min_sample_instantiate(vim_id)
|
||||||
|
instantiate_req['additionalParams'][
|
||||||
|
'lcm-kubernetes-def-files'] = ['Files/kubernetes/deployment.yaml']
|
||||||
|
instantiate_req['vnfConfigurableProperties'] = {
|
||||||
|
'isAutoscaleEnabled': True}
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 3. LCM-Show-OpOccV2: Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('INSTANTIATE', body['operation'])
|
||||||
|
|
||||||
|
# 4-5. Send alert and auto-scale out
|
||||||
|
alert = paramgen.prometheus_auto_scaling_alert(inst_id)
|
||||||
|
resp, body = self.prometheus_auto_scaling_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and scale completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 6. LCM-Show-OpOccV2: Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
scale_lcmocc = [
|
||||||
|
scale_lcmocc for scale_lcmocc in body
|
||||||
|
if scale_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = scale_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('SCALE', body['operation'])
|
||||||
|
self.assertEqual(1, len(affected_vnfcs))
|
||||||
|
self.assertEqual('ADDED', affected_vnfcs[0]['changeType'])
|
||||||
|
self.assertEqual(
|
||||||
|
alert['alerts'][0]['labels']['aspect_id'],
|
||||||
|
body['operationParams']['aspectId'])
|
||||||
|
|
||||||
|
# 7-8. Send alert and auto-scale in
|
||||||
|
alert['alerts'][0]['labels']['auto_scale_type'] = 'SCALE_IN'
|
||||||
|
resp, body = self.prometheus_auto_scaling_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and scale completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 9. LCM-Show-OpOccV2: Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
scale_lcmocc = [
|
||||||
|
scale_lcmocc for scale_lcmocc in body
|
||||||
|
if scale_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = scale_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('SCALE', body['operation'])
|
||||||
|
self.assertEqual(1, len(affected_vnfcs))
|
||||||
|
self.assertEqual('REMOVED', affected_vnfcs[0]['changeType'])
|
||||||
|
self.assertEqual(
|
||||||
|
alert['alerts'][0]['labels']['aspect_id'],
|
||||||
|
body['operationParams']['aspectId'])
|
||||||
|
|
||||||
|
# 10. LCM-Terminate: Terminate VNF
|
||||||
|
terminate_req = paramgen.terminate_vnf_min()
|
||||||
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and terminate completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 11. LCM-Show-OpOccV2: Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('TERMINATE', body['operation'])
|
||||||
|
|
||||||
|
# 12. LCM-Delete: Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# check deletion of VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(404, resp.status_code)
|
@ -1,115 +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 ddt
|
|
||||||
import os
|
|
||||||
import time
|
|
||||||
|
|
||||||
from tacker.objects import fields
|
|
||||||
from tacker.tests.functional.sol_kubernetes_v2 import base_v2
|
|
||||||
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
|
||||||
|
|
||||||
|
|
||||||
@ddt.ddt
|
|
||||||
class PrometheusAutoScalingTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def setUpClass(cls):
|
|
||||||
super(PrometheusAutoScalingTest, cls).setUpClass()
|
|
||||||
|
|
||||||
cur_dir = os.path.dirname(__file__)
|
|
||||||
|
|
||||||
test_instantiate_cnf_resources_path = os.path.join(
|
|
||||||
cur_dir, "samples/test_instantiate_cnf_resources")
|
|
||||||
cls.vnf_pkg_1, cls.vnfd_id_1 = cls.create_vnf_package(
|
|
||||||
test_instantiate_cnf_resources_path)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def tearDownClass(cls):
|
|
||||||
super(PrometheusAutoScalingTest, cls).tearDownClass()
|
|
||||||
|
|
||||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(PrometheusAutoScalingTest, self).setUp()
|
|
||||||
|
|
||||||
def test_prometheus_auto_scaling_basic(self):
|
|
||||||
"""Test Prometheus Auto Scaling operations with all attributes set
|
|
||||||
|
|
||||||
* About LCM operations:
|
|
||||||
This test includes the following operations.
|
|
||||||
- 1. Create a new VNF instance resource
|
|
||||||
- 2. Instantiate a VNF instance
|
|
||||||
- 3. Prometheus Auto Scaling alert.
|
|
||||||
- 4. Terminate a VNF instance
|
|
||||||
- 5. Delete a VNF instance
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 1. LCM-Create: Create a new VNF instance resource
|
|
||||||
# NOTE: extensions and vnfConfigurableProperties are omitted
|
|
||||||
# because they are commented out in etsi_nfv_sol001.
|
|
||||||
create_req = paramgen.instantiate_cnf_resources_create(self.vnfd_id_1)
|
|
||||||
resp, body = self.create_vnf_instance(create_req)
|
|
||||||
self.assertEqual(201, resp.status_code)
|
|
||||||
inst_id = body['id']
|
|
||||||
|
|
||||||
# 2. LCM-Instantiate: Instantiate a VNF instance
|
|
||||||
vim_id = self.get_k8s_vim_id()
|
|
||||||
instantiate_req = paramgen.min_sample_instantiate(vim_id)
|
|
||||||
instantiate_req['additionalParams'][
|
|
||||||
'lcm-kubernetes-def-files'] = ['Files/kubernetes/deployment.yaml']
|
|
||||||
instantiate_req['vnfConfigurableProperties'] = {
|
|
||||||
'isAutoscaleEnabled': True}
|
|
||||||
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
|
|
||||||
# 3. Send Auto-Healing alert
|
|
||||||
alert = paramgen.prometheus_auto_scaling_alert(inst_id)
|
|
||||||
# CNF scale is not integrated yet. use this value for now.
|
|
||||||
alert['alerts'][0]['labels']['aspect_id'] = 'invalid_id'
|
|
||||||
resp, body = self.prometheus_auto_scaling_alert(alert)
|
|
||||||
self.assertEqual(204, resp.status_code)
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
# 4. LCM-Terminate: Terminate VNF
|
|
||||||
terminate_req = paramgen.terminate_vnf_min()
|
|
||||||
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
|
||||||
self.assertEqual(202, resp.status_code)
|
|
||||||
|
|
||||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
|
||||||
self.wait_lcmocc_complete(lcmocc_id)
|
|
||||||
|
|
||||||
# wait a bit because there is a bit time lag between lcmocc DB
|
|
||||||
# update and terminate completion.
|
|
||||||
time.sleep(10)
|
|
||||||
|
|
||||||
# check instantiationState of VNF
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(200, resp.status_code)
|
|
||||||
self.assertEqual(fields.VnfInstanceState.NOT_INSTANTIATED,
|
|
||||||
body['instantiationState'])
|
|
||||||
|
|
||||||
# 5. LCM-Delete: Delete a VNF instance
|
|
||||||
resp, body = self.delete_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(204, resp.status_code)
|
|
||||||
|
|
||||||
# check deletion of VNF instance
|
|
||||||
resp, body = self.show_vnf_instance(inst_id)
|
|
||||||
self.assertEqual(404, resp.status_code)
|
|
361
tacker/tests/functional/sol_v2/test_prom_auto_scale_heal.py
Normal file
361
tacker/tests/functional/sol_v2/test_prom_auto_scale_heal.py
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
# Copyright (C) 2023 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 ddt
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
from tacker.tests.functional.sol_v2_common import paramgen
|
||||||
|
from tacker.tests.functional.sol_v2_common import test_vnflcm_basic_common
|
||||||
|
|
||||||
|
# Waiting time to trigger autoheal (unit: second)
|
||||||
|
WAIT_AUTO_HEAL_TIME = 23
|
||||||
|
|
||||||
|
# Waiting time to lcmocc update in DB (unit: second)
|
||||||
|
WAIT_LCMOCC_UPDATE_TIME = 3
|
||||||
|
|
||||||
|
|
||||||
|
@ddt.ddt
|
||||||
|
class PromAutoScaleHealTest(test_vnflcm_basic_common.CommonVnfLcmTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpClass(cls):
|
||||||
|
super(PromAutoScaleHealTest, cls).setUpClass()
|
||||||
|
cur_dir = os.path.dirname(__file__)
|
||||||
|
|
||||||
|
# for basic lcms tests min pattern
|
||||||
|
basic_lcms_min_path = os.path.join(
|
||||||
|
cur_dir, "../sol_v2_common/samples/basic_lcms_min")
|
||||||
|
# no image contained
|
||||||
|
cls.vnf_pkg_2, cls.vnfd_id_2 = cls.create_vnf_package(
|
||||||
|
basic_lcms_min_path)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super(PromAutoScaleHealTest, cls).tearDownClass()
|
||||||
|
cls.delete_vnf_package(cls.vnf_pkg_2)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(PromAutoScaleHealTest, self).setUp()
|
||||||
|
|
||||||
|
def test_vnfm_auto_heal_vnf(self):
|
||||||
|
"""Test Prometheus VNFM Auto Healing operations
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance
|
||||||
|
- 3. Show OpOcc
|
||||||
|
- 4-5. Receive Alert and Auto Heal
|
||||||
|
- 6. Show OpOcc
|
||||||
|
- 7. Receive Alert
|
||||||
|
- 8-9. Receive Alert and Auto Heal
|
||||||
|
- 10. Show OpOcc
|
||||||
|
- 11. Terminate a VNF instance
|
||||||
|
- 12. Show OpOcc
|
||||||
|
- 13. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Create VNF instance
|
||||||
|
expected_inst_attrs = [
|
||||||
|
'id', 'vnfdId', 'vnfProductName', 'vnfSoftwareVersion',
|
||||||
|
'vnfdVersion', 'instantiationState', '_links'
|
||||||
|
]
|
||||||
|
create_req = paramgen.create_vnf_min(self.vnfd_id_2)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
self.check_resp_headers_in_create(resp)
|
||||||
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. Instantiate VNF
|
||||||
|
instantiate_req = paramgen.instantiate_vnf_min()
|
||||||
|
instantiate_req['vnfConfigurableProperties'] = {
|
||||||
|
'isAutohealEnabled': True}
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 3. Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('INSTANTIATE', body['operation'])
|
||||||
|
|
||||||
|
# 4-5. Send alert and auto heal
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
vnfc_info_id = (affected_vnfcs[0]['vduId'] + '-'
|
||||||
|
+ affected_vnfcs[0]['id'])
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(inst_id, vnfc_info_id)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# Since auto heal takes 20 seconds to trigger,
|
||||||
|
# wait 23 seconds here.
|
||||||
|
time.sleep(WAIT_AUTO_HEAL_TIME)
|
||||||
|
|
||||||
|
# 6. Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
heal_lcmocc = [
|
||||||
|
heal_lcmocc for heal_lcmocc in body
|
||||||
|
if heal_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = heal_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('HEAL', body['operation'])
|
||||||
|
self.assertEqual(2, len(affected_vnfcs))
|
||||||
|
|
||||||
|
added_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'ADDED']
|
||||||
|
self.assertEqual(1, len(added_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'REMOVED']
|
||||||
|
self.assertEqual(1, len(removed_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfc_info_id = (affected_vnfcs[0]['vduId'] + '-'
|
||||||
|
+ affected_vnfcs[0]['id'])
|
||||||
|
self.assertEqual(vnfc_info_id, removed_vnfc_info_id)
|
||||||
|
|
||||||
|
# 7. Send alert
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
vnfc_infos = body['instantiatedVnfInfo']['vnfcInfo']
|
||||||
|
vnfc_info_id_1 = vnfc_infos[0]['id']
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(
|
||||||
|
inst_id, vnfc_info_id_1)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# 8-9. Send alert and auto heal
|
||||||
|
vnfc_info_id_2 = vnfc_infos[1]['id']
|
||||||
|
alert = paramgen.prometheus_auto_healing_alert(
|
||||||
|
inst_id, vnfc_info_id_2)
|
||||||
|
resp, body = self.prometheus_auto_healing_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# Since auto heal takes 20 seconds to trigger,
|
||||||
|
# wait 23 seconds here.
|
||||||
|
time.sleep(WAIT_AUTO_HEAL_TIME)
|
||||||
|
|
||||||
|
# 10. Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
heal_lcmocc = [
|
||||||
|
heal_lcmocc for heal_lcmocc in body
|
||||||
|
if heal_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = heal_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('HEAL', body['operation'])
|
||||||
|
self.assertEqual(4, len(affected_vnfcs))
|
||||||
|
|
||||||
|
added_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'ADDED']
|
||||||
|
self.assertEqual(2, len(added_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfcs = [
|
||||||
|
vnfc for vnfc in affected_vnfcs
|
||||||
|
if vnfc['changeType'] == 'REMOVED']
|
||||||
|
self.assertEqual(2, len(removed_vnfcs))
|
||||||
|
|
||||||
|
removed_vnfc_info_ids = [
|
||||||
|
removed_vnfcs[0]['vduId'] + '-' + removed_vnfcs[0]['id'],
|
||||||
|
removed_vnfcs[1]['vduId'] + '-' + removed_vnfcs[1]['id']
|
||||||
|
]
|
||||||
|
self.assertCountEqual(
|
||||||
|
[vnfc_info_id_1, vnfc_info_id_2], removed_vnfc_info_ids)
|
||||||
|
|
||||||
|
# 11. Terminate a VNF instance
|
||||||
|
terminate_req = paramgen.terminate_vnf_min()
|
||||||
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and terminate completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 12. Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('TERMINATE', body['operation'])
|
||||||
|
|
||||||
|
# 13. Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# check deletion of VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(404, resp.status_code)
|
||||||
|
|
||||||
|
def test_vnfm_auto_scale_vnf(self):
|
||||||
|
"""Test Prometheus VNFM Auto Scaling operations
|
||||||
|
|
||||||
|
* About LCM operations:
|
||||||
|
This test includes the following operations.
|
||||||
|
- 1. Create a new VNF instance resource
|
||||||
|
- 2. Instantiate a VNF instance
|
||||||
|
- 3. Show OpOcc
|
||||||
|
- 4-5. Receive Alert and Auto Scale out
|
||||||
|
- 6. Show OpOcc
|
||||||
|
- 7-8. Receive Alert and Auto Scale in
|
||||||
|
- 9. Show OpOcc
|
||||||
|
- 10. Terminate a VNF instance
|
||||||
|
- 11. Show OpOcc
|
||||||
|
- 12. Delete a VNF instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 1. Create VNF instance
|
||||||
|
expected_inst_attrs = [
|
||||||
|
'id', 'vnfdId', 'vnfProductName', 'vnfSoftwareVersion',
|
||||||
|
'vnfdVersion', 'instantiationState', '_links'
|
||||||
|
]
|
||||||
|
create_req = paramgen.create_vnf_min(self.vnfd_id_2)
|
||||||
|
resp, body = self.create_vnf_instance(create_req)
|
||||||
|
self.assertEqual(201, resp.status_code)
|
||||||
|
self.check_resp_headers_in_create(resp)
|
||||||
|
self.check_resp_body(body, expected_inst_attrs)
|
||||||
|
inst_id = body['id']
|
||||||
|
|
||||||
|
# 2. Instantiate VNF
|
||||||
|
instantiate_req = paramgen.instantiate_vnf_min()
|
||||||
|
instantiate_req['vnfConfigurableProperties'] = {
|
||||||
|
'isAutoscaleEnabled': True}
|
||||||
|
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# 3. Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('INSTANTIATE', body['operation'])
|
||||||
|
|
||||||
|
# 4-5. Send alert and auto heal
|
||||||
|
alert = paramgen.prometheus_auto_scaling_alert(inst_id)
|
||||||
|
resp, body = self.prometheus_auto_scaling_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and scale completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 6. Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
scale_lcmocc = [
|
||||||
|
scale_lcmocc for scale_lcmocc in body
|
||||||
|
if scale_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = scale_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('SCALE', body['operation'])
|
||||||
|
self.assertEqual(1, len(affected_vnfcs))
|
||||||
|
self.assertEqual('ADDED', affected_vnfcs[0]['changeType'])
|
||||||
|
self.assertEqual(
|
||||||
|
alert['alerts'][0]['labels']['aspect_id'],
|
||||||
|
body['operationParams']['aspectId'])
|
||||||
|
|
||||||
|
# 7-8. Send alert and auto-scale in
|
||||||
|
alert['alerts'][0]['labels']['auto_scale_type'] = 'SCALE_IN'
|
||||||
|
resp, body = self.prometheus_auto_scaling_alert(alert)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and scale completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 9. Show-OpOcc
|
||||||
|
filter_expr = {'filter': f'(eq,vnfInstanceId,{inst_id})'}
|
||||||
|
resp, body = self.list_lcmocc(filter_expr)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
|
||||||
|
scale_lcmocc = [
|
||||||
|
scale_lcmocc for scale_lcmocc in body
|
||||||
|
if scale_lcmocc['startTime'] == max(
|
||||||
|
[lcmocc['startTime'] for lcmocc in body])][0]
|
||||||
|
lcmocc_id = scale_lcmocc['id']
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
affected_vnfcs = body['resourceChanges']['affectedVnfcs']
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('SCALE', body['operation'])
|
||||||
|
self.assertEqual(1, len(affected_vnfcs))
|
||||||
|
self.assertEqual('REMOVED', affected_vnfcs[0]['changeType'])
|
||||||
|
self.assertEqual(
|
||||||
|
alert['alerts'][0]['labels']['aspect_id'],
|
||||||
|
body['operationParams']['aspectId'])
|
||||||
|
|
||||||
|
# 10. Terminate a VNF instance
|
||||||
|
terminate_req = paramgen.terminate_vnf_min()
|
||||||
|
resp, body = self.terminate_vnf_instance(inst_id, terminate_req)
|
||||||
|
self.assertEqual(202, resp.status_code)
|
||||||
|
|
||||||
|
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||||
|
self.wait_lcmocc_complete(lcmocc_id)
|
||||||
|
|
||||||
|
# wait a bit because there is a bit time lag between lcmocc DB
|
||||||
|
# update and terminate completion.
|
||||||
|
time.sleep(WAIT_LCMOCC_UPDATE_TIME)
|
||||||
|
|
||||||
|
# 11. Show OpOcc
|
||||||
|
resp, body = self.show_lcmocc(lcmocc_id)
|
||||||
|
self.assertEqual(200, resp.status_code)
|
||||||
|
self.assertEqual('COMPLETED', body['operationState'])
|
||||||
|
self.assertEqual('TERMINATE', body['operation'])
|
||||||
|
|
||||||
|
# 12. Delete a VNF instance
|
||||||
|
resp, body = self.delete_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(204, resp.status_code)
|
||||||
|
|
||||||
|
# check deletion of VNF instance
|
||||||
|
resp, body = self.show_vnf_instance(inst_id)
|
||||||
|
self.assertEqual(404, resp.status_code)
|
@ -495,6 +495,16 @@ class BaseSolV2Test(base.BaseTestCase):
|
|||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
path, "POST", version="2.0.0")
|
path, "POST", version="2.0.0")
|
||||||
|
|
||||||
|
def prometheus_auto_healing_alert(self, req_body):
|
||||||
|
path = "/alert/auto_healing"
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "POST", body=req_body)
|
||||||
|
|
||||||
|
def prometheus_auto_scaling_alert(self, req_body):
|
||||||
|
path = "/alert/auto_scaling"
|
||||||
|
return self.tacker_client.do_request(
|
||||||
|
path, "POST", body=req_body)
|
||||||
|
|
||||||
def create_subscription(self, req_body):
|
def create_subscription(self, req_body):
|
||||||
path = "/vnflcm/v2/subscriptions"
|
path = "/vnflcm/v2/subscriptions"
|
||||||
return self.tacker_client.do_request(
|
return self.tacker_client.do_request(
|
||||||
|
@ -1198,6 +1198,77 @@ def sample4_terminate():
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def prometheus_auto_healing_alert(inst_id, vnfc_info_id):
|
||||||
|
return {
|
||||||
|
"receiver": "receiver",
|
||||||
|
"status": "firing",
|
||||||
|
"alerts": [{
|
||||||
|
"status": "firing",
|
||||||
|
"labels": {
|
||||||
|
"receiver_type": "tacker",
|
||||||
|
"function_type": "auto_heal",
|
||||||
|
"vnf_instance_id": inst_id,
|
||||||
|
"vnfc_info_id": vnfc_info_id
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
},
|
||||||
|
"startsAt": "2022-06-21T23:47:36.453Z",
|
||||||
|
"endsAt": "0001-01-01T00:00:00Z",
|
||||||
|
"generatorURL": "http://controller147:9090/graph?g0.expr="
|
||||||
|
"up%7Bjob%3D%22node%22%7D+%3D%3D+0&g0.tab=1",
|
||||||
|
"fingerprint": "5ef77f1f8a3ecb8d"
|
||||||
|
}],
|
||||||
|
"groupLabels": {},
|
||||||
|
"commonLabels": {
|
||||||
|
"alertname": "NodeInstanceDown",
|
||||||
|
"job": "node"
|
||||||
|
},
|
||||||
|
"commonAnnotations": {
|
||||||
|
"description": "sample"
|
||||||
|
},
|
||||||
|
"externalURL": "http://controller147:9093",
|
||||||
|
"version": "4",
|
||||||
|
"groupKey": "{}:{}",
|
||||||
|
"truncatedAlerts": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def prometheus_auto_scaling_alert(inst_id):
|
||||||
|
return {
|
||||||
|
"receiver": "receiver",
|
||||||
|
"status": "firing",
|
||||||
|
"alerts": [{
|
||||||
|
"status": "firing",
|
||||||
|
"labels": {
|
||||||
|
"receiver_type": "tacker",
|
||||||
|
"function_type": "auto_scale",
|
||||||
|
"vnf_instance_id": inst_id,
|
||||||
|
"auto_scale_type": "SCALE_OUT",
|
||||||
|
"aspect_id": "VDU1_scale"
|
||||||
|
},
|
||||||
|
"annotations": {
|
||||||
|
},
|
||||||
|
"startsAt": "2022-06-21T23:47:36.453Z",
|
||||||
|
"endsAt": "0001-01-01T00:00:00Z",
|
||||||
|
"generatorURL": "http://controller147:9090/graph?g0.expr="
|
||||||
|
"up%7Bjob%3D%22node%22%7D+%3D%3D+0&g0.tab=1",
|
||||||
|
"fingerprint": "5ef77f1f8a3ecb8d"
|
||||||
|
}],
|
||||||
|
"groupLabels": {},
|
||||||
|
"commonLabels": {
|
||||||
|
"alertname": "NodeInstanceDown",
|
||||||
|
"job": "node"
|
||||||
|
},
|
||||||
|
"commonAnnotations": {
|
||||||
|
"description": "sample"
|
||||||
|
},
|
||||||
|
"externalURL": "http://controller147:9093",
|
||||||
|
"version": "4",
|
||||||
|
"groupKey": "{}:{}",
|
||||||
|
"truncatedAlerts": 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def server_notification(alarm_id):
|
def server_notification(alarm_id):
|
||||||
return {
|
return {
|
||||||
'notification': {
|
'notification': {
|
||||||
|
@ -707,6 +707,40 @@ class TestPrometheusPluginFm(base.TestCase):
|
|||||||
pp._alert, self.request)
|
pp._alert, self.request)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrometheusPluginAutoHealing(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPrometheusPluginAutoHealing, self).setUp()
|
||||||
|
objects.register_all()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.request = mock.Mock()
|
||||||
|
self.request.context = self.context
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing._instance = None
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestPrometheusPluginAutoHealing, self).tearDown()
|
||||||
|
# delete singleton object
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing._instance = None
|
||||||
|
|
||||||
|
def test_constructor_error(self):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=False)
|
||||||
|
mon_base.MonitoringPlugin.get_instance(
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing)
|
||||||
|
self.assertRaises(
|
||||||
|
SystemError,
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing)
|
||||||
|
|
||||||
|
def test_constructor_stub(self):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=False)
|
||||||
|
pp = mon_base.MonitoringPlugin.get_instance(
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing)
|
||||||
|
self.assertIsInstance(pp._instance, mon_base.MonitoringPluginStub)
|
||||||
|
pp = mon_base.MonitoringPlugin.get_instance(
|
||||||
|
prometheus_plugin.PrometheusPluginAutoHealing)
|
||||||
|
self.assertIsInstance(pp._instance, mon_base.MonitoringPluginStub)
|
||||||
|
|
||||||
|
|
||||||
class TestPrometheusPluginAutoScaling(base.TestCase):
|
class TestPrometheusPluginAutoScaling(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestPrometheusPluginAutoScaling, self).setUp()
|
super(TestPrometheusPluginAutoScaling, self).setUp()
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import webob
|
import time
|
||||||
|
|
||||||
from tacker import context
|
from tacker import context
|
||||||
from tacker.sol_refactored.common import http_client
|
from tacker.sol_refactored.common import vnflcm_utils
|
||||||
from tacker.sol_refactored.conductor import conductor_v2
|
from tacker.sol_refactored.conductor import conductor_v2
|
||||||
from tacker.sol_refactored.conductor import prometheus_plugin_driver as pp_drv
|
from tacker.sol_refactored.conductor import prometheus_plugin_driver as pp_drv
|
||||||
from tacker.sol_refactored import objects
|
from tacker.sol_refactored import objects
|
||||||
@ -119,6 +119,7 @@ class TestPrometheusPlugin(db_base.SqlTestCase):
|
|||||||
self.context = context.get_admin_context()
|
self.context = context.get_admin_context()
|
||||||
self.request = mock.Mock()
|
self.request = mock.Mock()
|
||||||
self.request.context = self.context
|
self.request.context = self.context
|
||||||
|
self.timer_test = (None, None)
|
||||||
self.config_fixture.config(
|
self.config_fixture.config(
|
||||||
group='prometheus_plugin', performance_management=True)
|
group='prometheus_plugin', performance_management=True)
|
||||||
self.conductor = conductor_v2.ConductorV2()
|
self.conductor = conductor_v2.ConductorV2()
|
||||||
@ -129,16 +130,13 @@ class TestPrometheusPlugin(db_base.SqlTestCase):
|
|||||||
# delete singleton object
|
# delete singleton object
|
||||||
pp_drv.PrometheusPluginDriver._instance = None
|
pp_drv.PrometheusPluginDriver._instance = None
|
||||||
|
|
||||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
@mock.patch.object(vnflcm_utils, 'scale')
|
||||||
def test_request_scale(self, mock_do_request):
|
def test_trigger_scale(self, mock_do_scale):
|
||||||
resp = webob.Response()
|
|
||||||
resp.status_code = 202
|
|
||||||
mock_do_request.return_value = resp, {}
|
|
||||||
scale_req = {
|
scale_req = {
|
||||||
'type': 'SCALE_OUT',
|
'type': 'SCALE_OUT',
|
||||||
'aspect_id': 'vdu',
|
'aspectId': 'vdu',
|
||||||
}
|
}
|
||||||
self.conductor.request_scale(
|
self.conductor.trigger_scale(
|
||||||
self.context, 'vnf_instance_id', scale_req)
|
self.context, 'vnf_instance_id', scale_req)
|
||||||
|
|
||||||
def test_constructor(self):
|
def test_constructor(self):
|
||||||
@ -150,8 +148,7 @@ class TestPrometheusPlugin(db_base.SqlTestCase):
|
|||||||
group='prometheus_plugin', performance_management=False)
|
group='prometheus_plugin', performance_management=False)
|
||||||
pp_drv.PrometheusPluginDriver._instance = None
|
pp_drv.PrometheusPluginDriver._instance = None
|
||||||
drv = pp_drv.PrometheusPluginDriver.instance()
|
drv = pp_drv.PrometheusPluginDriver.instance()
|
||||||
drv = pp_drv.PrometheusPluginDriver.instance()
|
drv.trigger_scale(None, None, None)
|
||||||
drv.request_scale(None, None, None)
|
|
||||||
self.config_fixture.config(
|
self.config_fixture.config(
|
||||||
group='prometheus_plugin', performance_management=True)
|
group='prometheus_plugin', performance_management=True)
|
||||||
drv = pp_drv.PrometheusPluginDriver.instance()
|
drv = pp_drv.PrometheusPluginDriver.instance()
|
||||||
@ -163,3 +160,73 @@ class TestPrometheusPlugin(db_base.SqlTestCase):
|
|||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
SystemError,
|
SystemError,
|
||||||
pp_drv.PrometheusPluginDriver)
|
pp_drv.PrometheusPluginDriver)
|
||||||
|
|
||||||
|
def test_conductor_vnfm_auto_heal_queue(self):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
pp_drv.PrometheusPluginDriver._instance = None
|
||||||
|
self.conductor.prom_driver = pp_drv.PrometheusPluginDriver.instance()
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', timer_interval=1)
|
||||||
|
# queueing test
|
||||||
|
id = 'test_id'
|
||||||
|
self.conductor.enqueue_auto_heal_instance(
|
||||||
|
self.context, id, 'id')
|
||||||
|
self.conductor.enqueue_auto_heal_instance(
|
||||||
|
self.context, id, 'id2')
|
||||||
|
self.assertEqual(
|
||||||
|
self.conductor.prom_driver.timer_map[id].queue,
|
||||||
|
{'id', 'id2'})
|
||||||
|
# Since the timeout period of `VnfmAutoHealTimer` is set to 1 second,
|
||||||
|
# it is also necessary to wait for 1 second before asserting.
|
||||||
|
time.sleep(1)
|
||||||
|
# remove_timer test
|
||||||
|
self.conductor.dequeue_auto_heal_instance(self.context, id)
|
||||||
|
self.assertNotIn(id, self.conductor.prom_driver.timer_map)
|
||||||
|
# remove_timer test: invalid_id
|
||||||
|
self.conductor.dequeue_auto_heal_instance(
|
||||||
|
self.context, 'invalid_id')
|
||||||
|
|
||||||
|
@mock.patch.object(vnflcm_utils, 'heal')
|
||||||
|
def test_conductor_timer_expired(self, mock_do_heal):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
pp_drv.PrometheusPluginDriver._instance = None
|
||||||
|
self.conductor.prom_driver = pp_drv.PrometheusPluginDriver.instance()
|
||||||
|
self.conductor.prom_driver._timer_expired(
|
||||||
|
self.context, 'test_id', ['id'])
|
||||||
|
|
||||||
|
def expired(self, context, id, queue):
|
||||||
|
queue.sort()
|
||||||
|
self.timer_test = (id, queue)
|
||||||
|
|
||||||
|
def test_timer(self):
|
||||||
|
# queueing test
|
||||||
|
timer = pp_drv.VnfmAutoHealTimer(self.context, 'id', 1, self.expired)
|
||||||
|
timer.add_vnfc_info_id('1')
|
||||||
|
timer.add_vnfc_info_id('3')
|
||||||
|
# Since the timeout period of `VnfmAutoHealTimer` is set to 1 second,
|
||||||
|
# it is also necessary to wait for 1 second before asserting.
|
||||||
|
time.sleep(1)
|
||||||
|
self.assertEqual(self.timer_test[0], 'id')
|
||||||
|
self.assertEqual(self.timer_test[1], ['1', '3'])
|
||||||
|
|
||||||
|
def test_timer_cancel(self):
|
||||||
|
# cancel test
|
||||||
|
timer = pp_drv.VnfmAutoHealTimer(self.context, 'id2', 1, self.expired)
|
||||||
|
timer.add_vnfc_info_id('5')
|
||||||
|
timer.cancel()
|
||||||
|
# Since the timeout period of `VnfmAutoHealTimer` is set to 1 second,
|
||||||
|
# it is also necessary to wait for 1 second before asserting.
|
||||||
|
time.sleep(1)
|
||||||
|
self.assertIsNone(self.timer_test[0])
|
||||||
|
self.assertIsNone(self.timer_test[1])
|
||||||
|
|
||||||
|
def test_timer_destructor(self):
|
||||||
|
# method call after cancel()
|
||||||
|
timer = pp_drv.VnfmAutoHealTimer(self.context, 'id', 1, self.expired)
|
||||||
|
timer.cancel()
|
||||||
|
timer.expire()
|
||||||
|
timer.add_vnfc_info_id(['4'])
|
||||||
|
timer.cancel()
|
||||||
|
timer.__del__()
|
||||||
|
@ -145,15 +145,65 @@ _body_scale_alert1 = {
|
|||||||
'fingerprint': '5ef77f1f8a3ecb8d'
|
'fingerprint': '5ef77f1f8a3ecb8d'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_body_heal_alert1 = {
|
||||||
|
'status': 'firing',
|
||||||
|
'labels': {
|
||||||
|
'receiver_type': 'tacker',
|
||||||
|
'function_type': 'auto_heal',
|
||||||
|
'vnf_instance_id': 'vnf instance id',
|
||||||
|
'vnfc_info_id': 'vnfc info id'
|
||||||
|
},
|
||||||
|
'annotations': {
|
||||||
|
},
|
||||||
|
'startsAt': '2022-06-21T23:47:36.453Z',
|
||||||
|
'endsAt': '0001-01-01T00:00:00Z',
|
||||||
|
'generatorURL': 'http://controller147:9090/graph?g0.expr='
|
||||||
|
'up%7Bjob%3D%22node%22%7D+%3D%3D+0&g0.tab=1',
|
||||||
|
'fingerprint': '5ef77f1f8a3ecb8d'
|
||||||
|
}
|
||||||
|
|
||||||
# function_type mismatch
|
# function_type mismatch
|
||||||
_body_scale_alert2 = copy.deepcopy(_body_scale_alert1)
|
_body_scale_alert2 = copy.deepcopy(_body_scale_alert1)
|
||||||
_body_scale_alert2['labels']['function_type'] = 'vnffm'
|
_body_scale_alert2['labels']['function_type'] = 'vnffm'
|
||||||
|
|
||||||
|
_body_scale_alert3 = copy.deepcopy(_body_scale_alert1)
|
||||||
|
_body_scale_alert3['status'] = 'resolved'
|
||||||
|
|
||||||
|
_body_scale_alert4 = copy.deepcopy(_body_scale_alert1)
|
||||||
|
_body_scale_alert4['labels']['function_type'] = 'auto_heal'
|
||||||
|
|
||||||
|
_body_scale_alert5 = copy.deepcopy(_body_scale_alert1)
|
||||||
|
_body_scale_alert5['labels']['aspect_id'] = 'aspect id'
|
||||||
|
|
||||||
_body_scale = copy.deepcopy(_body_base)
|
_body_scale = copy.deepcopy(_body_base)
|
||||||
_body_scale.update({
|
_body_scale.update({
|
||||||
'alerts': [_body_scale_alert1, _body_scale_alert2]
|
'alerts': [_body_scale_alert1, _body_scale_alert2]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
_body_scale_continue = copy.deepcopy(_body_base)
|
||||||
|
_body_scale_continue.update({
|
||||||
|
'alerts': [_body_scale_alert3, _body_scale_alert4, _body_scale_alert5]
|
||||||
|
})
|
||||||
|
|
||||||
|
_body_heal = copy.deepcopy(_body_base)
|
||||||
|
_body_heal.update({
|
||||||
|
'alerts': [_body_heal_alert1]
|
||||||
|
})
|
||||||
|
|
||||||
|
_body_heal_alert2 = copy.deepcopy(_body_heal_alert1)
|
||||||
|
_body_heal_alert2['status'] = 'resolved'
|
||||||
|
|
||||||
|
_body_heal_alert3 = copy.deepcopy(_body_heal_alert1)
|
||||||
|
_body_heal_alert3['labels']['function_type'] = 'auto_scale'
|
||||||
|
|
||||||
|
_body_heal_alert4 = copy.deepcopy(_body_heal_alert1)
|
||||||
|
_body_heal_alert4['labels']['vnfc_info_id'] = 'vnfcInfoId'
|
||||||
|
|
||||||
|
_body_heal_continue = copy.deepcopy(_body_base)
|
||||||
|
_body_heal_continue.update({
|
||||||
|
'alerts': [_body_heal_alert2, _body_heal_alert3, _body_heal_alert4]
|
||||||
|
})
|
||||||
|
|
||||||
_inst1 = {
|
_inst1 = {
|
||||||
'id': 'test_id',
|
'id': 'test_id',
|
||||||
'vnfdId': 'vnfdId',
|
'vnfdId': 'vnfdId',
|
||||||
@ -187,6 +237,14 @@ _inst1 = {
|
|||||||
'vduId': 'vdu_id',
|
'vduId': 'vdu_id',
|
||||||
'vnfcResourceInfoId': 'id2',
|
'vnfcResourceInfoId': 'id2',
|
||||||
'vnfcState': 'STARTED'
|
'vnfcState': 'STARTED'
|
||||||
|
}, {
|
||||||
|
'id': 'vnfc info id',
|
||||||
|
'vduId': 'vdu_id',
|
||||||
|
'vnfcResourceInfoId': 'id2',
|
||||||
|
'vnfcState': 'STARTED'
|
||||||
|
}],
|
||||||
|
'scaleStatus': [{
|
||||||
|
'aspectId': 'aspect'
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
'metadata': {
|
'metadata': {
|
||||||
@ -198,6 +256,31 @@ _inst2.update({
|
|||||||
'vnfConfigurableProperties': {
|
'vnfConfigurableProperties': {
|
||||||
'isAutoscaleEnabled': True
|
'isAutoscaleEnabled': True
|
||||||
},
|
},
|
||||||
|
'instantiationState': 'INSTANTIATED'
|
||||||
|
})
|
||||||
|
|
||||||
|
_inst3 = copy.deepcopy(_inst1)
|
||||||
|
_inst3.update({
|
||||||
|
'vnfConfigurableProperties': {
|
||||||
|
'isAutoscaleEnabled': False
|
||||||
|
},
|
||||||
|
'instantiationState': 'INSTANTIATED'
|
||||||
|
})
|
||||||
|
|
||||||
|
_inst4 = copy.deepcopy(_inst1)
|
||||||
|
_inst4.update({
|
||||||
|
'vnfConfigurableProperties': {
|
||||||
|
'isAutohealEnabled': False
|
||||||
|
},
|
||||||
|
'instantiationState': 'INSTANTIATED'
|
||||||
|
})
|
||||||
|
|
||||||
|
_inst5 = copy.deepcopy(_inst1)
|
||||||
|
_inst5.update({
|
||||||
|
'vnfConfigurableProperties': {
|
||||||
|
'isAutohealEnabled': True
|
||||||
|
},
|
||||||
|
'instantiationState': 'INSTANTIATED'
|
||||||
})
|
})
|
||||||
|
|
||||||
datetime_test = datetime.datetime.fromisoformat(
|
datetime_test = datetime.datetime.fromisoformat(
|
||||||
@ -324,6 +407,64 @@ class TestPrometheusPluginFm(base.TestCase):
|
|||||||
self.assertEqual(204, result.status)
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPrometheusPluginAutoHealing(base.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestPrometheusPluginAutoHealing, self).setUp()
|
||||||
|
objects.register_all()
|
||||||
|
self.context = context.get_admin_context()
|
||||||
|
self.request = mock.Mock()
|
||||||
|
self.request.context = self.context
|
||||||
|
self.controller = prometheus_plugin_controller.AutoHealingController()
|
||||||
|
plugin.PrometheusPluginAutoHealing._instance = None
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(TestPrometheusPluginAutoHealing, self).tearDown()
|
||||||
|
# delete singleton object
|
||||||
|
plugin.PrometheusPluginAutoHealing._instance = None
|
||||||
|
|
||||||
|
def test_auto_healing_config_false(self):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=False)
|
||||||
|
self.assertRaises(
|
||||||
|
sol_ex.PrometheusPluginNotEnabled,
|
||||||
|
self.controller.auto_healing, self.request, {})
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_healing_no_autoheal_enabled(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst4)
|
||||||
|
result = self.controller.auto_healing(
|
||||||
|
self.request, _body_heal)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_healing_is_autoheal_enabled(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst5)
|
||||||
|
result = self.controller.auto_healing(self.request, _body_heal)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_healing_multiple_continue(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst5)
|
||||||
|
result = self.controller.auto_healing(
|
||||||
|
self.request, _body_heal_continue)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_healing_not_instantiated(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_healing=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||||
|
result = self.controller.auto_healing(
|
||||||
|
self.request, _body_heal)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
|
||||||
class TestPrometheusPluginAutoScaling(base.TestCase):
|
class TestPrometheusPluginAutoScaling(base.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestPrometheusPluginAutoScaling, self).setUp()
|
super(TestPrometheusPluginAutoScaling, self).setUp()
|
||||||
@ -344,15 +485,15 @@ class TestPrometheusPluginAutoScaling(base.TestCase):
|
|||||||
group='prometheus_plugin', auto_scaling=False)
|
group='prometheus_plugin', auto_scaling=False)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
sol_ex.PrometheusPluginNotEnabled,
|
sol_ex.PrometheusPluginNotEnabled,
|
||||||
self.controller.auto_scaling_id, self.request, 'id', {})
|
self.controller.auto_scaling, self.request, {})
|
||||||
|
|
||||||
@mock.patch.object(inst_utils, 'get_inst')
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
def test_auto_scaling_no_autoscale_enabled(self, mock_inst):
|
def test_auto_scaling_no_autoscale_enabled(self, mock_inst):
|
||||||
self.config_fixture.config(
|
self.config_fixture.config(
|
||||||
group='prometheus_plugin', auto_scaling=True)
|
group='prometheus_plugin', auto_scaling=True)
|
||||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst3)
|
||||||
result = self.controller.auto_scaling_id(
|
result = self.controller.auto_scaling(
|
||||||
self.request, 'id', _body_scale)
|
self.request, _body_scale)
|
||||||
self.assertEqual(204, result.status)
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
@mock.patch.object(inst_utils, 'get_inst')
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
@ -378,3 +519,21 @@ class TestPrometheusPluginAutoScaling(base.TestCase):
|
|||||||
group='prometheus_plugin', auto_scaling=True)
|
group='prometheus_plugin', auto_scaling=True)
|
||||||
result = self.controller.auto_scaling(self.request, {})
|
result = self.controller.auto_scaling(self.request, {})
|
||||||
self.assertEqual(204, result.status)
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_scaling_multiple_continue(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_scaling=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst2)
|
||||||
|
result = self.controller.auto_scaling(
|
||||||
|
self.request, _body_scale_continue)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
|
||||||
|
@mock.patch.object(inst_utils, 'get_inst')
|
||||||
|
def test_auto_scaling_not_instantiated(self, mock_inst):
|
||||||
|
self.config_fixture.config(
|
||||||
|
group='prometheus_plugin', auto_scaling=True)
|
||||||
|
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||||
|
result = self.controller.auto_scaling(
|
||||||
|
self.request, _body_scale)
|
||||||
|
self.assertEqual(204, result.status)
|
||||||
|
Loading…
Reference in New Issue
Block a user