Support CNF auto scale via PM Threshold interface
Add the Performance Management Threshold API and CLI to support AutoScale. The Performance Management Threshold API is based on ETSI NFV-SOL 002 v3.3.1 and ETSI NFV-SOL 003 v3.3.1, on which v2 VNF LCM API of Tacker is based. Add the Prometheus Plugin that has an interface between Tacker and Prometheus as a sample of External Monitoring Tool. Implements: blueprint support-auto-lcm Change-Id: If41e89c5601592aea18a81df54d0db44a8588e1c
This commit is contained in:
parent
c65931ceab
commit
f75827612f
|
@ -12,6 +12,7 @@ use = egg:Paste#urlmap
|
|||
/alert/auto_healing: prometheus_auto_healing
|
||||
/alert: prometheus_fm
|
||||
/pm_event: prometheus_pm
|
||||
/pm_threshold: prometheus_threshold
|
||||
/server_notification: server_notification
|
||||
|
||||
[composite:tackerapi_v1_0]
|
||||
|
@ -103,5 +104,8 @@ paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:FmAlertRo
|
|||
[app:prometheus_pm]
|
||||
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:PmEventRouter.factory
|
||||
|
||||
[app:prometheus_threshold]
|
||||
paste.app_factory = tacker.sol_refactored.api.prometheus_plugin_router:PmThresholdRouter.factory
|
||||
|
||||
[app:server_notification]
|
||||
paste.app_factory = tacker.sol_refactored.api.server_notification_router:ServerNotificationRouter.factory
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
---
|
||||
features:
|
||||
- |
|
||||
Add the Performance Management Threshold interfaces and CLI to support
|
||||
AutoScale.
|
||||
The Performance Management Threshold interfaces are based on ETSI NFV-SOL
|
||||
003 v3.3.1, on which v2 VNF LCM API of Tacker is based.
|
||||
Add the Prometheus Plugin that has an interface between Tacker and the
|
||||
External Monitoring Tool.
|
||||
Prometheus Plugin supports data model conversion from Prometheus format
|
||||
data to SOL based PM Threshold schema, and vice versa.
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2023 OpenStack Foundation
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""add_pm_threshold_table
|
||||
|
||||
Revision ID: 2531c976c0f1
|
||||
Revises: de6bfa5bea46
|
||||
Create Date: 2023-02-01 07:10:06.910825
|
||||
|
||||
"""
|
||||
|
||||
# flake8: noqa: E402
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '2531c976c0f1'
|
||||
down_revision = 'de6bfa5bea46'
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
def upgrade(active_plugins=None, options=None):
|
||||
op.create_table('ThresholdV2',
|
||||
sa.Column('id', sa.String(length=255), nullable=False),
|
||||
sa.Column('objectType', sa.String(length=32), nullable=False),
|
||||
sa.Column('objectInstanceId', sa.String(length=255), nullable=False),
|
||||
sa.Column('subObjectInstanceIds', sa.JSON(), nullable=True),
|
||||
sa.Column('criteria', sa.JSON(), nullable=False),
|
||||
sa.Column('callbackUri', sa.String(length=255), nullable=False),
|
||||
sa.Column('authentication', sa.JSON(), nullable=True),
|
||||
sa.Column('metadata', sa.JSON(), nullable=True),
|
||||
sa.PrimaryKeyConstraint('id'),
|
||||
mysql_engine='InnoDB'
|
||||
)
|
|
@ -1 +1 @@
|
|||
de6bfa5bea46
|
||||
2531c976c0f1
|
||||
|
|
|
@ -1,133 +1,205 @@
|
|||
# Copyright (C) 2022 Fujitsu
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
|
||||
POLICY_NAME = 'os_nfv_orchestration_api_v2:vnf_performance_management:{}'
|
||||
RULE_ANY = '@'
|
||||
|
||||
V2_PATH = '/vnfpm/v2'
|
||||
PM_JOB_PATH = V2_PATH + '/pm_jobs'
|
||||
PM_JOB_ID_PATH = PM_JOB_PATH + '/{pmJobId}'
|
||||
REPORT_GET = '/vnfpm/v2/pm_jobs/{id}/reports/{report_id}'
|
||||
|
||||
POLICY_NAME_PROM_PLUGIN = 'tacker_PROM_PLUGIN_api:PROM_PLUGIN:{}'
|
||||
PROM_PLUGIN_PM_PATH = '/pm_event'
|
||||
PROM_PLUGIN_AUTO_HEALING_PATH = '/alert/auto_healing'
|
||||
PROM_PLUGIN_AUTO_SCALING_PATH = '/alert/auto_scaling'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('create'),
|
||||
check_str=RULE_ANY,
|
||||
description="Create a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': PM_JOB_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('index'),
|
||||
check_str=RULE_ANY,
|
||||
description="Query PM jobs.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_JOB_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('update'),
|
||||
check_str=RULE_ANY,
|
||||
description="Update a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('show'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get an individual PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('delete'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
# Add new Rest API GET /vnfpm/v2/pm_jobs/{id}/reports/{report_id} to
|
||||
# get the specified PM report.
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('report_get'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get an individual performance report.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': REPORT_GET
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME_PROM_PLUGIN.format('pm_event'),
|
||||
check_str=RULE_ANY,
|
||||
description="Receive the PM event sent from External Monitoring Tool",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'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(
|
||||
name=POLICY_NAME_PROM_PLUGIN.format('auto_scaling'),
|
||||
check_str=RULE_ANY,
|
||||
description="auto_scaling",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': PROM_PLUGIN_AUTO_SCALING_PATH}
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules
|
||||
# Copyright (C) 2022 Fujitsu
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from oslo_policy import policy
|
||||
|
||||
POLICY_NAME = 'os_nfv_orchestration_api_v2:vnf_performance_management:{}'
|
||||
RULE_ANY = '@'
|
||||
|
||||
V2_PATH = '/vnfpm/v2'
|
||||
PM_JOB_PATH = V2_PATH + '/pm_jobs'
|
||||
PM_THRESHOLD_PATH = V2_PATH + '/thresholds'
|
||||
PM_JOB_ID_PATH = PM_JOB_PATH + '/{pmJobId}'
|
||||
PM_THRESHOLD_ID_PATH = PM_THRESHOLD_PATH + '/{thresholdId}'
|
||||
REPORT_GET = '/vnfpm/v2/pm_jobs/{id}/reports/{report_id}'
|
||||
|
||||
POLICY_NAME_PROM_PLUGIN = 'tacker_PROM_PLUGIN_api:PROM_PLUGIN:{}'
|
||||
PROM_PLUGIN_PM_PATH = '/pm_event'
|
||||
PROM_PLUGIN_PM_THRESHOLD_PATH = '/pm_threshold'
|
||||
PROM_PLUGIN_AUTO_HEALING_PATH = '/alert/auto_healing'
|
||||
PROM_PLUGIN_AUTO_SCALING_PATH = '/alert/auto_scaling'
|
||||
|
||||
rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('create'),
|
||||
check_str=RULE_ANY,
|
||||
description="Create a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': PM_JOB_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('index'),
|
||||
check_str=RULE_ANY,
|
||||
description="Query PM jobs.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_JOB_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('update'),
|
||||
check_str=RULE_ANY,
|
||||
description="Update a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('show'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get an individual PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('delete'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete a PM job.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': PM_JOB_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
# Add new Rest API GET /vnfpm/v2/pm_jobs/{id}/reports/{report_id} to
|
||||
# get the specified PM report.
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('report_get'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get an individual performance report.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': REPORT_GET
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME_PROM_PLUGIN.format('pm_event'),
|
||||
check_str=RULE_ANY,
|
||||
description="Receive the PM event sent from External Monitoring Tool",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'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(
|
||||
name=POLICY_NAME_PROM_PLUGIN.format('auto_scaling'),
|
||||
check_str=RULE_ANY,
|
||||
description="auto_scaling",
|
||||
operations=[
|
||||
{'method': 'POST',
|
||||
'path': PROM_PLUGIN_AUTO_SCALING_PATH}
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
threshold_rules = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('create_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Create a PM threshold.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': PM_THRESHOLD_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('index_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Query PM thresholds.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_THRESHOLD_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('show_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Get an individual PM threshold.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': PM_THRESHOLD_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('update_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Update a PM threshold callback.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'PATCH',
|
||||
'path': PM_THRESHOLD_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME.format('delete_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Delete a PM threshold.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'DELETE',
|
||||
'path': PM_THRESHOLD_ID_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_NAME_PROM_PLUGIN.format('pm_threshold'),
|
||||
check_str=RULE_ANY,
|
||||
description="Receive the PM threshold sent from "
|
||||
"External Monitoring Tool.",
|
||||
operations=[
|
||||
{
|
||||
'method': 'POST',
|
||||
'path': PROM_PLUGIN_PM_THRESHOLD_PATH
|
||||
}
|
||||
]
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def list_rules():
|
||||
return rules + threshold_rules
|
||||
|
|
|
@ -26,6 +26,13 @@ class PmEventRouter(prom_wsgi.PrometheusPluginAPIRouter):
|
|||
route_list = [("", {"POST": "pm_event"})]
|
||||
|
||||
|
||||
class PmThresholdRouter(prom_wsgi.PrometheusPluginAPIRouter):
|
||||
controller = prom_wsgi.PrometheusPluginResource(
|
||||
prometheus_plugin_controller.PmThresholdController(),
|
||||
policy_name=vnfpm_policy_v2.POLICY_NAME_PROM_PLUGIN)
|
||||
route_list = [("", {"POST": "pm_threshold"})]
|
||||
|
||||
|
||||
class FmAlertRouter(prom_wsgi.PrometheusPluginAPIRouter):
|
||||
controller = prom_wsgi.PrometheusPluginResource(
|
||||
prometheus_plugin_controller.FmAlertController(),
|
||||
|
|
|
@ -84,4 +84,9 @@ class VnfPmAPIRouterV2(sol_wsgi.SolAPIRouter):
|
|||
("/pm_jobs/{id}", {
|
||||
"PATCH": "update", "GET": "show", "DELETE": "delete"}),
|
||||
("/pm_jobs/{id}/reports/{report_id}", {"GET": "report_get"}),
|
||||
("/thresholds", {"POST": "create_threshold",
|
||||
"GET": "index_threshold"}),
|
||||
("/thresholds/{thresholdId}", {"PATCH": "update_threshold",
|
||||
"GET": "show_threshold",
|
||||
"DELETE": "delete_threshold"}),
|
||||
]
|
||||
|
|
|
@ -34,9 +34,12 @@ Alert = {
|
|||
},
|
||||
'function_type': {
|
||||
'type': 'string',
|
||||
'enum': ['vnffm', 'vnfpm', 'auto_scale', 'auto_heal']
|
||||
'enum': ['vnffm', 'vnfpm', 'vnfpm_threshold',
|
||||
'auto_scale', 'auto_heal']
|
||||
},
|
||||
'job_id': {'type': 'string'},
|
||||
'threshold_id': {'type': 'string'},
|
||||
'metric': {'type': 'string'},
|
||||
'object_instance_id': {'type': 'string'},
|
||||
'vnf_instance_id': {'type': 'string'},
|
||||
'vnfc_info_id': {'type': 'string'},
|
||||
|
|
|
@ -84,3 +84,71 @@ PmJobModificationsRequest_V210 = {
|
|||
'required': [],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 6.5.3.4
|
||||
ThresholdCriteria_V210 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'performanceMetric': {'type': 'string'},
|
||||
'thresholdType': {
|
||||
'type': 'string',
|
||||
'enum': ['SIMPLE']
|
||||
},
|
||||
'simpleThresholdDetails': {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'thresholdValue': {'type': 'number'},
|
||||
'hysteresis': {'type': 'number', 'minimum': 0.0},
|
||||
},
|
||||
'required': ['thresholdValue', 'hysteresis'],
|
||||
'additionalProperties': True
|
||||
},
|
||||
},
|
||||
'required': ['performanceMetric', 'thresholdType'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 6.5.2.8
|
||||
CreateThresholdRequest_V210 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'objectType': {
|
||||
'type': 'string',
|
||||
'enum': [
|
||||
# TODO(YiFeng): Currently, this API only supports CNF, and
|
||||
# supports the following types. When VNF is supported,
|
||||
# the types can be extended.
|
||||
'Vnf',
|
||||
'Vnfc',
|
||||
'VnfIntCp',
|
||||
'VnfExtCp']
|
||||
},
|
||||
'objectInstanceId': {
|
||||
'type': 'string',
|
||||
},
|
||||
'subObjectInstanceIds': {
|
||||
'type': 'array',
|
||||
'items': common_types.IdentifierInVnf
|
||||
},
|
||||
'criteria': ThresholdCriteria_V210,
|
||||
'callbackUri': {'type': 'string'},
|
||||
'authentication': common_types.SubscriptionAuthentication,
|
||||
},
|
||||
'required': ['objectType', 'objectInstanceId', 'criteria', 'callbackUri'],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
||||
# SOL003 6.5.2.11
|
||||
ThresholdModifications_V210 = {
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'callbackUri': {'type': 'string'},
|
||||
'authentication': common_types.SubscriptionAuthentication
|
||||
},
|
||||
'anyOf': [
|
||||
{'required': ['callbackUri']},
|
||||
{'required': ['authentication']}
|
||||
],
|
||||
'required': [],
|
||||
'additionalProperties': True,
|
||||
}
|
||||
|
|
|
@ -13,7 +13,31 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import threading
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import subscription_utils
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
CONF = config.CONF
|
||||
|
||||
TEST_NOTIFICATION_TIMEOUT = 20 # seconds
|
||||
NOTIFY_TYPE_PM = 'PM'
|
||||
NOTIFY_TYPE_FM = 'FM'
|
||||
AUTH_TYPE_OAUTH2_CLIENT_CERT = 'OAUTH2_CLIENT_CERT'
|
||||
AUTH_TYPE_OAUTH2_CLIENT_CREDENTIALS = 'OAUTH2_CLIENT_CREDENTIALS'
|
||||
AUTH_TYPE_BASIC = 'BASIC'
|
||||
|
||||
|
||||
def get_vnfd(vnfd_id, csar_dir):
|
||||
|
@ -273,3 +297,107 @@ def apply_ext_managed_vls_from_inst(hot_dict, inst):
|
|||
for mgd_vl in mgd_vls
|
||||
]
|
||||
_apply_ext_managed_vls(hot_dict, mgd_vls)
|
||||
|
||||
|
||||
def get_notification_auth_handle(obj_data):
|
||||
auth_req = obj_data.get('authentication', None)
|
||||
if auth_req:
|
||||
auth = objects.SubscriptionAuthentication(
|
||||
authType=auth_req['authType']
|
||||
)
|
||||
if AUTH_TYPE_OAUTH2_CLIENT_CERT in auth.authType:
|
||||
param = obj_data.authentication.paramsOauth2ClientCert
|
||||
ca_cert = CONF.v2_vnfm.notification_mtls_ca_cert_file
|
||||
client_cert = CONF.v2_vnfm.notification_mtls_client_cert_file
|
||||
return http_client.OAuth2MtlsAuthHandle(
|
||||
None, param.tokenEndpoint,
|
||||
param.clientId, ca_cert, client_cert)
|
||||
elif AUTH_TYPE_OAUTH2_CLIENT_CREDENTIALS in auth.authType:
|
||||
param = obj_data.authentication.paramsOauth2ClientCredentials
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.OAuth2AuthHandle(
|
||||
None, param.tokenEndpoint, param.clientId,
|
||||
param.clientPassword,
|
||||
verify=verify)
|
||||
elif AUTH_TYPE_BASIC in auth.authType:
|
||||
param = obj_data.authentication.paramsBasic
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.BasicAuthHandle(
|
||||
param.userName, param.password,
|
||||
verify=verify)
|
||||
else:
|
||||
raise sol_ex.AuthTypeNotFound(auth.authType)
|
||||
else:
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.NoAuthHandle(verify=verify)
|
||||
|
||||
|
||||
def async_call(func):
|
||||
def inner(*args, **kwargs):
|
||||
th = threading.Thread(target=func, args=args,
|
||||
kwargs=kwargs, daemon=True)
|
||||
th.start()
|
||||
return inner
|
||||
|
||||
|
||||
@async_call
|
||||
def send_notification(obj_data, notif_data, notify_type=None):
|
||||
version = api_version.CURRENT_VERSION
|
||||
auth_handle = subscription_utils.get_notification_auth_handle(obj_data)
|
||||
connect_retries = (CONF.v2_vnfm.notify_connect_retries
|
||||
if CONF.v2_vnfm.notify_connect_retries else None)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=version,
|
||||
connect_retries=connect_retries)
|
||||
if notify_type == NOTIFY_TYPE_PM:
|
||||
version = api_version.CURRENT_PM_VERSION
|
||||
auth_handle = get_notification_auth_handle(obj_data)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=version)
|
||||
if notify_type == NOTIFY_TYPE_FM:
|
||||
version = api_version.CURRENT_FM_VERSION
|
||||
auth_handle = get_notification_auth_handle(obj_data)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=version)
|
||||
|
||||
url = obj_data.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(
|
||||
url, "POST", expected_status=[204], body=notif_data)
|
||||
except sol_ex.SolException as ex:
|
||||
# it may occur if test_notification was not executed.
|
||||
LOG.exception(f"send_notification failed: {ex}")
|
||||
|
||||
if resp.status_code != 204:
|
||||
LOG.error(f"send_notification failed: {resp.__dict__}")
|
||||
|
||||
|
||||
def test_notification(obj_data, notify_type=None):
|
||||
version = api_version.CURRENT_VERSION
|
||||
auth_handle = subscription_utils.get_notification_auth_handle(obj_data)
|
||||
if notify_type == NOTIFY_TYPE_PM:
|
||||
version = api_version.CURRENT_PM_VERSION
|
||||
auth_handle = get_notification_auth_handle(obj_data)
|
||||
if notify_type == NOTIFY_TYPE_FM:
|
||||
version = api_version.CURRENT_FM_VERSION
|
||||
auth_handle = get_notification_auth_handle(obj_data)
|
||||
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=version,
|
||||
timeout=TEST_NOTIFICATION_TIMEOUT)
|
||||
|
||||
url = obj_data.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(url, "GET", expected_status=[204])
|
||||
except sol_ex.SolException as e:
|
||||
# any notify_type of error is considered. avoid 500 error.
|
||||
raise sol_ex.TestNotificationFailed() from e
|
||||
|
||||
if resp.status_code != 204:
|
||||
raise sol_ex.TestNotificationFailed()
|
||||
|
|
|
@ -68,6 +68,10 @@ VNFM_OPTS = [
|
|||
default=0, # 0 means no paging
|
||||
help=_('Paged response size of the query result '
|
||||
'for VNF Fault Management alarm.')),
|
||||
cfg.IntOpt('vnfpm_pmthreshold_page_size',
|
||||
default=0, # 0 means no paging
|
||||
help=_('Paged response size of the query result for '
|
||||
'VNF PM threshold.')),
|
||||
cfg.IntOpt('vnfpm_pmjob_page_size',
|
||||
default=0, # 0 means no paging
|
||||
help=_('Paged response size of the query result for '
|
||||
|
@ -206,13 +210,25 @@ PROMETHEUS_PLUGIN_OPTS = [
|
|||
help=_('Enable prometheus plugin autoscaling')),
|
||||
cfg.StrOpt('performance_management_package',
|
||||
default='tacker.sol_refactored.common.prometheus_plugin',
|
||||
help=_('Package name for performance management. '
|
||||
help=_('Package name for performance management PMJob. '
|
||||
'This configuration is changed in case of replacing '
|
||||
'the original function with a vendor specific '
|
||||
'function.')),
|
||||
cfg.StrOpt('performance_management_threshold_package',
|
||||
default='tacker.sol_refactored.common.prometheus_plugin',
|
||||
help=_('Package name for performance management threshold. '
|
||||
'This configuration is changed in case of replacing '
|
||||
'the original function with a vendor specific '
|
||||
'function.')),
|
||||
cfg.StrOpt('performance_management_class',
|
||||
default='PrometheusPluginPm',
|
||||
help=_('Class name for performance management. '
|
||||
help=_('Class name for performance management PMJob. '
|
||||
'This configuration is changed in case of replacing '
|
||||
'the original function with a vendor specific '
|
||||
'function.')),
|
||||
cfg.StrOpt('performance_management_threshold_class',
|
||||
default='PrometheusPluginThreshold',
|
||||
help=_('Class name for performance management threshold. '
|
||||
'This configuration is changed in case of replacing '
|
||||
'the original function with a vendor specific '
|
||||
'function.')),
|
||||
|
@ -259,6 +275,20 @@ PROMETHEUS_PLUGIN_OPTS = [
|
|||
cfg.BoolOpt('test_rule_with_promtool',
|
||||
default=False,
|
||||
help=_('Enable rule file validation using promtool.')),
|
||||
cfg.IntOpt('reporting_period_threshold',
|
||||
default=90,
|
||||
help=_('The time of reportingPeriod for the PM Threshold. '
|
||||
'If there is a PromQL '
|
||||
'that requires `reporting_period`, '
|
||||
'it is read from the configuration file. '
|
||||
'The unit shall be seconds.')),
|
||||
cfg.IntOpt('collection_period_threshold',
|
||||
default=30,
|
||||
help=_('The time of collectionPeriod for the PM threshold. '
|
||||
'If there is a PromQL '
|
||||
'that requires `collection_period`, '
|
||||
'it is read from the configuration file. '
|
||||
'The unit shall be seconds.')),
|
||||
]
|
||||
|
||||
CONF.register_opts(PROMETHEUS_PLUGIN_OPTS, 'prometheus_plugin')
|
||||
|
|
|
@ -408,6 +408,10 @@ class PMJobNotExist(SolHttpError404):
|
|||
message = _("The specified PM job does not exist.")
|
||||
|
||||
|
||||
class PMThresholdNotExist(SolHttpError404):
|
||||
message = _("The specified PM Threshold %(threshold_id)s does not exist.")
|
||||
|
||||
|
||||
class PMReportNotExist(SolHttpError404):
|
||||
message = _("The specified Performance Report does not exist.")
|
||||
|
||||
|
@ -416,6 +420,10 @@ class PMJobInvalidRequest(SolHttpError400):
|
|||
message = _("Invalid request")
|
||||
|
||||
|
||||
class PMThresholdInvalidRequest(SolHttpError400):
|
||||
message = _("Invalid request")
|
||||
|
||||
|
||||
class ResourcesOtherOperationInProgress(SolHttpError409):
|
||||
message = _("Other LCM operation of resources %(inst_id)s "
|
||||
"is in progress.")
|
||||
|
|
|
@ -14,14 +14,10 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
import threading
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
|
@ -30,8 +26,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
CONF = config.CONF
|
||||
|
||||
TEST_NOTIFICATION_TIMEOUT = 20 # seconds
|
||||
|
||||
|
||||
def get_subsc(context, subsc_id):
|
||||
subsc = objects.FmSubscriptionV1.get_by_id(context, subsc_id)
|
||||
|
@ -48,84 +42,6 @@ def subsc_href(subsc_id, endpoint):
|
|||
return f"{endpoint}/vnffm/v1/subscriptions/{subsc_id}"
|
||||
|
||||
|
||||
def _get_notification_auth_handle(subsc):
|
||||
if not subsc.obj_attr_is_set('authentication'):
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.NoAuthHandle(verify=verify)
|
||||
|
||||
if subsc.authentication.obj_attr_is_set('paramsBasic'):
|
||||
param = subsc.authentication.paramsBasic
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.BasicAuthHandle(
|
||||
param.userName, param.password, verify=verify)
|
||||
|
||||
if subsc.authentication.obj_attr_is_set(
|
||||
'paramsOauth2ClientCredentials'):
|
||||
param = subsc.authentication.paramsOauth2ClientCredentials
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.OAuth2AuthHandle(None,
|
||||
param.tokenEndpoint, param.clientId, param.clientPassword,
|
||||
verify=verify)
|
||||
|
||||
if subsc.authentication.obj_attr_is_set('paramsOauth2ClientCert'):
|
||||
param = subsc.authentication.paramsOauth2ClientCert
|
||||
ca_cert = CONF.v2_vnfm.notification_mtls_ca_cert_file
|
||||
client_cert = CONF.v2_vnfm.notification_mtls_client_cert_file
|
||||
return http_client.OAuth2MtlsAuthHandle(None,
|
||||
param.tokenEndpoint, param.clientId, ca_cert, client_cert)
|
||||
|
||||
# not reach here
|
||||
|
||||
|
||||
def async_call(func):
|
||||
def inner(*args, **kwargs):
|
||||
th = threading.Thread(target=func, args=args,
|
||||
kwargs=kwargs, daemon=True)
|
||||
th.start()
|
||||
return inner
|
||||
|
||||
|
||||
@async_call
|
||||
def send_notification(subsc, notif_data):
|
||||
auth_handle = _get_notification_auth_handle(subsc)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_FM_VERSION)
|
||||
|
||||
url = subsc.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(
|
||||
url, "POST", expected_status=[204], body=notif_data)
|
||||
except sol_ex.SolException:
|
||||
# it may occur if test_notification was not executed.
|
||||
LOG.exception("send_notification failed")
|
||||
|
||||
if resp.status_code != 204:
|
||||
LOG.error(f"send_notification failed: {resp.status_code}")
|
||||
|
||||
|
||||
def test_notification(subsc):
|
||||
auth_handle = _get_notification_auth_handle(subsc)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_FM_VERSION,
|
||||
timeout=TEST_NOTIFICATION_TIMEOUT)
|
||||
|
||||
url = subsc.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(url, "GET", expected_status=[204])
|
||||
except sol_ex.SolException as e:
|
||||
# any sort of error is considered. avoid 500 error.
|
||||
raise sol_ex.TestNotificationFailed() from e
|
||||
|
||||
if resp.status_code != 204:
|
||||
raise sol_ex.TestNotificationFailed()
|
||||
|
||||
|
||||
def get_matched_subscs(context, inst, notif_type, alarm):
|
||||
subscs = []
|
||||
for subsc in get_subsc_all(context):
|
||||
|
|
|
@ -41,9 +41,15 @@ class MonitoringPlugin():
|
|||
def create_job(self, **kwargs):
|
||||
pass
|
||||
|
||||
def create_threshold(self, **kwargs):
|
||||
pass
|
||||
|
||||
def delete_job(self, **kwargs):
|
||||
pass
|
||||
|
||||
def delete_threshold(self, **kwargs):
|
||||
pass
|
||||
|
||||
def alert(self, **kwargs):
|
||||
pass
|
||||
|
||||
|
|
|
@ -13,21 +13,17 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import threading
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = config.CONF
|
||||
TEST_NOTIFICATION_TIMEOUT = 20 # seconds
|
||||
|
||||
|
||||
def update_report(context, job_id, report, timestamp):
|
||||
|
@ -92,54 +88,6 @@ def make_pm_job_links(pm_job, endpoint):
|
|||
return links
|
||||
|
||||
|
||||
def _get_notification_auth_handle(pm_job):
|
||||
if not pm_job.obj_attr_is_set('authentication'):
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.NoAuthHandle(verify=verify)
|
||||
if pm_job.authentication.obj_attr_is_set('paramsBasic'):
|
||||
param = pm_job.authentication.paramsBasic
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.BasicAuthHandle(param.userName, param.password,
|
||||
verify=verify)
|
||||
if pm_job.authentication.obj_attr_is_set(
|
||||
'paramsOauth2ClientCredentials'):
|
||||
param = pm_job.authentication.paramsOauth2ClientCredentials
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.OAuth2AuthHandle(
|
||||
None, param.tokenEndpoint, param.clientId, param.clientPassword,
|
||||
verify=verify)
|
||||
if pm_job.authentication.obj_attr_is_set('paramsOauth2ClientCert'):
|
||||
param = pm_job.authentication.paramsOauth2ClientCert
|
||||
ca_cert = CONF.v2_vnfm.notification_mtls_ca_cert_file
|
||||
client_cert = CONF.v2_vnfm.notification_mtls_client_cert_file
|
||||
return http_client.OAuth2MtlsAuthHandle(None,
|
||||
param.tokenEndpoint, param.clientId, ca_cert, client_cert)
|
||||
return None
|
||||
|
||||
|
||||
def test_notification(pm_job):
|
||||
auth_handle = _get_notification_auth_handle(pm_job)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_PM_VERSION,
|
||||
timeout=TEST_NOTIFICATION_TIMEOUT)
|
||||
|
||||
url = pm_job.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(url, "GET", expected_status=[204])
|
||||
except sol_ex.SolException as e:
|
||||
# any sort of error is considered. avoid 500 error.
|
||||
raise sol_ex.TestNotificationFailed() from e
|
||||
|
||||
if resp.status_code != 204:
|
||||
raise sol_ex.TestNotificationFailed()
|
||||
|
||||
|
||||
def make_pm_notif_data(instance_id, sub_instance_ids, report_id,
|
||||
pm_job, timestamp, endpoint):
|
||||
notif_data = objects.PerformanceInformationAvailableNotificationV2(
|
||||
|
@ -163,30 +111,3 @@ def make_pm_notif_data(instance_id, sub_instance_ids, report_id,
|
|||
if sub_instance_ids:
|
||||
notif_data.subObjectInstanceIds = sub_instance_ids
|
||||
return notif_data
|
||||
|
||||
|
||||
def async_call(func):
|
||||
def inner(*args, **kwargs):
|
||||
th = threading.Thread(target=func, args=args,
|
||||
kwargs=kwargs, daemon=True)
|
||||
th.start()
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
@async_call
|
||||
def send_notification(pm_job, notif_data):
|
||||
auth_handle = _get_notification_auth_handle(pm_job)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_PM_VERSION)
|
||||
|
||||
url = pm_job.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(
|
||||
url, "POST", expected_status=[204], body=notif_data)
|
||||
except sol_ex.SolException:
|
||||
# it may occur if test_notification was not executed.
|
||||
LOG.exception("send_notification failed")
|
||||
|
||||
if resp.status_code != 204:
|
||||
LOG.error(f'send_notification failed: {resp.status_code}')
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
# 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 oslo_log import log as logging
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import coordinate
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
CONF = config.CONF
|
||||
|
||||
|
||||
def get_pm_threshold_all(context, marker=None):
|
||||
return objects.ThresholdV2.get_all(context, marker)
|
||||
|
||||
|
||||
def get_pm_threshold(context, pm_threshold_id):
|
||||
return objects.ThresholdV2.get_by_id(context, pm_threshold_id)
|
||||
|
||||
|
||||
def get_pm_threshold_state(pm_threshold, sub_object_instance_id):
|
||||
if (not pm_threshold.obj_attr_is_set('metadata') or
|
||||
not pm_threshold.metadata.get('thresholdState')):
|
||||
return None
|
||||
|
||||
pm_threshold_states = pm_threshold.metadata['thresholdState']
|
||||
for pm_threshold_state in pm_threshold_states:
|
||||
if (pm_threshold_state.get('subObjectInstanceId') ==
|
||||
sub_object_instance_id):
|
||||
return pm_threshold_state
|
||||
|
||||
|
||||
def pm_threshold_href(threshold_id, endpoint):
|
||||
return f"{endpoint}/vnfpm/v2/thresholds/{threshold_id}"
|
||||
|
||||
|
||||
def make_pm_threshold_links(threshold, endpoint):
|
||||
links = objects.ThresholdV2_Links()
|
||||
links.self = objects.Link(href=pm_threshold_href(threshold.id, endpoint))
|
||||
links.object = objects.Link(
|
||||
href=inst_utils.inst_href(threshold.objectInstanceId, endpoint))
|
||||
return links
|
||||
|
||||
|
||||
@coordinate.lock_resources('{threshold_id}')
|
||||
def update_threshold_state_data(context, threshold_id,
|
||||
threshold_state_data):
|
||||
pm_threshold = get_pm_threshold(context, threshold_id)
|
||||
if not pm_threshold:
|
||||
raise sol_ex.PMThresholdNotExist(threshold_id=threshold_id)
|
||||
if pm_threshold.obj_attr_is_set('metadata'):
|
||||
if 'thresholdState' not in pm_threshold.metadata.keys():
|
||||
pm_threshold.metadata.update({
|
||||
'thresholdState': [threshold_state_data]})
|
||||
else:
|
||||
is_exist = False
|
||||
for threshold_state in pm_threshold.metadata['thresholdState']:
|
||||
if (threshold_state.get('subObjectInstanceId') ==
|
||||
threshold_state_data['subObjectInstanceId'] and
|
||||
threshold_state.get('metrics') ==
|
||||
threshold_state_data['metrics']):
|
||||
threshold_state.update(threshold_state_data)
|
||||
is_exist = True
|
||||
break
|
||||
if not is_exist:
|
||||
pm_threshold.metadata['thresholdState'].append(
|
||||
threshold_state_data)
|
||||
else:
|
||||
pm_threshold.metadata = {
|
||||
'thresholdState': [threshold_state_data]}
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
pm_threshold.update(context)
|
||||
|
||||
|
||||
def make_threshold_notif_data(timestamp, threshold_state,
|
||||
endpoint, pm_threshold):
|
||||
|
||||
notif_data = objects.ThresholdCrossedNotificationV2(
|
||||
id=uuidutils.generate_uuid(),
|
||||
notificationType="ThresholdCrossedNotification",
|
||||
timeStamp=timestamp,
|
||||
thresholdId=pm_threshold.id,
|
||||
crossingDirection=threshold_state['crossingDirection'],
|
||||
objectType=pm_threshold.objectType,
|
||||
objectInstanceId=pm_threshold.objectInstanceId,
|
||||
subObjectInstanceId=threshold_state['subObjectInstanceId'],
|
||||
performanceMetric=threshold_state['metrics'],
|
||||
performanceValue=threshold_state['performanceValue'],
|
||||
_links=objects.ThresholdCrossedNotificationV2_Links(
|
||||
objectInstance=objects.NotificationLink(
|
||||
href=inst_utils.inst_href(
|
||||
pm_threshold.objectInstanceId, endpoint)
|
||||
),
|
||||
threshold=objects.NotificationLink(
|
||||
href=pm_threshold_href(
|
||||
pm_threshold.id, endpoint)
|
||||
)
|
||||
)
|
||||
)
|
||||
if threshold_state['subObjectInstanceId']:
|
||||
notif_data.subObjectInstanceId = threshold_state['subObjectInstanceId']
|
||||
return notif_data
|
|
@ -34,6 +34,7 @@ from tacker.sol_refactored.common import fm_alarm_utils
|
|||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.conductor import conductor_rpc_v2 as rpc
|
||||
from tacker.sol_refactored import objects
|
||||
|
@ -62,6 +63,64 @@ class PrometheusPluginPmBase(PrometheusPlugin):
|
|||
auth_handle = http_client.NoAuthHandle()
|
||||
self.client = http_client.HttpClient(auth_handle)
|
||||
|
||||
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 are ignored here and 204 response will always
|
||||
# be returned because when Tacker responds errors to alertmanager,
|
||||
# alertmanager may repeat the same reports.
|
||||
LOG.error("%s: %s", e.__class__.__name__, e.args[0])
|
||||
|
||||
def decompose_metrics(self, pm_job_or_threshold):
|
||||
if pm_job_or_threshold.objectType in {'Vnf', 'Vnfc'}:
|
||||
return self.decompose_metrics_vnfc(pm_job_or_threshold)
|
||||
if pm_job_or_threshold.objectType in {'VnfIntCp', 'VnfExtCp'}:
|
||||
return self.decompose_metrics_vnfintextcp(pm_job_or_threshold)
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
f"Invalid objectType: {pm_job_or_threshold.objectType}.")
|
||||
|
||||
def _make_rules(self, pm_job_or_threshold, metric, inst_map):
|
||||
sub_objs = []
|
||||
if (pm_job_or_threshold.obj_attr_is_set('subObjectInstanceIds')
|
||||
and pm_job_or_threshold.subObjectInstanceIds):
|
||||
sub_objs = pm_job_or_threshold.subObjectInstanceIds
|
||||
|
||||
# Cardinality of objectInstanceIds and subObjectInstanceIds
|
||||
# is N:0 or 1:N.
|
||||
if len(sub_objs) > 0:
|
||||
return self._make_rules_for_each_sub_obj(
|
||||
pm_job_or_threshold, inst_map, metric)
|
||||
return self._make_rules_for_each_obj(
|
||||
pm_job_or_threshold, inst_map, metric)
|
||||
|
||||
def make_rules(self, context, pm_job_or_threshold):
|
||||
target_list, reload_list = self.get_access_info(pm_job_or_threshold)
|
||||
metrics = self.decompose_metrics(pm_job_or_threshold)
|
||||
inst_map = self.get_vnf_instances(context, pm_job_or_threshold)
|
||||
rules = sum(
|
||||
[self._make_rules(pm_job_or_threshold, metric, inst_map)
|
||||
for metric in metrics], [])
|
||||
if len(rules) == 0:
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
"Converting from a PM job/threshold to alert rules is failed."
|
||||
f" PM job/threshold id: {pm_job_or_threshold.id}")
|
||||
rule_group = {
|
||||
'groups': [
|
||||
{
|
||||
'name': f'tacker_{pm_job_or_threshold.id}',
|
||||
'rules': rules
|
||||
}
|
||||
]
|
||||
}
|
||||
self.upload_rules(
|
||||
context, target_list, reload_list,
|
||||
rule_group, pm_job_or_threshold.id)
|
||||
return rule_group
|
||||
|
||||
def convert_measurement_unit(self, metric, value):
|
||||
if re.match(r'^V(Cpu|Memory|Disk)Usage(Mean|Peak)Vnf\..+', metric):
|
||||
value = float(value)
|
||||
|
@ -69,7 +128,7 @@ class PrometheusPluginPmBase(PrometheusPlugin):
|
|||
metric):
|
||||
value = int(value)
|
||||
else:
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
raise sol_ex.PrometheusPluginSkipped(
|
||||
"Failed to convert annotations.value to measurement unit.")
|
||||
return value
|
||||
|
||||
|
@ -116,7 +175,7 @@ class PrometheusPluginPmBase(PrometheusPlugin):
|
|||
labels = {
|
||||
'alertname': '',
|
||||
'receiver_type': 'tacker',
|
||||
'function_type': 'vnfpm-threshold',
|
||||
'function_type': 'vnfpm_threshold',
|
||||
'threshold_id': id,
|
||||
'object_instance_id': object_instance_id,
|
||||
'sub_object_instance_id': sub_object_instance_id,
|
||||
|
@ -588,16 +647,6 @@ class PrometheusPluginPm(PrometheusPluginPmBase, mon_base.MonitoringPlugin):
|
|||
collection_period=collection_period))
|
||||
return rules
|
||||
|
||||
def _make_rules(self, pm_job, metric, inst_map):
|
||||
sub_objs = pm_job.subObjectInstanceIds\
|
||||
if (pm_job.obj_attr_is_set('subObjectInstanceIds') and
|
||||
pm_job.subObjectInstanceIds) else []
|
||||
# Cardinality of objectInstanceIds and subObjectInstanceIds
|
||||
# is N:0 or 1:N.
|
||||
if len(sub_objs) > 0:
|
||||
return self._make_rules_for_each_sub_obj(pm_job, inst_map, metric)
|
||||
return self._make_rules_for_each_obj(pm_job, inst_map, metric)
|
||||
|
||||
def decompose_metrics_vnfintextcp(self, pm_job):
|
||||
group_name = 'VnfInternalCp'\
|
||||
if pm_job.objectType == 'VnfIntCp' else 'VnfExternalCp'
|
||||
|
@ -623,14 +672,6 @@ class PrometheusPluginPm(PrometheusPluginPmBase, mon_base.MonitoringPlugin):
|
|||
)
|
||||
return metrics
|
||||
|
||||
def decompose_metrics(self, pm_job):
|
||||
if pm_job.objectType in {'Vnf', 'Vnfc'}:
|
||||
return self.decompose_metrics_vnfc(pm_job)
|
||||
if pm_job.objectType in {'VnfIntCp', 'VnfExtCp'}:
|
||||
return self.decompose_metrics_vnfintextcp(pm_job)
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
f"Invalid objectType: {pm_job.objectType}.")
|
||||
|
||||
def get_vnf_instances(self, context, pm_job):
|
||||
object_instance_ids = list(set(pm_job.objectInstanceIds))
|
||||
return dict(zip(
|
||||
|
@ -639,27 +680,267 @@ class PrometheusPluginPm(PrometheusPluginPmBase, mon_base.MonitoringPlugin):
|
|||
lambda inst: inst_utils.get_inst(context, inst),
|
||||
object_instance_ids))))
|
||||
|
||||
def make_rules(self, context, pm_job):
|
||||
target_list, reload_list = self.get_access_info(pm_job)
|
||||
metrics = self.decompose_metrics(pm_job)
|
||||
inst_map = self.get_vnf_instances(context, pm_job)
|
||||
rules = sum([self._make_rules(pm_job, metric, inst_map)
|
||||
for metric in metrics], [])
|
||||
if len(rules) == 0:
|
||||
|
||||
class PrometheusPluginThreshold(PrometheusPluginPmBase,
|
||||
mon_base.MonitoringPlugin):
|
||||
|
||||
_instance = None
|
||||
|
||||
@staticmethod
|
||||
def instance():
|
||||
if PrometheusPluginThreshold._instance is None:
|
||||
if not CONF.prometheus_plugin.performance_management:
|
||||
stub = mon_base.MonitoringPluginStub.instance()
|
||||
PrometheusPluginThreshold._instance = stub
|
||||
else:
|
||||
PrometheusPluginThreshold()
|
||||
return PrometheusPluginThreshold._instance
|
||||
|
||||
def __init__(self):
|
||||
if PrometheusPluginThreshold._instance:
|
||||
raise SystemError(
|
||||
"Not constructor but instance() should be used.")
|
||||
super(PrometheusPluginThreshold, self).__init__()
|
||||
self.notification_callback = self.default_callback
|
||||
PrometheusPluginThreshold._instance = self
|
||||
|
||||
def create_threshold(self, **kwargs):
|
||||
return self.make_rules(kwargs['context'], kwargs['pm_threshold'])
|
||||
|
||||
def delete_threshold(self, **kwargs):
|
||||
self.delete_rules(kwargs['context'], kwargs['pm_threshold'])
|
||||
|
||||
def default_callback(self, context, threshold_states):
|
||||
self.rpc.store_threshold_state_info(context, threshold_states)
|
||||
|
||||
def valid_alert(self, pm_threshold, metric, object_instance_id,
|
||||
sub_object_instance_id):
|
||||
instance_id = (
|
||||
pm_threshold.objectInstanceId
|
||||
if (pm_threshold.obj_attr_is_set('objectInstanceId') and
|
||||
pm_threshold.objectInstanceId) else None)
|
||||
if object_instance_id != instance_id:
|
||||
LOG.error(
|
||||
f"labels.object_instance_id {object_instance_id} "
|
||||
"doesn't match pm_threshold.")
|
||||
raise sol_ex.PrometheusPluginSkipped()
|
||||
|
||||
sub_object_instance_ids = (
|
||||
pm_threshold.subObjectInstanceIds
|
||||
if (pm_threshold.obj_attr_is_set('subObjectInstanceIds') and
|
||||
pm_threshold.subObjectInstanceIds) else [])
|
||||
if (sub_object_instance_id and
|
||||
(not sub_object_instance_ids or
|
||||
sub_object_instance_id not in sub_object_instance_ids)):
|
||||
LOG.error(
|
||||
f"labels.sub_object_instance_id {sub_object_instance_id} "
|
||||
"doesn't match pm_threshold.")
|
||||
raise sol_ex.PrometheusPluginSkipped()
|
||||
|
||||
if metric != pm_threshold.criteria.performanceMetric:
|
||||
LOG.error(
|
||||
f"labels.metric {metric} doesn't match pm_threshold.")
|
||||
raise sol_ex.PrometheusPluginSkipped()
|
||||
|
||||
def set_threshold_last_value(
|
||||
self, threshold_value, pm_threshold_state):
|
||||
threshold_last_value = threshold_value
|
||||
if pm_threshold_state and pm_threshold_state.get('performanceValue'):
|
||||
threshold_last_value = pm_threshold_state['performanceValue']
|
||||
return float(threshold_last_value)
|
||||
|
||||
def set_crossing_direction(self, threshold_new_value, threshold_last_value,
|
||||
threshold_value, threshold_hysteresis):
|
||||
# NOTE: "IN" is simply used to mark not to send
|
||||
# ThresholdCrossedNotification.
|
||||
crossing_direction = "IN"
|
||||
if (threshold_new_value > (threshold_value + threshold_hysteresis) >=
|
||||
threshold_last_value):
|
||||
crossing_direction = "UP"
|
||||
if (threshold_new_value < (threshold_value - threshold_hysteresis) <=
|
||||
threshold_last_value):
|
||||
crossing_direction = "DOWN"
|
||||
return crossing_direction
|
||||
|
||||
@validator.schema(prometheus_plugin_schemas.AlertMessage)
|
||||
def _alert(self, request, body):
|
||||
result = []
|
||||
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'] == 'vnfpm_threshold')
|
||||
|
||||
for alert in alerts:
|
||||
try:
|
||||
pm_threshold_id = alert['labels']['threshold_id']
|
||||
object_instance_id = alert['labels']['object_instance_id']
|
||||
metric = alert['labels']['metric']
|
||||
sub_object_instance_id = alert['labels'].get(
|
||||
'sub_object_instance_id')
|
||||
value = alert['annotations']['value']
|
||||
|
||||
pm_threshold = pm_threshold_utils.get_pm_threshold(
|
||||
context, pm_threshold_id)
|
||||
if not pm_threshold:
|
||||
raise sol_ex.PMThresholdNotExist(
|
||||
threshold_id=pm_threshold_id)
|
||||
threshold_type = pm_threshold.criteria.thresholdType
|
||||
simple_threshold_details = (
|
||||
pm_threshold.criteria.simpleThresholdDetails)
|
||||
|
||||
self.valid_alert(pm_threshold, metric, object_instance_id,
|
||||
sub_object_instance_id)
|
||||
|
||||
if threshold_type == "SIMPLE" and simple_threshold_details:
|
||||
threshold_value = simple_threshold_details.thresholdValue
|
||||
threshold_hysteresis = simple_threshold_details.hysteresis
|
||||
|
||||
pm_threshold_state = (
|
||||
pm_threshold_utils.get_pm_threshold_state(
|
||||
pm_threshold, sub_object_instance_id))
|
||||
|
||||
threshold_new_value = self.convert_measurement_unit(
|
||||
metric, value)
|
||||
threshold_last_value = self.set_threshold_last_value(
|
||||
threshold_value, pm_threshold_state)
|
||||
|
||||
crossing_direction = self.set_crossing_direction(
|
||||
threshold_new_value, threshold_last_value,
|
||||
threshold_value, threshold_hysteresis
|
||||
)
|
||||
|
||||
result.append({
|
||||
'thresholdId': pm_threshold.id,
|
||||
'subObjectInstanceId': sub_object_instance_id,
|
||||
'performanceValue': threshold_new_value,
|
||||
'metrics': metric,
|
||||
'crossingDirection': crossing_direction
|
||||
})
|
||||
else:
|
||||
LOG.error("Lack thresholdValue and hysteresis")
|
||||
except (sol_ex.PrometheusPluginSkipped,
|
||||
sol_ex.PMThresholdNotExist):
|
||||
pass
|
||||
|
||||
# Call ConductorV2
|
||||
if result and self.notification_callback:
|
||||
self.notification_callback(
|
||||
context, result)
|
||||
for res in result:
|
||||
res.pop('thresholdId')
|
||||
return result
|
||||
|
||||
def decompose_metrics_vnfc(self, pm_threshold):
|
||||
# pm_threshold.criteria.performanceMetric : String 1
|
||||
# pm_threshold.objectInstanceId : String 1
|
||||
metrics = []
|
||||
metric = (pm_threshold.criteria.performanceMetric
|
||||
if pm_threshold.criteria.obj_attr_is_set('performanceMetric')
|
||||
else None)
|
||||
|
||||
_metric = (re.match(
|
||||
r'^V(Cpu|Memory|Disk)Usage(Mean|Peak)Vnf\..+', metric) and re.sub(
|
||||
r'^V(Cpu|Memory|Disk)Usage(Mean|Peak)Vnf\.', '', metric))
|
||||
if _metric == pm_threshold.objectInstanceId:
|
||||
metrics.append(metric)
|
||||
return metrics
|
||||
else:
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
f"Converting from a PM job to alert rules is failed."
|
||||
f" PM job id: {pm_job.id}")
|
||||
rule_group = {
|
||||
'groups': [
|
||||
{
|
||||
'name': f'tacker_{pm_job.id}',
|
||||
'rules': rules
|
||||
}
|
||||
]
|
||||
"Invalid performanceMetric.")
|
||||
|
||||
def decompose_metrics_vnfintextcp(self, pm_threshold):
|
||||
# pm_threshold.criteria.performanceMetric : String 1
|
||||
metrics = []
|
||||
metric = (pm_threshold.criteria.performanceMetric
|
||||
if pm_threshold.criteria.obj_attr_is_set('performanceMetric')
|
||||
else None)
|
||||
|
||||
_metric = re.match(r'^(Byte|Packet)(Incoming|Outgoing)' +
|
||||
pm_threshold.objectType, metric)
|
||||
if _metric:
|
||||
metrics.append(metric)
|
||||
return metrics
|
||||
else:
|
||||
raise sol_ex.PrometheusPluginError(
|
||||
"Invalid performanceMetric.")
|
||||
|
||||
def _make_rules_for_each_obj(self, pm_threshold, inst_map, metric):
|
||||
target = re.sub(r'\..+$', '', metric)
|
||||
obj = pm_threshold.objectInstanceId
|
||||
rules = []
|
||||
# resource ids are like:
|
||||
# ['test-test1-756757f8f-xcwmt',
|
||||
# 'test-test2-756757f8f-kmghr', ...]
|
||||
# convert them to a regex string such as:
|
||||
# '(test-test1-[0-9a-f]{1,10}-[0-9a-z]{5}$|
|
||||
# test-test2-[0-9a-f]{1,10}-[0-9a-z]{5}$|...)'
|
||||
pods_regexp = self.get_pod_regexp(inst_map[obj])
|
||||
namespace = self.get_namespace(inst_map[obj])
|
||||
reporting_period = CONF.prometheus_plugin.reporting_period_threshold
|
||||
collection_period = CONF.prometheus_plugin.collection_period_threshold
|
||||
|
||||
expr = self.make_prom_ql(
|
||||
target, pods_regexp, collection_period=collection_period,
|
||||
reporting_period=reporting_period, pm_type="Threshold",
|
||||
namespace=namespace)
|
||||
rules.append(self.make_rule(
|
||||
'Threshold', pm_threshold.id, obj, None, metric, expr,
|
||||
collection_period=collection_period))
|
||||
return rules
|
||||
|
||||
def _make_rules_for_each_sub_obj(self, pm_threshold, inst_map, metric):
|
||||
reporting_period = CONF.prometheus_plugin.reporting_period_threshold
|
||||
collection_period = CONF.prometheus_plugin.collection_period_threshold
|
||||
target = re.sub(r'\..+$', '', metric)
|
||||
obj = pm_threshold.objectInstanceId
|
||||
sub_objs = (pm_threshold.subObjectInstanceIds
|
||||
if (pm_threshold.obj_attr_is_set('subObjectInstanceIds')
|
||||
and pm_threshold.subObjectInstanceIds) else [])
|
||||
rules = []
|
||||
if pm_threshold.objectType in {'Vnf', 'Vnfc'}:
|
||||
inst = inst_map[obj]
|
||||
for sub_obj in sub_objs:
|
||||
compute_resource = self.get_compute_resource_by_sub_obj(
|
||||
inst, sub_obj)
|
||||
if not compute_resource:
|
||||
continue
|
||||
resource_id = compute_resource.resourceId
|
||||
namespace = self.get_namespace(inst)
|
||||
expr = self.make_prom_ql(
|
||||
target, resource_id,
|
||||
collection_period=collection_period,
|
||||
reporting_period=reporting_period,
|
||||
pm_type="Threshold",
|
||||
namespace=namespace
|
||||
)
|
||||
rules.append(self.make_rule(
|
||||
'Threshold', pm_threshold.id, obj, sub_obj, metric, expr,
|
||||
collection_period=collection_period))
|
||||
else:
|
||||
pods_regexp = self.get_pod_regexp(inst_map[obj])
|
||||
if pods_regexp is None:
|
||||
return []
|
||||
for sub_obj in sub_objs:
|
||||
namespace = self.get_namespace(inst_map[obj])
|
||||
expr = self.make_prom_ql(
|
||||
target, pods_regexp,
|
||||
collection_period=collection_period,
|
||||
reporting_period=reporting_period,
|
||||
sub_object_instance_id=sub_obj,
|
||||
pm_type="Threshold",
|
||||
namespace=namespace
|
||||
)
|
||||
rules.append(self.make_rule(
|
||||
'Threshold', pm_threshold.id, obj, sub_obj, metric, expr,
|
||||
collection_period=collection_period))
|
||||
return rules
|
||||
|
||||
def get_vnf_instances(self, context, pm_threshold):
|
||||
return {
|
||||
pm_threshold.objectInstanceId: inst_utils.get_inst(
|
||||
context, pm_threshold.objectInstanceId)
|
||||
}
|
||||
self.upload_rules(
|
||||
context, target_list, reload_list, rule_group, pm_job.id)
|
||||
return rule_group
|
||||
|
||||
|
||||
class PrometheusPluginFm(PrometheusPlugin, mon_base.MonitoringPlugin):
|
||||
|
|
|
@ -14,13 +14,11 @@
|
|||
# under the License.
|
||||
|
||||
|
||||
import threading
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import timeutils
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
|
@ -32,8 +30,6 @@ LOG = logging.getLogger(__name__)
|
|||
|
||||
CONF = config.CONF
|
||||
|
||||
TEST_NOTIFICATION_TIMEOUT = 20 # seconds
|
||||
|
||||
|
||||
def get_subsc(context, subsc_id):
|
||||
subsc = objects.LccnSubscriptionV2.get_by_id(context, subsc_id)
|
||||
|
@ -50,87 +46,16 @@ def subsc_href(subsc_id, endpoint):
|
|||
return "{}/vnflcm/v2/subscriptions/{}".format(endpoint, subsc_id)
|
||||
|
||||
|
||||
def _get_notification_auth_handle(subsc):
|
||||
def get_notification_auth_handle(subsc):
|
||||
auth_req = subsc.get('authentication', None)
|
||||
if auth_req:
|
||||
auth = objects.SubscriptionAuthentication(
|
||||
authType=auth_req['authType']
|
||||
)
|
||||
if 'OAUTH2_CLIENT_CERT' in auth.authType:
|
||||
param = subsc.authentication.paramsOauth2ClientCert
|
||||
verify_cert = CONF.v2_vnfm.notification_mtls_ca_cert_file
|
||||
client_cert = CONF.v2_vnfm.notification_mtls_client_cert_file
|
||||
return http_client.OAuth2MtlsAuthHandle(None,
|
||||
param.tokenEndpoint, param.clientId, verify_cert, client_cert)
|
||||
elif 'OAUTH2_CLIENT_CREDENTIALS' in auth.authType:
|
||||
param = subsc.authentication.paramsOauth2ClientCredentials
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.OAuth2AuthHandle(None,
|
||||
param.tokenEndpoint, param.clientId, param.clientPassword,
|
||||
verify=verify)
|
||||
elif 'BASIC' in auth.authType:
|
||||
param = subsc.authentication.paramsBasic
|
||||
verify = CONF.v2_vnfm.notification_verify_cert
|
||||
if verify and CONF.v2_vnfm.notification_ca_cert_file:
|
||||
verify = CONF.v2_vnfm.notification_ca_cert_file
|
||||
return http_client.BasicAuthHandle(param.userName, param.password,
|
||||
verify=verify)
|
||||
else:
|
||||
raise sol_ex.AuthTypeNotFound(auth.authType)
|
||||
return common_script_utils.get_notification_auth_handle(subsc)
|
||||
else:
|
||||
return http_client.NoAuthHandle()
|
||||
|
||||
# not reach here
|
||||
|
||||
|
||||
def async_call(func):
|
||||
def inner(*args, **kwargs):
|
||||
th = threading.Thread(target=func, args=args,
|
||||
kwargs=kwargs, daemon=True)
|
||||
th.start()
|
||||
return inner
|
||||
|
||||
|
||||
@async_call
|
||||
def send_notification(subsc, notif_data):
|
||||
auth_handle = _get_notification_auth_handle(subsc)
|
||||
connect_retries = (CONF.v2_vnfm.notify_connect_retries
|
||||
if CONF.v2_vnfm.notify_connect_retries else None)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_VERSION,
|
||||
connect_retries=connect_retries)
|
||||
|
||||
url = subsc.callbackUri
|
||||
try:
|
||||
resp, body = client.do_request(
|
||||
url, "POST", expected_status=[204], body=notif_data)
|
||||
except sol_ex.SolException:
|
||||
# it may occur if test_notification was not executed.
|
||||
LOG.exception("send_notification failed")
|
||||
|
||||
if resp.status_code != 204:
|
||||
LOG.error("send_notification failed: %d" % resp.status_code)
|
||||
|
||||
|
||||
def test_notification(subsc):
|
||||
auth_handle = _get_notification_auth_handle(subsc)
|
||||
client = http_client.HttpClient(auth_handle,
|
||||
version=api_version.CURRENT_VERSION,
|
||||
timeout=TEST_NOTIFICATION_TIMEOUT)
|
||||
|
||||
url = subsc.callbackUri
|
||||
try:
|
||||
resp, _ = client.do_request(url, "GET", expected_status=[204])
|
||||
except sol_ex.SolException as e:
|
||||
# any sort of error is considered. avoid 500 error.
|
||||
raise sol_ex.TestNotificationFailed() from e
|
||||
|
||||
if resp.status_code != 204:
|
||||
raise sol_ex.TestNotificationFailed()
|
||||
|
||||
|
||||
def match_version(version, inst):
|
||||
# - vnfSoftwareVersion 1
|
||||
# - vnfdVersions 0..N
|
||||
|
|
|
@ -95,6 +95,10 @@ class PrometheusPluginConductor(object):
|
|||
def store_job_info(self, context, report):
|
||||
self.cast(context, 'store_job_info', report=report)
|
||||
|
||||
def store_threshold_state_info(self, context, threshold_states):
|
||||
self.cast(context, 'store_threshold_state_info',
|
||||
threshold_states=threshold_states)
|
||||
|
||||
def trigger_scale(self, context, id, scale_req):
|
||||
self.cast(context, 'trigger_scale', id=id, scale_req=scale_req)
|
||||
|
||||
|
|
|
@ -393,6 +393,10 @@ class ConductorV2(object):
|
|||
# call pm_driver
|
||||
self.vnfpm_driver.store_job_info(context, report)
|
||||
|
||||
def store_threshold_state_info(self, context, threshold_states):
|
||||
# call pm_driver
|
||||
self.vnfpm_driver.store_threshold_info(context, threshold_states)
|
||||
|
||||
@log.log
|
||||
def trigger_scale(self, context, id, scale_req):
|
||||
self.prom_driver.trigger_scale(context, id, scale_req)
|
||||
|
|
|
@ -13,8 +13,13 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import datetime
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.nfvo import nfvo_client
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
|
@ -44,6 +49,31 @@ class VnfPmDriverV2():
|
|||
self.nfvo_client.send_pm_job_notification(
|
||||
report, pm_job, timestamp, self.endpoint)
|
||||
|
||||
def store_threshold_info(self, context, threshold_states):
|
||||
for threshold_state in threshold_states:
|
||||
update_threshold_state_data = {
|
||||
'subObjectInstanceId': threshold_state[
|
||||
'subObjectInstanceId'],
|
||||
'performanceValue': threshold_state['performanceValue'],
|
||||
'metrics': threshold_state['metrics'],
|
||||
'crossingDirection': threshold_state['crossingDirection']
|
||||
}
|
||||
pm_threshold_utils.update_threshold_state_data(
|
||||
context, threshold_state['thresholdId'],
|
||||
update_threshold_state_data)
|
||||
datetime_now = datetime.datetime.now(datetime.timezone.utc)
|
||||
threshold = pm_threshold_utils.get_pm_threshold(
|
||||
context, threshold_state['thresholdId'])
|
||||
if not threshold:
|
||||
raise sol_ex.PMThresholdNotExist(
|
||||
threshold_id=threshold_state['thresholdId'])
|
||||
if threshold_state['crossingDirection'] in {"UP", "DOWN"}:
|
||||
notif_data = pm_threshold_utils.make_threshold_notif_data(
|
||||
datetime_now, threshold_state,
|
||||
self.endpoint, threshold)
|
||||
common_script_utils.send_notification(
|
||||
threshold, notif_data, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
||||
def _store_report(self, context, report):
|
||||
report = objects.PerformanceReportV2.from_dict(report)
|
||||
report.create(context)
|
||||
|
|
|
@ -34,6 +34,19 @@ class PmEventController(prom_wsgi.PrometheusPluginAPIController):
|
|||
return prom_wsgi.PrometheusPluginResponse(204, None)
|
||||
|
||||
|
||||
class PmThresholdController(prom_wsgi.PrometheusPluginAPIController):
|
||||
def pm_threshold(self, request, body):
|
||||
if not CONF.prometheus_plugin.performance_management:
|
||||
raise sol_ex.PrometheusPluginNotEnabled(
|
||||
name='Performance management')
|
||||
cls = mon_base.get_class(
|
||||
CONF.prometheus_plugin.performance_management_threshold_package,
|
||||
CONF.prometheus_plugin.performance_management_threshold_class)
|
||||
mon_base.MonitoringPlugin.get_instance(cls).alert(
|
||||
request=request, body=body)
|
||||
return prom_wsgi.PrometheusPluginResponse(204, None)
|
||||
|
||||
|
||||
class FmAlertController(prom_wsgi.PrometheusPluginAPIController):
|
||||
def alert(self, request, body):
|
||||
if not CONF.prometheus_plugin.fault_management:
|
||||
|
|
|
@ -20,6 +20,7 @@ from tacker.sol_refactored.api import api_version
|
|||
from tacker.sol_refactored.api.schemas import vnffm_v1 as schema
|
||||
from tacker.sol_refactored.api import validator
|
||||
from tacker.sol_refactored.api import wsgi as sol_wsgi
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import coordinate
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
@ -122,7 +123,8 @@ class VnfFmControllerV1(sol_wsgi.SolAPIController):
|
|||
subsc.authentication = subsc_utils.check_http_client_auth(auth_req)
|
||||
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
fm_subsc_utils.test_notification(subsc)
|
||||
common_script_utils.test_notification(
|
||||
subsc, common_script_utils.NOTIFY_TYPE_FM)
|
||||
|
||||
subsc.create(context)
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ from tacker.sol_refactored.api import api_version
|
|||
from tacker.sol_refactored.api.schemas import vnflcm_v2 as schema
|
||||
from tacker.sol_refactored.api import validator
|
||||
from tacker.sol_refactored.api import wsgi as sol_wsgi
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import coordinate
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
|
@ -399,7 +400,7 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||
subsc.authentication = subsc_utils.check_http_client_auth(auth_req)
|
||||
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
subsc_utils.test_notification(subsc)
|
||||
common_script_utils.test_notification(subsc)
|
||||
|
||||
subsc.create(context)
|
||||
|
||||
|
|
|
@ -23,11 +23,13 @@ from tacker.sol_refactored.api import api_version
|
|||
from tacker.sol_refactored.api.schemas import vnfpm_v2 as schema
|
||||
from tacker.sol_refactored.api import validator
|
||||
from tacker.sol_refactored.api import wsgi as sol_wsgi
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import coordinate
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import monitoring_plugin_base as plugin
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.controller import vnfpm_view
|
||||
|
@ -45,7 +47,7 @@ OBJ_TYPE_TO_GROUP_TYPE = {
|
|||
'VnfExtCp': 'VnfExternalCp'
|
||||
}
|
||||
|
||||
OBJ_TYPE_TO_METRIC_LISt = {
|
||||
OBJ_TYPE_TO_METRIC_LIST = {
|
||||
'Vnf': {'VCpuUsageMeanVnf', 'VCpuUsagePeakVnf',
|
||||
'VMemoryUsageMeanVnf', 'VMemoryUsagePeakVnf',
|
||||
'VDiskUsageMeanVnf', 'VDiskUsagePeakVnf'},
|
||||
|
@ -71,20 +73,40 @@ def _check_performance_metric_or_group(
|
|||
# Check if the type in performance metric matches the standard type.
|
||||
if performance_metric:
|
||||
metric_types = {metric.split('.')[0] for metric in performance_metric}
|
||||
if not metric_types.issubset(OBJ_TYPE_TO_METRIC_LISt[obj_type]):
|
||||
if not metric_types.issubset(OBJ_TYPE_TO_METRIC_LIST[obj_type]):
|
||||
raise sol_ex.PMJobInvalidRequest
|
||||
|
||||
|
||||
def _check_metric_and_obj_type(performance_metric, obj_type):
|
||||
metric_type = {performance_metric}
|
||||
if obj_type == 'Vnf' or obj_type == 'Vnfc':
|
||||
if metric_type.issubset(OBJ_TYPE_TO_METRIC_LIST[obj_type]):
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
if obj_type == 'VnfIntCp' or obj_type == 'VnfExtCp':
|
||||
if len(performance_metric.split('.')) == 2:
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
metric_type = {performance_metric.split('.')[0]}
|
||||
if not metric_type.issubset(OBJ_TYPE_TO_METRIC_LIST[obj_type]):
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
|
||||
|
||||
class VnfPmControllerV2(sol_wsgi.SolAPIController):
|
||||
|
||||
def __init__(self):
|
||||
self.nfvo_client = nfvo_client.NfvoClient()
|
||||
self.endpoint = CONF.v2_vnfm.endpoint
|
||||
self._pm_job_view = vnfpm_view.PmJobViewBuilder(self.endpoint)
|
||||
self._pm_threshold_view = (
|
||||
vnfpm_view.PmThresholdViewBuilder(self.endpoint))
|
||||
cls = plugin.get_class(
|
||||
CONF.prometheus_plugin.performance_management_package,
|
||||
CONF.prometheus_plugin.performance_management_class)
|
||||
self.plugin = plugin.MonitoringPlugin.get_instance(cls)
|
||||
threshold_cls = plugin.get_class(
|
||||
CONF.prometheus_plugin.performance_management_threshold_package,
|
||||
CONF.prometheus_plugin.performance_management_threshold_class)
|
||||
self.threshold_plugin = (
|
||||
plugin.MonitoringPlugin.get_instance(threshold_cls))
|
||||
|
||||
@validator.schema(schema.CreatePmJobRequest_V210, '2.1.0')
|
||||
def create(self, request, body):
|
||||
|
@ -160,7 +182,8 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
|
|||
pm_job.metadata = metadata
|
||||
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
pm_job_utils.test_notification(pm_job)
|
||||
common_script_utils.test_notification(
|
||||
pm_job, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
||||
try:
|
||||
self.plugin.create_job(context=context, pm_job=pm_job)
|
||||
|
@ -216,7 +239,8 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
|
|||
body.get("authentication"))
|
||||
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
pm_job_utils.test_notification(pm_job)
|
||||
common_script_utils.test_notification(
|
||||
pm_job, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
pm_job.update(context)
|
||||
|
@ -253,7 +277,7 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
|
|||
version=api_version.CURRENT_PM_VERSION)
|
||||
|
||||
def allowed_content_types(self, action):
|
||||
if action == 'update':
|
||||
if action in {'update', 'update_threshold'}:
|
||||
# Content-Type of Modify request shall be
|
||||
# 'application/mergepatch+json' according to SOL spec.
|
||||
# But 'application/json' and 'text/plain' is OK for backward
|
||||
|
@ -262,5 +286,157 @@ class VnfPmControllerV2(sol_wsgi.SolAPIController):
|
|||
'text/plain']
|
||||
return ['application/json', 'text/plain']
|
||||
|
||||
def allowed_accept(self, action):
|
||||
return ['application/json', 'application/mergepatch+json',
|
||||
'text/plain']
|
||||
|
||||
def supported_api_versions(self, action):
|
||||
return api_version.v2_pm_versions
|
||||
|
||||
@validator.schema(schema.CreateThresholdRequest_V210, '2.1.0')
|
||||
def create_threshold(self, request, body):
|
||||
context = request.context
|
||||
|
||||
object_type = body['objectType']
|
||||
performance_metric = body["criteria"]['performanceMetric']
|
||||
|
||||
# Check if the type in performance metric matches the standard type.
|
||||
_check_metric_and_obj_type(performance_metric, object_type)
|
||||
|
||||
# According to nfv-sol003 6.5.3.4,
|
||||
# currently criteria.thresholdType supports only "SIMPLE".
|
||||
if body["criteria"]["thresholdType"] != "SIMPLE":
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
# check criteria.thresholdType and criteria.ThresholdDetails
|
||||
if not body["criteria"].get("simpleThresholdDetails"):
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
|
||||
threshold_value = (
|
||||
body["criteria"]["simpleThresholdDetails"]["thresholdValue"])
|
||||
threshold_hysteresis = (
|
||||
body["criteria"]["simpleThresholdDetails"]["hysteresis"])
|
||||
|
||||
# check vnf instance status
|
||||
inst_id = body.get("objectInstanceId")
|
||||
inst = inst_utils.get_inst(context, inst_id)
|
||||
if inst.instantiationState == 'NOT_INSTANTIATED':
|
||||
raise sol_ex.VnfInstanceIsNotInstantiated(inst_id=inst_id)
|
||||
|
||||
threshold_criteria = objects.ThresholdCriteriaV2(
|
||||
performanceMetric=performance_metric,
|
||||
thresholdType=body["criteria"]['thresholdType']
|
||||
)
|
||||
threshold_details = objects.SimpleThresholdDetails(
|
||||
thresholdValue=threshold_value,
|
||||
hysteresis=threshold_hysteresis)
|
||||
threshold_criteria.simpleThresholdDetails = threshold_details
|
||||
|
||||
threshold_id = uuidutils.generate_uuid()
|
||||
threshold = objects.ThresholdV2(
|
||||
id=threshold_id,
|
||||
objectType=object_type,
|
||||
objectInstanceId=inst_id,
|
||||
criteria=threshold_criteria,
|
||||
callbackUri=body["callbackUri"],
|
||||
)
|
||||
if body.get("subObjectInstanceIds"):
|
||||
threshold.subObjectInstanceIds = body["subObjectInstanceIds"]
|
||||
metadata = body.get('metadata')
|
||||
if not metadata:
|
||||
raise sol_ex.PMThresholdInvalidRequest
|
||||
threshold.metadata = metadata
|
||||
|
||||
auth_req = body.get('authentication')
|
||||
if auth_req:
|
||||
threshold.authentication = subsc_utils.check_http_client_auth(
|
||||
auth_req)
|
||||
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
common_script_utils.test_notification(
|
||||
threshold, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
||||
try:
|
||||
self.threshold_plugin.create_threshold(
|
||||
context=context,
|
||||
pm_threshold=threshold)
|
||||
except sol_ex.PrometheusPluginError as e:
|
||||
LOG.error("Failed to create PM Threshold: %s", e.args[0])
|
||||
raise sol_ex.PrometheusSettingFailed from e
|
||||
|
||||
threshold.create(context)
|
||||
|
||||
location = pm_threshold_utils.pm_threshold_href(threshold.id,
|
||||
self.endpoint)
|
||||
resp_body = self._pm_threshold_view.detail(threshold)
|
||||
return sol_wsgi.SolResponse(201, resp_body,
|
||||
version=api_version.CURRENT_PM_VERSION,
|
||||
location=location)
|
||||
|
||||
def index_threshold(self, request):
|
||||
filter_param = request.GET.get('filter')
|
||||
filters = (self._pm_threshold_view.parse_filter(filter_param)
|
||||
if filter_param else None)
|
||||
|
||||
page_size = CONF.v2_vnfm.vnfpm_pmthreshold_page_size
|
||||
pager = self._pm_threshold_view.parse_pager(request, page_size)
|
||||
pm_job = pm_threshold_utils.get_pm_threshold_all(request.context,
|
||||
marker=pager.marker)
|
||||
resp_body = self._pm_threshold_view.detail_list(pm_job, filters,
|
||||
None, pager)
|
||||
|
||||
return sol_wsgi.SolResponse(200, resp_body,
|
||||
version=api_version.CURRENT_PM_VERSION,
|
||||
link=pager.get_link())
|
||||
|
||||
def show_threshold(self, request, thresholdId):
|
||||
pm_threshold = pm_threshold_utils.get_pm_threshold(
|
||||
request.context, thresholdId)
|
||||
if not pm_threshold:
|
||||
raise sol_ex.PMThresholdNotExist(threshold_id=thresholdId)
|
||||
pm_threshold_resp = self._pm_threshold_view.detail(pm_threshold)
|
||||
return sol_wsgi.SolResponse(200, pm_threshold_resp,
|
||||
version=api_version.CURRENT_PM_VERSION)
|
||||
|
||||
@validator.schema(schema.ThresholdModifications_V210, '2.1.0')
|
||||
@coordinate.lock_resources('{thresholdId}')
|
||||
def update_threshold(self, request, thresholdId, body):
|
||||
context = request.context
|
||||
|
||||
pm_threshold = pm_threshold_utils.get_pm_threshold(
|
||||
context, thresholdId)
|
||||
if not pm_threshold:
|
||||
raise sol_ex.PMThresholdNotExist(threshold_id=thresholdId)
|
||||
|
||||
if body.get("callbackUri"):
|
||||
pm_threshold.callbackUri = body.get("callbackUri")
|
||||
if CONF.v2_nfvo.test_callback_uri:
|
||||
common_script_utils.test_notification(
|
||||
pm_threshold, common_script_utils.NOTIFY_TYPE_PM)
|
||||
if body.get("authentication"):
|
||||
pm_threshold.authentication = subsc_utils.check_http_client_auth(
|
||||
body.get("authentication"))
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
pm_threshold.update(context)
|
||||
|
||||
pm_threshold_modifications = objects.ThresholdModificationsV2(
|
||||
callbackUri=pm_threshold.callbackUri)
|
||||
resp = pm_threshold_modifications.to_dict()
|
||||
return sol_wsgi.SolResponse(200, resp,
|
||||
version=api_version.CURRENT_PM_VERSION)
|
||||
|
||||
@coordinate.lock_resources('{thresholdId}')
|
||||
def delete_threshold(self, request, thresholdId):
|
||||
context = request.context
|
||||
pm_threshold = pm_threshold_utils.get_pm_threshold(
|
||||
context, thresholdId)
|
||||
if not pm_threshold:
|
||||
raise sol_ex.PMThresholdNotExist(thresholdId=thresholdId)
|
||||
|
||||
self.threshold_plugin.delete_threshold(
|
||||
context=context, pm_threshold=pm_threshold)
|
||||
|
||||
pm_threshold.delete(context)
|
||||
|
||||
return sol_wsgi.SolResponse(204, None,
|
||||
version=api_version.CURRENT_PM_VERSION)
|
||||
|
|
|
@ -17,6 +17,7 @@ from oslo_log import log as logging
|
|||
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.controller import vnflcm_view as base_view
|
||||
|
||||
|
||||
|
@ -52,3 +53,23 @@ class PmJobViewBuilder(base_view.BaseViewBuilder):
|
|||
if resp.get('jobId'):
|
||||
resp.pop('jobId')
|
||||
return resp
|
||||
|
||||
|
||||
class PmThresholdViewBuilder(base_view.BaseViewBuilder):
|
||||
_EXCLUDE_DEFAULT = []
|
||||
|
||||
def __init__(self, endpoint):
|
||||
self.endpoint = endpoint
|
||||
|
||||
def detail(self, threshold, selector=None):
|
||||
# NOTE: _links is not saved in DB. create when it is necessary.
|
||||
if not threshold.obj_attr_is_set('_links'):
|
||||
threshold._links = pm_threshold_utils.make_pm_threshold_links(
|
||||
threshold, self.endpoint)
|
||||
|
||||
resp = threshold.to_dict()
|
||||
resp.pop('authentication', None)
|
||||
resp.pop('metadata', None)
|
||||
if selector is not None:
|
||||
resp = selector.filter(threshold, resp)
|
||||
return resp
|
||||
|
|
|
@ -227,6 +227,28 @@ class PmJobV2(model_base.BASE):
|
|||
metadata__ = sa.Column("metadata", sa.JSON(), nullable=True)
|
||||
|
||||
|
||||
class ThresholdV2(model_base.BASE):
|
||||
"""Type: Threshold
|
||||
|
||||
NFV-SOL 003
|
||||
- v3.3.1 6.5.2.9 (API version: 2.1.0)
|
||||
"""
|
||||
|
||||
__tablename__ = 'ThresholdV2'
|
||||
id = sa.Column(sa.String(255), nullable=False, primary_key=True)
|
||||
objectType = sa.Column(sa.String(32), nullable=False)
|
||||
objectInstanceId = sa.Column(sa.String(255), nullable=False)
|
||||
subObjectInstanceIds = sa.Column(sa.JSON(), nullable=True)
|
||||
criteria = sa.Column(sa.JSON(), nullable=False)
|
||||
callbackUri = sa.Column(sa.String(255), nullable=False)
|
||||
# NOTE: 'authentication' attribute is not included in the
|
||||
# original 'Threshold' data type definition.
|
||||
authentication = sa.Column(sa.JSON(), nullable=True)
|
||||
# NOTE: 'metadata' attribute is not included in the
|
||||
# original 'Threshold' data type definition.
|
||||
metadata__ = sa.Column("metadata", sa.JSON(), nullable=True)
|
||||
|
||||
|
||||
class PerformanceReportV2(model_base.BASE):
|
||||
"""Type: Report
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import zipfile
|
|||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import fm_alarm_utils as alarm_utils
|
||||
from tacker.sol_refactored.common import fm_subscription_utils as fm_utils
|
||||
|
@ -185,7 +186,7 @@ class NfvoClient(object):
|
|||
for subsc in subscs:
|
||||
notif_data = subsc_utils.make_create_inst_notif_data(
|
||||
subsc, inst, endpoint)
|
||||
subsc_utils.send_notification(subsc, notif_data)
|
||||
common_script_utils.send_notification(subsc, notif_data)
|
||||
|
||||
if self.is_local:
|
||||
self.nfvo.recv_inst_create_notification(context, inst)
|
||||
|
@ -195,7 +196,7 @@ class NfvoClient(object):
|
|||
for subsc in subscs:
|
||||
notif_data = subsc_utils.make_delete_inst_notif_data(
|
||||
subsc, inst, endpoint)
|
||||
subsc_utils.send_notification(subsc, notif_data)
|
||||
common_script_utils.send_notification(subsc, notif_data)
|
||||
|
||||
if self.is_local:
|
||||
self.nfvo.recv_inst_delete_notification(context, inst)
|
||||
|
@ -208,7 +209,7 @@ class NfvoClient(object):
|
|||
for subsc in subscs:
|
||||
notif_data = lcmocc_utils.make_lcmocc_notif_data(
|
||||
subsc, lcmocc, endpoint)
|
||||
subsc_utils.send_notification(subsc, notif_data)
|
||||
common_script_utils.send_notification(subsc, notif_data)
|
||||
|
||||
if self.is_local:
|
||||
self.nfvo.recv_lcmocc_notification(context, lcmocc, inst)
|
||||
|
@ -218,7 +219,8 @@ class NfvoClient(object):
|
|||
for subsc in subscs:
|
||||
notif_data = alarm_utils.make_alarm_notif_data(
|
||||
subsc, alarm, endpoint)
|
||||
fm_utils.send_notification(subsc, notif_data)
|
||||
common_script_utils.send_notification(
|
||||
subsc, notif_data, common_script_utils.NOTIFY_TYPE_FM)
|
||||
|
||||
def send_pm_job_notification(self, report, pm_job, timestamp, endpoint):
|
||||
report_object_instance_id = {entry.objectInstanceId
|
||||
|
@ -232,4 +234,5 @@ class NfvoClient(object):
|
|||
notif_data = pm_job_utils.make_pm_notif_data(
|
||||
instance_id, sub_instance_ids, report.id,
|
||||
pm_job, timestamp, endpoint)
|
||||
pm_job_utils.send_notification(pm_job, notif_data)
|
||||
common_script_utils.send_notification(
|
||||
pm_job, notif_data, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
|
|
@ -71,6 +71,7 @@ def register_all():
|
|||
__import__(objects_root + '.v2.change_ext_vnf_connectivity_request')
|
||||
__import__(objects_root + '.v2.change_vnf_flavour_request')
|
||||
__import__(objects_root + '.v2.cp_protocol_info')
|
||||
__import__(objects_root + '.v2.create_threshold_request')
|
||||
__import__(objects_root + '.v2.create_vnf_pkg_info_request')
|
||||
__import__(objects_root + '.v2.create_vnf_request')
|
||||
__import__(objects_root + '.v2.create_vnf_snapshot_info_request')
|
||||
|
@ -105,6 +106,10 @@ def register_all():
|
|||
__import__(objects_root + '.v2.scale_vnf_request')
|
||||
__import__(objects_root + '.v2.scale_vnf_to_level_request')
|
||||
__import__(objects_root + '.v2.terminate_vnf_request')
|
||||
__import__(objects_root + '.v2.threshold')
|
||||
__import__(objects_root + '.v2.threshold_criteria')
|
||||
__import__(objects_root + '.v2.threshold_crossed_notification')
|
||||
__import__(objects_root + '.v2.threshold_modifications')
|
||||
__import__(objects_root + '.v2.upload_vnf_package_from_uri_request')
|
||||
__import__(objects_root + '.v2.virtual_storage_resource_info')
|
||||
__import__(objects_root + '.v2.vnfc_info')
|
||||
|
|
|
@ -38,6 +38,8 @@ MACAddressField = ovoo_fields.MACAddressField
|
|||
NonNegativeIntegerField = ovoo_fields.NonNegativeIntegerField
|
||||
ObjectField = ovoo_fields.ObjectField
|
||||
StringField = ovoo_fields.StringField
|
||||
FloatField = ovoo_fields.FloatField
|
||||
NonNegativeFloatField = ovoo_fields.NonNegativeFloatField
|
||||
|
||||
|
||||
class BaseTackerEnum(Enum):
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
# 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 tacker.sol_refactored.objects import base
|
||||
from tacker.sol_refactored.objects import fields
|
||||
|
||||
|
||||
# NFV-SOL 003
|
||||
# - v3.3.1 6.5.2.8 (API version: 2.1.0)
|
||||
@base.TackerObjectRegistry.register
|
||||
class CreateThresholdRequestV2(base.TackerObject,
|
||||
base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objectType': fields.StringField(nullable=False),
|
||||
'objectInstanceId': fields.StringField(nullable=False),
|
||||
'subObjectInstanceIds': fields.ListOfStringsField(nullable=True),
|
||||
'criteria': fields.ObjectField('ThresholdCriteriaV2', nullable=False),
|
||||
'callbackUri': fields.UriField(nullable=False),
|
||||
'authentication': fields.ObjectField(
|
||||
'SubscriptionAuthentication', nullable=True),
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
# 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 tacker.sol_refactored.objects import base
|
||||
from tacker.sol_refactored.objects import fields
|
||||
|
||||
|
||||
# NFV-SOL 003
|
||||
# - v3.3.1 6.5.2.9 (API version: 2.1.0)
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdV2(base.TackerPersistentObject, base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.StringField(nullable=False),
|
||||
'objectType': fields.StringField(nullable=False),
|
||||
'objectInstanceId': fields.StringField(nullable=False),
|
||||
'subObjectInstanceIds': fields.ListOfStringsField(nullable=True),
|
||||
'criteria': fields.ObjectField('ThresholdCriteriaV2', nullable=False),
|
||||
'callbackUri': fields.UriField(nullable=False),
|
||||
'_links': fields.ObjectField(
|
||||
'ThresholdV2_Links', nullable=False),
|
||||
# NOTE: 'authentication' attribute is not included in the
|
||||
# original 'Threshold' data type definition.
|
||||
# It is necessary to keep this to be used at sending
|
||||
# notifications. Note that it is dropped at GET subscription.
|
||||
'authentication': fields.ObjectField(
|
||||
'SubscriptionAuthentication', nullable=True),
|
||||
# NOTE: 'metadata' attribute is not included in the
|
||||
# original 'Threshold' data type definition.
|
||||
# It is necessary to keep this to be used at setting prometheus config.
|
||||
'metadata': fields.KeyValuePairsField(nullable=True),
|
||||
}
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdV2_Links(base.TackerObject, base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'self': fields.ObjectField('Link', nullable=False),
|
||||
'object': fields.ObjectField('Link', nullable=True),
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
# 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 tacker.sol_refactored.objects import base
|
||||
from tacker.sol_refactored.objects import fields
|
||||
|
||||
|
||||
# NFV-SOL 003
|
||||
# - v3.3.1 6.5.3.4 (API version: 2.1.0)
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdCriteriaV2(base.TackerObject, base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'performanceMetric': fields.StringField(nullable=False),
|
||||
'thresholdType': fields.StringField(nullable=False),
|
||||
'simpleThresholdDetails': fields.ObjectField(
|
||||
'SimpleThresholdDetails', nullable=True),
|
||||
}
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class SimpleThresholdDetails(base.TackerObject, base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'thresholdValue': fields.FloatField(nullable=False),
|
||||
'hysteresis': fields.NonNegativeFloatField(nullable=False),
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
# 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 tacker.sol_refactored.objects import base
|
||||
from tacker.sol_refactored.objects import fields
|
||||
|
||||
|
||||
# NFV-SOL 003
|
||||
# - v3.3.1 6.5.2.4 (API version: 2.1.0)
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdCrossedNotificationV2(
|
||||
base.TackerObject,
|
||||
base.TackerObjectDictCompat
|
||||
):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'id': fields.StringField(nullable=False),
|
||||
'notificationType': fields.StringField(nullable=False),
|
||||
'timeStamp': fields.DateTimeField(nullable=False),
|
||||
'thresholdId': fields.StringField(nullable=False),
|
||||
'crossingDirection': fields.StringField(nullable=False),
|
||||
'objectType': fields.StringField(nullable=False),
|
||||
'objectInstanceId': fields.StringField(nullable=False),
|
||||
'subObjectInstanceId': fields.StringField(nullable=True),
|
||||
'performanceMetric': fields.StringField(nullable=False),
|
||||
'performanceValue': fields.StringField(nullable=False),
|
||||
# NOTE: ThresholdCrossedNotificationV2 does not support 'context' now
|
||||
'context': fields.KeyValuePairsField(nullable=True),
|
||||
'_links': fields.ObjectField(
|
||||
'ThresholdCrossedNotificationV2_Links',
|
||||
nullable=False),
|
||||
}
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdCrossedNotificationV2_Links(
|
||||
base.TackerObject,
|
||||
base.TackerObjectDictCompat
|
||||
):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'objectInstance': fields.ObjectField(
|
||||
'NotificationLink', nullable=True),
|
||||
'threshold': fields.ObjectField(
|
||||
'NotificationLink', nullable=False),
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
# 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 tacker.sol_refactored.objects import base
|
||||
from tacker.sol_refactored.objects import fields
|
||||
|
||||
|
||||
# NFV-SOL 003
|
||||
# - v3.3.1 6.5.2.12 (API version: 2.1.0)
|
||||
@base.TackerObjectRegistry.register
|
||||
class ThresholdModificationsV2(base.TackerObject, base.TackerObjectDictCompat):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'callbackUri': fields.StringField(nullable=True),
|
||||
'authentication': fields.ObjectField(
|
||||
'SubscriptionAuthentication', nullable=True),
|
||||
}
|
|
@ -206,6 +206,16 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
|
|||
self.assertEqual(notify_type, notify_mock_responses[0].request_body[
|
||||
'notificationType'])
|
||||
|
||||
def _check_no_notification(self, callback_url):
|
||||
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
|
||||
callback_url)
|
||||
self.assertEqual(0, len(notify_mock_responses))
|
||||
|
||||
def _get_crossing_direction(self, callback_url):
|
||||
notify_mock_responses = FAKE_SERVER_MANAGER.get_history(
|
||||
callback_url)
|
||||
return notify_mock_responses[0].request_body['crossingDirection']
|
||||
|
||||
@classmethod
|
||||
def delete_vnf_package(cls, pkg_id):
|
||||
path = f"/vnfpkgm/v1/vnf_packages/{pkg_id}"
|
||||
|
@ -376,6 +386,39 @@ class BaseVnfLcmKubernetesV2Test(base.BaseTestCase):
|
|||
return self.tacker_client.do_request(
|
||||
path, "DELETE", version="2.1.0")
|
||||
|
||||
def create_pm_threshold(self, req_body):
|
||||
path = "/vnfpm/v2/thresholds"
|
||||
return self.tacker_client.do_request(
|
||||
path, "POST", body=req_body, version="2.1.0")
|
||||
|
||||
def update_pm_threshold(self, pm_threshold_id, req_body):
|
||||
path = f"/vnfpm/v2/thresholds/{pm_threshold_id}"
|
||||
return self.tacker_client.do_request(
|
||||
path, "PATCH", body=req_body, version="2.1.0",
|
||||
content_type="application/mergepatch+json")
|
||||
|
||||
def pm_threshold(self, req_body):
|
||||
path = "/pm_threshold"
|
||||
return self.tacker_client.do_request(
|
||||
path, "POST", body=req_body, version="2.1.0")
|
||||
|
||||
def list_pm_threshold(self, filter_expr=None):
|
||||
path = "/vnfpm/v2/thresholds"
|
||||
if filter_expr:
|
||||
path = "{}?{}".format(path, urllib.parse.urlencode(filter_expr))
|
||||
return self.tacker_client.do_request(
|
||||
path, "GET", version="2.1.0")
|
||||
|
||||
def show_pm_threshold(self, pm_threshold_id):
|
||||
path = f"/vnfpm/v2/thresholds/{pm_threshold_id}"
|
||||
return self.tacker_client.do_request(
|
||||
path, "GET", version="2.1.0")
|
||||
|
||||
def delete_pm_threshold(self, pm_threshold_id):
|
||||
path = f"/vnfpm/v2/thresholds/{pm_threshold_id}"
|
||||
return self.tacker_client.do_request(
|
||||
path, "DELETE", version="2.1.0")
|
||||
|
||||
def prometheus_auto_scaling_alert(self, req_body):
|
||||
path = "/alert/auto_scaling"
|
||||
return self.tacker_client.do_request(
|
||||
|
|
|
@ -153,6 +153,15 @@ def max_sample_scale_in():
|
|||
}
|
||||
|
||||
|
||||
def scale_out():
|
||||
return {
|
||||
"type": "SCALE_OUT",
|
||||
"aspectId": "vdu2_aspect",
|
||||
"numberOfSteps": 1,
|
||||
"additionalParams": {"dummy-key": "dummy-value"}
|
||||
}
|
||||
|
||||
|
||||
def max_sample_heal(vnfc_ids):
|
||||
return {
|
||||
"vnfcInstanceId": vnfc_ids
|
||||
|
@ -756,6 +765,113 @@ def update_pm_job(callback_uri):
|
|||
}
|
||||
|
||||
|
||||
def pm_threshold_min(
|
||||
callback_uri, inst_id, host_ip,
|
||||
objectType="Vnf",
|
||||
sub_object_instance_id=None,
|
||||
p_metric=None,
|
||||
thresholdValue=55,
|
||||
hysteresis=30):
|
||||
metric = f"VCpuUsageMeanVnf.{inst_id}"
|
||||
if p_metric:
|
||||
metric = f"{p_metric}"
|
||||
return {
|
||||
"objectType": objectType,
|
||||
"objectInstanceId": inst_id,
|
||||
"subObjectInstanceIds": ([sub_object_instance_id]
|
||||
if sub_object_instance_id else []),
|
||||
"criteria": {
|
||||
"performanceMetric": metric,
|
||||
"thresholdType": "SIMPLE",
|
||||
"simpleThresholdDetails": {
|
||||
"thresholdValue": thresholdValue,
|
||||
"hysteresis": hysteresis
|
||||
}
|
||||
},
|
||||
"callbackUri": callback_uri,
|
||||
"metadata": {
|
||||
"monitoring": {
|
||||
"monitorName": "prometheus",
|
||||
"driverType": "external",
|
||||
"targetsInfo": [
|
||||
{
|
||||
"prometheusHost": host_ip,
|
||||
"prometheusHostPort": 50022,
|
||||
"authInfo": {
|
||||
"ssh_username": "root",
|
||||
"ssh_password": "root"
|
||||
},
|
||||
"alertRuleConfigPath":
|
||||
"/tmp",
|
||||
"prometheusReloadApiEndpoint":
|
||||
"http://localhost:9990/-/reload"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def pm_threshold_max(
|
||||
callback_uri, inst_id, host_ip,
|
||||
objectType="Vnf",
|
||||
sub_object_instance_id=None,
|
||||
p_metric=None,
|
||||
thresholdValue=55,
|
||||
hysteresis=30):
|
||||
metric = f"VCpuUsageMeanVnf.{inst_id}"
|
||||
if p_metric:
|
||||
metric = f"{p_metric}"
|
||||
return {
|
||||
"objectType": objectType,
|
||||
"objectInstanceId": inst_id,
|
||||
"subObjectInstanceIds": ([sub_object_instance_id]
|
||||
if sub_object_instance_id else []),
|
||||
"criteria": {
|
||||
"performanceMetric": metric,
|
||||
"thresholdType": "SIMPLE",
|
||||
"simpleThresholdDetails": {
|
||||
"thresholdValue": thresholdValue,
|
||||
"hysteresis": hysteresis
|
||||
}
|
||||
},
|
||||
"callbackUri": callback_uri,
|
||||
"authentication": {
|
||||
"authType": ["BASIC"],
|
||||
"paramsBasic": {
|
||||
"userName": "test",
|
||||
"password": "test"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"monitoring": {
|
||||
"monitorName": "prometheus",
|
||||
"driverType": "external",
|
||||
"targetsInfo": [
|
||||
{
|
||||
"prometheusHost": host_ip,
|
||||
"prometheusHostPort": 50022,
|
||||
"authInfo": {
|
||||
"ssh_username": "root",
|
||||
"ssh_password": "root"
|
||||
},
|
||||
"alertRuleConfigPath":
|
||||
"/tmp",
|
||||
"prometheusReloadApiEndpoint":
|
||||
"http://localhost:9990/-/reload"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def update_pm_threshold(callback_uri):
|
||||
return {
|
||||
"callbackUri": callback_uri
|
||||
}
|
||||
|
||||
|
||||
def pm_event(job_id, inst_id):
|
||||
return {
|
||||
"receiver": "receiver",
|
||||
|
@ -795,6 +911,51 @@ def pm_event(job_id, inst_id):
|
|||
}
|
||||
|
||||
|
||||
def pm_threshold(threshold_id, inst_id,
|
||||
sub_inst_id=None, value=99, p_metric=None):
|
||||
metric = f"VCpuUsageMeanVnf.{inst_id}"
|
||||
if p_metric:
|
||||
metric = f"{p_metric}"
|
||||
# This data simulates the complete request body sent by alertmanager.
|
||||
return {
|
||||
"receiver": "receiver",
|
||||
"status": "firing",
|
||||
"alerts": [
|
||||
{
|
||||
"status": "firing",
|
||||
"labels": {
|
||||
"receiver_type": "tacker",
|
||||
"function_type": "vnfpm_threshold",
|
||||
"threshold_id": threshold_id,
|
||||
"metric": metric,
|
||||
"object_instance_id": inst_id,
|
||||
"sub_object_instance_id": sub_inst_id
|
||||
},
|
||||
"annotations": {
|
||||
"value": value,
|
||||
},
|
||||
"startsAt": "2022-12-15T23:47:36.453Z",
|
||||
"endsAt": "0001-01-01T00:00:00Z",
|
||||
"generatorURL": "http://controller147:9090/graph?g0.expr=up%7B"
|
||||
"job%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",
|
||||
|
|
|
@ -46,6 +46,7 @@ max_sample_instantiate = paramgen.max_sample_instantiate(
|
|||
max_sample_terminate = paramgen.max_sample_terminate()
|
||||
max_sample_scale_out = paramgen.max_sample_scale_out()
|
||||
max_sample_scale_in = paramgen.max_sample_scale_in()
|
||||
scale_out = paramgen.scale_out()
|
||||
max_sample_heal = paramgen.max_sample_heal(["replace real vnfc ids"])
|
||||
|
||||
# if you instantiate with only one resource
|
||||
|
@ -78,6 +79,9 @@ with open("max_sample_scale_out", "w", encoding='utf-8') as f:
|
|||
with open("max_sample_scale_in", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(max_sample_scale_in, indent=2))
|
||||
|
||||
with open("scale_out", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(scale_out, indent=2))
|
||||
|
||||
with open("max_sample_heal", "w", encoding='utf-8') as f:
|
||||
f.write(json.dumps(max_sample_heal, indent=2))
|
||||
|
||||
|
|
|
@ -0,0 +1,782 @@
|
|||
# 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.objects import fields
|
||||
from tacker.tests.functional.common.fake_server import FakeServerManager
|
||||
from tacker.tests.functional.sol_kubernetes_v2 import base_v2
|
||||
from tacker.tests.functional.sol_kubernetes_v2 import paramgen
|
||||
|
||||
FAKE_SERVER_MANAGER = FakeServerManager()
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class VnfPmThresholdTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(VnfPmThresholdTest, 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(VnfPmThresholdTest, cls).tearDownClass()
|
||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
||||
|
||||
def setUp(self):
|
||||
super(VnfPmThresholdTest, self).setUp()
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'PUT', "/-/reload",
|
||||
status_code=202,
|
||||
response_headers={"Content-Type": "text/plain"})
|
||||
|
||||
def test_pm_threshold_autoscaling_min(self):
|
||||
"""Test PM Threshold operations with omitting except for required attributes
|
||||
|
||||
* About attributes:
|
||||
Omit except for required attributes.
|
||||
Only the following cardinality attributes are set.
|
||||
- 1
|
||||
- 1..N (1)
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- 1. Create a new VNF instance resource
|
||||
- 2. Instantiate a VNF instance
|
||||
- 3. PMThreshold-Create 1
|
||||
- 4. PM-Threshold 1
|
||||
- 5. LCM-Scale
|
||||
- 6. PMThreshold-Create 2
|
||||
- 7. PM-Threshold 2
|
||||
- 8. PMThreshold-Create 3
|
||||
- 9. PM-Threshold 3
|
||||
- 10. PMThreshold-Create 4
|
||||
- 11. PM-Threshold 4
|
||||
- 12. Terminate a VNF instance
|
||||
- 13. Delete a VNF instance
|
||||
|
||||
* About PMThreshold-Create 1-4/PM-Threshold 1-4:
|
||||
PMThreshold-Create 1:
|
||||
"objectType": "vnf"
|
||||
no "subObjectInstanceIds"
|
||||
"performanceMetric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
PMThreshold-Create 2:
|
||||
"objectType": "Vnfc"
|
||||
"subObjectInstanceIds": {rsc}
|
||||
"performanceMetric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
PMThreshold-Create 3:
|
||||
"objectType": "VnfIntCp"
|
||||
"subObjectInstanceIds": "eth0"
|
||||
"performanceMetric": "ByteIncomingVnfIntCp"
|
||||
PMThreshold-Create 4:
|
||||
"objectType": "VnfExtCp",
|
||||
"subObjectInstanceIds": "eth0"
|
||||
"performanceMetric": "ByteIncomingVnfExtCp"
|
||||
PMThreshold-Create 1-4 uses different types of "objectType".
|
||||
|
||||
PM-Threshold 1:
|
||||
"metric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
no "sub_object_instance_id"
|
||||
PM-Threshold 2:
|
||||
"metric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
"sub_object_instance_id": {rsc}
|
||||
PM-Threshold 3:
|
||||
"metric": "ByteIncomingVnfIntCp"
|
||||
"sub_object_instance_id": "eth0",
|
||||
PM-Threshold 4:
|
||||
"metric": "ByteIncomingVnfExtCp"
|
||||
"sub_object_instance_id": "eth0"
|
||||
"""
|
||||
# 1. LCM-Create: Create a new VNF instance resource
|
||||
create_req = paramgen.pm_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']
|
||||
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)
|
||||
rsc = body['instantiatedVnfInfo']['vnfcInfo'][0]['id']
|
||||
|
||||
# 3. PMThreshold-Create 1
|
||||
pm_expected_attrs = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
sub_req = paramgen.pm_threshold_min(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_1 = body.get('id')
|
||||
|
||||
# 4. PM-Threshold 1
|
||||
sub_req = paramgen.pm_threshold(pm_threshold_id_1, inst_id)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 5. LCM-Scale
|
||||
# Scale out a VNF instance
|
||||
scale_out_req = paramgen.scale_out()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# 6. PMThreshold-Create 2
|
||||
pm_expected_attrs_sub = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'subObjectInstanceIds',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
sub_req = paramgen.pm_threshold_min(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="Vnfc",
|
||||
sub_object_instance_id=rsc
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_2 = body.get('id')
|
||||
|
||||
# 7. PM-Threshold 2
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_2, inst_id,
|
||||
sub_inst_id=rsc,
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 8. PMThreshold-Create 3
|
||||
sub_req = paramgen.pm_threshold_min(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="VnfIntCp",
|
||||
sub_object_instance_id="eth0",
|
||||
p_metric="ByteIncomingVnfIntCp"
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_3 = body.get('id')
|
||||
|
||||
# 9. PM-Threshold 3
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_3, inst_id,
|
||||
sub_inst_id="eth0",
|
||||
p_metric="ByteIncomingVnfIntCp"
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 10. PMThreshold-Create 4
|
||||
sub_req = paramgen.pm_threshold_min(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="VnfExtCp",
|
||||
sub_object_instance_id="eth0",
|
||||
p_metric="ByteIncomingVnfExtCp"
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_4 = body.get('id')
|
||||
|
||||
# 11. PM-Threshold 4
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_4, inst_id,
|
||||
sub_inst_id="eth0",
|
||||
p_metric="ByteIncomingVnfExtCp"
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_1)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_2)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_3)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_4)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
# 12. 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'])
|
||||
|
||||
# 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_pm_threshold_autoscaling_max(self):
|
||||
"""Test PM Threshold operations with all attributes set
|
||||
|
||||
* About attributes:
|
||||
All of the following cardinality attributes are set.
|
||||
In addition, 0..N or 1..N attributes are set to 2 or more.
|
||||
- 0..1 (1)
|
||||
- 0..N (2 or more)
|
||||
- 1
|
||||
- 1..N (2 or more)
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- 1. Create a new VNF instance resource
|
||||
- 2. Instantiate a VNF instance
|
||||
- 3. PMThreshold-Create 1
|
||||
- 4. PM-Threshold 1
|
||||
- 5. LCM-Scale
|
||||
- 6. PMThreshold-Create 2
|
||||
- 7. PM-Threshold 2
|
||||
- 8. PMThreshold-Create 3
|
||||
- 9. PM-Threshold 3
|
||||
- 10. PMThreshold-Create 4
|
||||
- 11. PM-Threshold 4
|
||||
- 12. Terminate a VNF instance
|
||||
- 13. Delete a VNF instance
|
||||
|
||||
* About PMThreshold-Create 1-4/PM-Threshold 1-4:
|
||||
PMThreshold-Create 1:
|
||||
"objectType": "vnf"
|
||||
no "subObjectInstanceIds"
|
||||
"performanceMetric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
PMThreshold-Create 2:
|
||||
"objectType": "Vnfc"
|
||||
"subObjectInstanceIds": {rsc}
|
||||
"performanceMetric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
PMThreshold-Create 3:
|
||||
"objectType": "VnfIntCp"
|
||||
"subObjectInstanceIds": "eth0"
|
||||
"performanceMetric": "ByteIncomingVnfIntCp"
|
||||
PMThreshold-Create 4:
|
||||
"objectType": "VnfExtCp",
|
||||
"subObjectInstanceIds": "eth0"
|
||||
"performanceMetric": "ByteIncomingVnfExtCp"
|
||||
PMThreshold-Create 1-4 uses different types of "objectType".
|
||||
|
||||
PM-Threshold 1:
|
||||
"metric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
no "sub_object_instance_id"
|
||||
PM-Threshold 2:
|
||||
"metric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
"sub_object_instance_id": {rsc}
|
||||
PM-Threshold 3:
|
||||
"metric": "ByteIncomingVnfIntCp"
|
||||
"sub_object_instance_id": "eth0",
|
||||
PM-Threshold 4:
|
||||
"metric": "ByteIncomingVnfExtCp"
|
||||
"sub_object_instance_id": "eth0"
|
||||
"""
|
||||
# 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.pm_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']
|
||||
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)
|
||||
rsc = body['instantiatedVnfInfo']['vnfcInfo'][0]['id']
|
||||
|
||||
# 3. PMThreshold-Create 1
|
||||
pm_expected_attrs = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_1 = body.get('id')
|
||||
|
||||
# 4. PM-Threshold 1
|
||||
sub_req = paramgen.pm_threshold(pm_threshold_id_1, inst_id)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 5. LCM-Scale
|
||||
# Scale out a VNF instance
|
||||
scale_out_req = paramgen.scale_out()
|
||||
resp, body = self.scale_vnf_instance(inst_id, scale_out_req)
|
||||
self.assertEqual(202, resp.status_code)
|
||||
self.check_resp_headers_in_operation_task(resp)
|
||||
|
||||
lcmocc_id = os.path.basename(resp.headers['Location'])
|
||||
self.wait_lcmocc_complete(lcmocc_id)
|
||||
|
||||
# 6. PMThreshold-Create 2
|
||||
pm_expected_attrs_sub = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'subObjectInstanceIds',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="Vnfc",
|
||||
sub_object_instance_id=rsc
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_2 = body.get('id')
|
||||
|
||||
# 7. PM-Threshold 2
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_2, inst_id,
|
||||
sub_inst_id=rsc,
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 8. PMThreshold-Create 3
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="VnfIntCp",
|
||||
sub_object_instance_id="eth0",
|
||||
p_metric="ByteIncomingVnfIntCp"
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_3 = body.get('id')
|
||||
|
||||
# 9. PM-Threshold 3
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_3, inst_id,
|
||||
sub_inst_id="eth0",
|
||||
p_metric="ByteIncomingVnfIntCp"
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 10. PMThreshold-Create 4
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip,
|
||||
objectType="VnfExtCp",
|
||||
sub_object_instance_id="eth0",
|
||||
p_metric="ByteIncomingVnfExtCp"
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs_sub)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_4 = body.get('id')
|
||||
|
||||
# 11. PM-Threshold 4
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_4, inst_id,
|
||||
sub_inst_id="eth0",
|
||||
p_metric="ByteIncomingVnfExtCp"
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_1)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_2)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_3)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_4)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
# 12. 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']
|
||||
)
|
||||
|
||||
# 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_pm_threshold_with_all_attibutes(self):
|
||||
"""Test PM Threshold operations with all attributes set
|
||||
|
||||
* About attributes:
|
||||
All of the following cardinality attributes are set.
|
||||
In addition, 0..N or 1..N attributes are set to 2 or more.
|
||||
- 0..1 (1)
|
||||
- 0..N (2 or more)
|
||||
- 1
|
||||
- 1..N (2 or more)
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- 1. Create a new VNF instance resource
|
||||
- 2. Instantiate a VNF instance
|
||||
- 3. PMThreshold-Create 5
|
||||
- 4. PM-Threshold 5-1
|
||||
- 5. PM-Threshold 5-2
|
||||
- 6. PM-Threshold 5-3
|
||||
- 7. PM-Threshold 5-4
|
||||
- 8. PM-Threshold 5-5
|
||||
- 9. PM-Threshold 5-6
|
||||
- 10. Terminate a VNF instance
|
||||
- 11. Delete a VNF instance
|
||||
|
||||
* About PMThreshold-Create 5/PM-Threshold 5:
|
||||
PMThreshold-Create 5:
|
||||
"objectType": "vnf"
|
||||
no "subObjectInstanceIds"
|
||||
"performanceMetric": "VCpuUsageMeanVnf.{inst_id}"
|
||||
This is a mediocre threshold.
|
||||
|
||||
PM-Threshold 5-1:
|
||||
"value": 99
|
||||
PM-Threshold 5-2:
|
||||
"value": 105
|
||||
PM-Threshold 5-3:
|
||||
"value": 80
|
||||
PM-Threshold 5-4:
|
||||
"value": 40
|
||||
PM-Threshold 5-5:
|
||||
"value": 20
|
||||
PM-Threshold 5-6:
|
||||
"value": 10
|
||||
At PM-Threshold 5-1 PM-Threshold 5-5, the notification will be
|
||||
triggered.
|
||||
"""
|
||||
# 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.pm_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']
|
||||
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. PMThreshold-Create 5
|
||||
pm_expected_attrs = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id_5 = body.get('id')
|
||||
|
||||
# 4. PM-Threshold 5-1
|
||||
sub_req = paramgen.pm_threshold(pm_threshold_id_5, inst_id)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self.assertEqual('UP', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 5. PM-Threshold 5-2
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_5, inst_id,
|
||||
value=105,
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self._check_no_notification(callback_url)
|
||||
|
||||
# 6. PM-Threshold 5-3
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_5, inst_id,
|
||||
value=80
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self._check_no_notification(callback_url)
|
||||
|
||||
# 7. PM-Threshold 5-4
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_5, inst_id,
|
||||
value=40
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self._check_no_notification(callback_url)
|
||||
|
||||
# 8. PM-Threshold 5-5
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_5, inst_id,
|
||||
value=20
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self.assertEqual('DOWN', self._get_crossing_direction(callback_url))
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 9. PM-Threshold 5-6
|
||||
sub_req = paramgen.pm_threshold(
|
||||
pm_threshold_id_5, inst_id,
|
||||
value=10
|
||||
)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# wait a bit because there is a bit time lag between lcmocc DB
|
||||
# update and terminate completion.
|
||||
time.sleep(5)
|
||||
self._check_no_notification(callback_url)
|
||||
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id_5)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
# 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(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']
|
||||
)
|
||||
|
||||
# 11. 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)
|
|
@ -0,0 +1,336 @@
|
|||
# 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.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 VnfPmThresholdTest(base_v2.BaseVnfLcmKubernetesV2Test):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(VnfPmThresholdTest, 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(VnfPmThresholdTest, cls).tearDownClass()
|
||||
cls.delete_vnf_package(cls.vnf_pkg_1)
|
||||
|
||||
def setUp(self):
|
||||
super(VnfPmThresholdTest, self).setUp()
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'PUT', "/-/reload",
|
||||
status_code=202,
|
||||
response_headers={"Content-Type": "text/plain"})
|
||||
|
||||
def test_pm_threshold_interface_min(self):
|
||||
"""Test PM Threshold operations with all attributes set
|
||||
|
||||
* About attributes:
|
||||
Omit except for required attributes.
|
||||
Only the following cardinality attributes are set.
|
||||
- 1
|
||||
- 1..N (1)
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- 1. Create a new VNF instance resource
|
||||
- 2. Instantiate a VNF instance
|
||||
- 3. PMThreshold-Create
|
||||
- 4. PMThreshold-Update
|
||||
- 5. PM-Threshold
|
||||
- 6. PMThreshold-List
|
||||
- 7. PMThreshold-Show
|
||||
- 8. PMThreshold-Delete
|
||||
- 9. Terminate a VNF instance
|
||||
- 10. 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.pm_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']
|
||||
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. PMThreshold-Create
|
||||
pm_expected_attrs = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
sub_req = paramgen.pm_threshold_min(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip
|
||||
)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id = body.get('id')
|
||||
|
||||
# 4. PMThreshold-Update
|
||||
callback_url = os.path.join(
|
||||
base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName
|
||||
)
|
||||
callback_url = f'{callback_url}_1'
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
# Because the update of the threshold is executed, the 'callback_url'
|
||||
# is updated, so the url of the fake server needs to be modified.
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'GET', callback_url, status_code=204)
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'POST', callback_url, status_code=204)
|
||||
update_req = paramgen.update_pm_threshold(callback_uri)
|
||||
resp, body = self.update_pm_threshold(pm_threshold_id, update_req)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
self.check_resp_body(body, ['callbackUri'])
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
|
||||
# 5. PM-Threshold
|
||||
sub_req = paramgen.pm_threshold(pm_threshold_id, inst_id)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 6. PMThreshold-List
|
||||
resp, body = self.list_pm_threshold()
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
for sbsc in body:
|
||||
self.check_resp_body(sbsc, pm_expected_attrs)
|
||||
|
||||
# 7. PMThreshold-Show
|
||||
resp, body = self.show_pm_threshold(pm_threshold_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
|
||||
# 8. PMThreshold-Delete
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
# 9. 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'])
|
||||
|
||||
# 10. 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_pm_threshold_interface_max(self):
|
||||
"""Test PM Threshold operations with all attributes set
|
||||
|
||||
* About attributes:
|
||||
All of the following cardinality attributes are set.
|
||||
In addition, 0..N or 1..N attributes are set to 2 or more.
|
||||
- 0..1 (1)
|
||||
- 0..N (2 or more)
|
||||
- 1
|
||||
- 1..N (2 or more)
|
||||
|
||||
* About LCM operations:
|
||||
This test includes the following operations.
|
||||
- 1. Create a new VNF instance resource
|
||||
- 2. Instantiate a VNF instance
|
||||
- 3. PMThreshold-Create
|
||||
- 4. PMThreshold-Update
|
||||
- 5. PM-Threshold
|
||||
- 6. PMThreshold-List
|
||||
- 7. PMThreshold-Show
|
||||
- 8. PMThreshold-Delete
|
||||
- 9. Terminate a VNF instance
|
||||
- 10. 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']
|
||||
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. PMThreshold-Create
|
||||
pm_expected_attrs = [
|
||||
'id',
|
||||
'objectType',
|
||||
'objectInstanceId',
|
||||
'criteria',
|
||||
'callbackUri',
|
||||
'_links'
|
||||
]
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
sub_req = paramgen.pm_threshold_max(
|
||||
callback_uri, inst_id, self.fake_prometheus_ip)
|
||||
resp, body = self.create_pm_threshold(sub_req)
|
||||
self.assertEqual(201, resp.status_code)
|
||||
self.check_resp_headers_in_create(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
pm_threshold_id = body.get('id')
|
||||
|
||||
# 4. PMThreshold-Update
|
||||
callback_url = os.path.join(base_v2.MOCK_NOTIFY_CALLBACK_URL,
|
||||
self._testMethodName)
|
||||
callback_url = f'{callback_url}_1'
|
||||
callback_uri = ('http://localhost:'
|
||||
f'{base_v2.FAKE_SERVER_MANAGER.SERVER_PORT}'
|
||||
f'{callback_url}')
|
||||
# Because the update of the threshold is executed, the 'callback_url'
|
||||
# is updated, so the url of the fake server needs to be modified.
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'GET', callback_url, status_code=204)
|
||||
base_v2.FAKE_SERVER_MANAGER.set_callback(
|
||||
'POST', callback_url, status_code=204)
|
||||
update_req = paramgen.update_pm_threshold(callback_uri)
|
||||
resp, body = self.update_pm_threshold(pm_threshold_id, update_req)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
self.check_resp_body(body, ['callbackUri'])
|
||||
# Test notification
|
||||
self.assert_notification_get(callback_url)
|
||||
|
||||
# 5. PM-Threshold
|
||||
sub_req = paramgen.pm_threshold(pm_threshold_id, inst_id)
|
||||
resp, body = self.pm_threshold(sub_req)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
# The creation of "pm_threshold" will be asynchronous
|
||||
# and wait for the creation to end
|
||||
time.sleep(5)
|
||||
self._check_notification(
|
||||
callback_url, 'ThresholdCrossedNotification')
|
||||
|
||||
# 6. PMThreshold-List
|
||||
filter_expr = {'filter': '(eq,objectType,VirtualCompute)'}
|
||||
resp, body = self.list_pm_threshold(filter_expr)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
for sbsc in body:
|
||||
self.check_resp_body(sbsc, pm_expected_attrs)
|
||||
|
||||
# 7. PMThreshold-Show
|
||||
resp, body = self.show_pm_threshold(pm_threshold_id)
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.check_resp_headers_in_get(resp)
|
||||
self.check_resp_body(body, pm_expected_attrs)
|
||||
|
||||
# 8. PMThreshold-Delete
|
||||
resp, body = self.delete_pm_threshold(pm_threshold_id)
|
||||
self.assertEqual(204, resp.status_code)
|
||||
self.check_resp_headers_in_delete(resp)
|
||||
|
||||
# 9. 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'])
|
||||
|
||||
# 10. 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)
|
|
@ -13,11 +13,22 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import os
|
||||
import requests
|
||||
from unittest import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import fm_alarm_utils
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests import base
|
||||
|
||||
from tacker.tests.unit.sol_refactored.samples import fakes_for_fm
|
||||
|
||||
SAMPLE_VNFD_ID = "b1bb0ce7-ebca-4fa7-95ed-4840d7000000"
|
||||
SAMPLE_FLAVOUR_ID = "simple"
|
||||
|
@ -27,6 +38,7 @@ class TestCommontScriptUtils(base.BaseTestCase):
|
|||
|
||||
def setUp(self):
|
||||
super(TestCommontScriptUtils, self).setUp()
|
||||
objects.register_all()
|
||||
cur_dir = os.path.dirname(__file__)
|
||||
sample_dir = os.path.join(cur_dir, "..", "samples")
|
||||
|
||||
|
@ -566,3 +578,276 @@ class TestCommontScriptUtils(base.BaseTestCase):
|
|||
# removed
|
||||
self.assertNotIn(vl, top_hot['resources'])
|
||||
self.assertNotIn(vl_subnet, top_hot['resources'])
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-2', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['BASIC'],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test')))
|
||||
|
||||
# execute basic_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = objects.LccnSubscriptionV2(
|
||||
id='sub-3', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['OAUTH2_CLIENT_CREDENTIALS'],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'))))
|
||||
|
||||
# execute oauth2
|
||||
common_script_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2_mtls = objects.LccnSubscriptionV2(
|
||||
id='sub-4', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CERT"],
|
||||
paramsOauth2ClientCert=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2ClientCert(
|
||||
clientId='test',
|
||||
certificateRef=objects.
|
||||
ParamsOauth2ClientCert_CertificateRef(
|
||||
type='x5t#256',
|
||||
value='03c6e188d1fe5d3da8c9bc9a8dc531a2'
|
||||
'b3ecf812b03aede9bec7ba1b410b6b64'
|
||||
),
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# execute oauth2 mtls
|
||||
common_script_utils.send_notification(
|
||||
subsc_oauth2_mtls, notif_data_no_auth)
|
||||
|
||||
cfg.CONF.set_override("notification_verify_cert", "True",
|
||||
group="v2_vnfm")
|
||||
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-5', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-6', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['BASIC'],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test')
|
||||
)
|
||||
)
|
||||
|
||||
# execute basic_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = objects.LccnSubscriptionV2(
|
||||
id='sub-7', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['OAUTH2_CLIENT_CREDENTIALS'],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token')
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# execute oauth2
|
||||
common_script_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = Exception()
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
common_script_utils.send_notification(
|
||||
subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
common_script_utils.test_notification(subsc_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
common_script_utils.test_notification, subsc_no_auth)
|
||||
|
||||
class mock_session():
|
||||
|
||||
def request(url, method, raise_exc=False, **kwargs):
|
||||
resp = requests.Response()
|
||||
resp.status_code = 400
|
||||
resp.headers['Content-Type'] = 'application/zip'
|
||||
return resp
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, '_decode_body')
|
||||
@mock.patch.object(http_client.NoAuthHandle, 'get_session')
|
||||
def test_test_notification_error(self, mock_session, mock_decode_body):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
mock_session.return_value = self.mock_session
|
||||
mock_decode_body.return_value = None
|
||||
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
common_script_utils.test_notification, subsc_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_fm_subscription(self, mock_resp):
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
subsc_basic_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
subsc_basic_auth.authentication = objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test'))
|
||||
|
||||
common_script_utils.test_notification(
|
||||
subsc_basic_auth, common_script_utils.NOTIFY_TYPE_FM)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_pm_job(self, mock_do_request):
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_do_request.return_value = (resp_no_auth, None)
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
)
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
common_script_utils.test_notification(
|
||||
pm_job, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_fm_subscribtion(self, mock_resp):
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
subsc_basic_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
subsc_basic_auth.authentication = objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test'))
|
||||
|
||||
alarm = objects.AlarmV1.from_dict(
|
||||
fakes_for_fm.alarm_example)
|
||||
notif_data = fm_alarm_utils.make_alarm_notif_data(
|
||||
subsc_basic_auth, alarm, 'http://127.0.0.1:9890')
|
||||
|
||||
common_script_utils.send_notification(
|
||||
subsc_basic_auth, notif_data)
|
||||
self.assertEqual(1, mock_resp.call_count)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_pm_job(self, mock_resp):
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
objectType='VNF',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
sub_instance_ids = ['1', '2', '3', '4']
|
||||
notif_data = pm_job_utils.make_pm_notif_data('instance_id',
|
||||
sub_instance_ids,
|
||||
'report_id',
|
||||
pm_job,
|
||||
'2008-01-03 08:04:34',
|
||||
'endpoint')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
common_script_utils.send_notification(
|
||||
pm_job, notif_data, common_script_utils.NOTIFY_TYPE_PM)
|
||||
|
|
|
@ -12,18 +12,16 @@
|
|||
# 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 copy
|
||||
import requests
|
||||
from unittest import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import fm_alarm_utils as alarm_utils
|
||||
from tacker.sol_refactored.common import fm_subscription_utils as subsc_utils
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests import base
|
||||
from tacker.tests.unit.sol_refactored.samples import fakes_for_fm
|
||||
|
@ -60,156 +58,6 @@ class TestFmSubscriptionUtils(base.BaseTestCase):
|
|||
result = subsc_utils.get_subsc_all(context)
|
||||
self.assertEqual(fakes_for_fm.fm_subsc_example['id'], result[0].id)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
alarm = objects.AlarmV1.from_dict(fakes_for_fm.alarm_example)
|
||||
notif_data_no_auth = alarm_utils.make_alarm_notif_data(
|
||||
subsc_no_auth, alarm, 'http://127.0.0.1:9890')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# 1. execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = copy.deepcopy(subsc_no_auth)
|
||||
subsc_basic_auth.authentication = objects.SubscriptionAuthentication(
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test'))
|
||||
|
||||
# 2. execute basic_auth
|
||||
subsc_utils.send_notification(subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = copy.deepcopy(subsc_no_auth)
|
||||
subsc_oauth2.authentication = objects.SubscriptionAuthentication(
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token')))
|
||||
|
||||
# 3. execute oauth2
|
||||
subsc_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2_mtls = copy.deepcopy(subsc_no_auth)
|
||||
subsc_oauth2_mtls.authentication = objects.SubscriptionAuthentication(
|
||||
paramsOauth2ClientCert=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2ClientCert(
|
||||
clientId='test',
|
||||
certificateRef=objects.
|
||||
ParamsOauth2ClientCert_CertificateRef(
|
||||
type='x5t#256',
|
||||
value='03c6e188d1fe5d3da8c9bc9a8dc531a2'
|
||||
'b3ecf812b03aede9bec7ba1b410b6b64'
|
||||
),
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
# 4. execute oauth2 mTLS
|
||||
subsc_utils.send_notification(subsc_oauth2_mtls, notif_data_no_auth)
|
||||
|
||||
cfg.CONF.set_override("notification_verify_cert", "True",
|
||||
group="v2_vnfm")
|
||||
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
|
||||
# 5. execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = copy.deepcopy(subsc_no_auth)
|
||||
subsc_basic_auth.authentication = objects.SubscriptionAuthentication(
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test'))
|
||||
|
||||
# 6. execute basic_auth
|
||||
subsc_utils.send_notification(subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = copy.deepcopy(subsc_no_auth)
|
||||
subsc_oauth2.authentication = objects.SubscriptionAuthentication(
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token')))
|
||||
|
||||
# 7. execute oauth2
|
||||
subsc_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
self.assertEqual(7, mock_resp.call_count)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
alarm = objects.AlarmV1.from_dict(fakes_for_fm.alarm_example)
|
||||
notif_data_no_auth = alarm_utils.make_alarm_notif_data(
|
||||
subsc_no_auth, alarm, 'http://127.0.0.1:9890')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
self.assertLogs(LOG, 'ERROR')
|
||||
|
||||
def test_send_notification_error(self):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
alarm = objects.AlarmV1.from_dict(fakes_for_fm.alarm_example)
|
||||
notif_data_no_auth = alarm_utils.make_alarm_notif_data(
|
||||
subsc_no_auth, alarm, 'http://127.0.0.1:9890')
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
self.assertLogs(LOG, 'EXCEPTION')
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.test_notification(subsc_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
subsc_utils.test_notification, subsc_no_auth)
|
||||
|
||||
class mock_session():
|
||||
|
||||
def request(url, method, raise_exc=False, **kwargs):
|
||||
resp = requests.Response()
|
||||
resp.status_code = 400
|
||||
resp.headers['Content-Type'] = 'application/zip'
|
||||
return resp
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, '_decode_body')
|
||||
@mock.patch.object(http_client.NoAuthHandle, 'get_session')
|
||||
def test_test_notification_error(self, mock_session, mock_decode_body):
|
||||
subsc_no_auth = objects.FmSubscriptionV1.from_dict(
|
||||
fakes_for_fm.fm_subsc_example)
|
||||
|
||||
mock_session.return_value = self.mock_session
|
||||
mock_decode_body.return_value = None
|
||||
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
subsc_utils.test_notification, subsc_no_auth)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
def test_get_matched_subscs(self, mock_subscs):
|
||||
inst = objects.VnfInstanceV2(id='test-instance', vnfProvider='company')
|
||||
|
|
|
@ -13,15 +13,12 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
import requests
|
||||
from unittest import mock
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests import base
|
||||
|
@ -120,236 +117,3 @@ class TestPmJobUtils(base.BaseTestCase):
|
|||
result = pm_job_utils.make_pm_job_links(pm_job, 'endpoint')
|
||||
href = result.self.href
|
||||
self.assertEqual('endpoint/vnfpm/v2/pm_jobs/pm_job_1', href)
|
||||
|
||||
def test_get_notification_auth_handle(self):
|
||||
pm_job = objects.PmJobV2(id='pm_job_1')
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job)
|
||||
res = type(result).__name__
|
||||
name = type(http_client.NoAuthHandle()).__name__
|
||||
self.assertEqual(name, res)
|
||||
pm_job_1_auth = objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test',
|
||||
)
|
||||
)
|
||||
pm_job_1 = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
authentication=pm_job_1_auth)
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job_1)
|
||||
self.assertIsInstance(result, http_client.BasicAuthHandle)
|
||||
|
||||
pm_job_2 = objects.PmJobV2(
|
||||
id='pm_job_2',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CREDENTIALS"],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test',
|
||||
clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
))
|
||||
)
|
||||
)
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job_2)
|
||||
self.assertIsInstance(result, http_client.OAuth2AuthHandle)
|
||||
|
||||
pm_job_3 = objects.PmJobV2(
|
||||
id='pm_job_3',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CERT"],
|
||||
paramsOauth2ClientCert=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2ClientCert(
|
||||
clientId='test',
|
||||
certificateRef=objects.
|
||||
ParamsOauth2ClientCert_CertificateRef(
|
||||
type='x5t#256',
|
||||
value='03c6e188d1fe5d3da8c9bc9a8dc531a2'
|
||||
'b3ecf812b03aede9bec7ba1b410b6b64'
|
||||
),
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
))
|
||||
)
|
||||
)
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job_3)
|
||||
self.assertIsInstance(result, http_client.OAuth2MtlsAuthHandle)
|
||||
|
||||
cfg.CONF.set_override("notification_verify_cert", "True",
|
||||
group="v2_vnfm")
|
||||
|
||||
pm_job_4 = objects.PmJobV2(
|
||||
id='pm_job_4',
|
||||
authentication=pm_job_1_auth)
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job_4)
|
||||
res = type(result).__name__
|
||||
name = type(http_client.BasicAuthHandle('test', 'test')).__name__
|
||||
self.assertEqual(name, res)
|
||||
|
||||
pm_job_5 = objects.PmJobV2(
|
||||
id='pm_job_5',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CREDENTIALS"],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test',
|
||||
clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
))
|
||||
)
|
||||
)
|
||||
result = pm_job_utils._get_notification_auth_handle(pm_job_5)
|
||||
res = type(result).__name__
|
||||
name = type(http_client.OAuth2AuthHandle(
|
||||
None, 'http://127.0.0.1/token', 'test', 'test')).__name__
|
||||
self.assertEqual(name, res)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification(self, mock_do_request):
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_do_request.return_value = (resp_no_auth, None)
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
)
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
pm_job_utils.test_notification(pm_job)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_error_code(self, mock_do_request):
|
||||
# execute not 204
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 500
|
||||
mock_do_request.return_value = (resp_no_auth, None)
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
)
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
pm_job_utils.test_notification, pm_job=pm_job)
|
||||
|
||||
class mock_session():
|
||||
|
||||
def request(url, method, raise_exc=False, **kwargs):
|
||||
resp = requests.Response()
|
||||
resp.status_code = 400
|
||||
resp.headers['Content-Type'] = 'application/zip'
|
||||
return resp
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, '_decode_body')
|
||||
@mock.patch.object(http_client.BasicAuthHandle, 'get_session')
|
||||
def test_test_notification_error(self, mock_session, mock_decode_body):
|
||||
# execute not 204
|
||||
mock_session.return_value = self.mock_session
|
||||
mock_decode_body.return_value = None
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
pm_job_utils.test_notification, pm_job=pm_job)
|
||||
|
||||
def test_make_pm_notif_data(self):
|
||||
sub_instance_ids = ['1', '2', '3', '4']
|
||||
pm_job = objects.PmJobV2(id='pm_job_1',
|
||||
objectType='VNF'
|
||||
)
|
||||
result = pm_job_utils.make_pm_notif_data('instance_id',
|
||||
sub_instance_ids,
|
||||
'report_id',
|
||||
pm_job,
|
||||
'2008-01-03 08:04:34',
|
||||
'endpoint')
|
||||
self.assertEqual('instance_id', result.objectInstanceId)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification(self, mock_resp):
|
||||
pm_job = objects.PmJobV2(id='pm_job_1',
|
||||
objectType='VNF',
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
sub_instance_ids = ['1', '2', '3', '4']
|
||||
notif_data = pm_job_utils.make_pm_notif_data('instance_id',
|
||||
sub_instance_ids,
|
||||
'report_id',
|
||||
pm_job,
|
||||
'2008-01-03 08:04:34',
|
||||
'endpoint')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
# execute no_auth
|
||||
pm_job_utils.send_notification(pm_job, notif_data)
|
||||
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
objectType='VNF',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
sub_instance_ids = ['1', '2', '3', '4']
|
||||
notif_data = pm_job_utils.make_pm_notif_data('instance_id',
|
||||
sub_instance_ids,
|
||||
'report_id',
|
||||
pm_job,
|
||||
'2008-01-03 08:04:34',
|
||||
'endpoint')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
# execute basic_auth
|
||||
pm_job_utils.send_notification(pm_job, notif_data)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error(self, mock_resp):
|
||||
pm_job = objects.PmJobV2(
|
||||
id='pm_job_1',
|
||||
objectType='VNF',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
),
|
||||
callbackUri='http://127.0.0.1/callback'
|
||||
)
|
||||
sub_instance_ids = ['1', '2', '3', '4']
|
||||
notif_data = pm_job_utils.make_pm_notif_data('instance_id',
|
||||
sub_instance_ids,
|
||||
'report_id',
|
||||
pm_job,
|
||||
'2008-01-03 08:04:34',
|
||||
'endpoint')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = Exception()
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
# execute basic_auth
|
||||
pm_job_utils.send_notification(pm_job, notif_data)
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
# 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 unittest import mock
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests import base
|
||||
|
||||
|
||||
class TestPmThresholdUtils(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPmThresholdUtils, self).setUp()
|
||||
objects.register_all()
|
||||
self.context = context.get_admin_context()
|
||||
self.context.api_version = api_version.APIVersion('2.1.0')
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
def test_get_pm_threshold_all(self, mock_pm):
|
||||
mock_pm.return_value = [objects.ThresholdV2(id='pm_threshold_1')]
|
||||
|
||||
result = pm_threshold_utils.get_pm_threshold_all(self.context)
|
||||
self.assertEqual('pm_threshold_1', result[0].id)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_get_pm_threshold(self, mock_pm):
|
||||
mock_pm.return_value = objects.ThresholdV2(id='pm_threshold_1')
|
||||
|
||||
result = pm_threshold_utils.get_pm_threshold(
|
||||
self.context, 'pm_threshold_1')
|
||||
self.assertEqual('pm_threshold_1', result.id)
|
||||
|
||||
def test_get_pm_threshold_state(self):
|
||||
pm_threshold = objects.ThresholdV2(id='pm_threshold_1')
|
||||
|
||||
result = pm_threshold_utils.get_pm_threshold_state(
|
||||
pm_threshold, 'subObjectInstanceId')
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_get_pm_threshold_state_with_empty_metadata(self):
|
||||
pm_threshold = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
metadata={})
|
||||
|
||||
result = pm_threshold_utils.get_pm_threshold_state(
|
||||
pm_threshold, 'subObjectInstanceId')
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_get_pm_threshold_state_with_metadata(self):
|
||||
pm_threshold = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
metadata={
|
||||
'thresholdState': [{
|
||||
'subObjectInstanceId': 'subObjectInstanceId',
|
||||
'performanceValue': 200,
|
||||
'metrics': 'metrics',
|
||||
'crossingDirection': 'crossingDirection'
|
||||
}]
|
||||
}
|
||||
)
|
||||
|
||||
result = pm_threshold_utils.get_pm_threshold_state(
|
||||
pm_threshold, 'subObjectInstanceId')
|
||||
self.assertEqual(200, result['performanceValue'])
|
||||
|
||||
def test_pm_threshold_href(self):
|
||||
result = pm_threshold_utils.pm_threshold_href(
|
||||
'pm_threshold_1', 'endpoint')
|
||||
self.assertEqual('endpoint/vnfpm/v2/thresholds/pm_threshold_1', result)
|
||||
|
||||
def test_pm_threshold_links(self):
|
||||
pm_threshold = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
objectInstanceId="id_1")
|
||||
result = pm_threshold_utils.make_pm_threshold_links(
|
||||
pm_threshold, 'endpoint')
|
||||
href = result.self.href
|
||||
self.assertEqual('endpoint/vnfpm/v2/thresholds/pm_threshold_1', href)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'update')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_update_threshold_state_data(
|
||||
self, mock_pms, mock_create, mock_update):
|
||||
mock_pms.return_value = objects.ThresholdV2(
|
||||
id='pm_threshold_1')
|
||||
mock_create.return_value = None
|
||||
mock_update.return_value = None
|
||||
pm_threshold_state_1 = {
|
||||
'thresholdId': 'pm_threshold_1',
|
||||
'subObjectInstanceId': 'sub_id_1',
|
||||
'performanceValue': '200.5',
|
||||
'metrics': 'VCpuUsageMeanVnf.VNF',
|
||||
'crossingDirection': 'UP'
|
||||
}
|
||||
update_threshold_state_data = {
|
||||
'subObjectInstanceId': pm_threshold_state_1[
|
||||
'subObjectInstanceId'],
|
||||
'performanceValue': pm_threshold_state_1['performanceValue'],
|
||||
'metrics': pm_threshold_state_1['metrics'],
|
||||
'crossingDirection': pm_threshold_state_1['crossingDirection']
|
||||
}
|
||||
pm_threshold_utils.update_threshold_state_data(
|
||||
self.context,
|
||||
pm_threshold_state_1['thresholdId'],
|
||||
update_threshold_state_data)
|
|
@ -26,6 +26,7 @@ from tacker.sol_refactored.common import exceptions as sol_ex
|
|||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import monitoring_plugin_base as mon_base
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.common import prometheus_plugin
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored import objects
|
||||
|
@ -67,8 +68,6 @@ _body_pm_alert1 = {
|
|||
},
|
||||
'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'
|
||||
}
|
||||
|
||||
|
@ -90,6 +89,44 @@ _body_pm_alert5['labels']['metric'] = 'ByteIncomingVnfIntCp'
|
|||
_body_pm_alert6 = copy.deepcopy(_body_pm_alert1)
|
||||
_body_pm_alert6['labels']['metric'] = 'InvalidMetric'
|
||||
|
||||
_body_pm_threshold_alert1 = {
|
||||
'status': 'firing',
|
||||
'labels': {
|
||||
'receiver_type': 'tacker',
|
||||
'function_type': 'vnfpm_threshold',
|
||||
'threshold_id': '64e46b0e-887a-4691-8d2b-aa3d7b157e2c',
|
||||
'metric': 'VCpuUsageMeanVnf.'
|
||||
'25b9b9d0-2461-4109-866e-a7767375415b',
|
||||
'object_instance_id': '25b9b9d0-2461-4109-866e-a7767375415b'
|
||||
},
|
||||
'annotations': {
|
||||
'value': '510',
|
||||
},
|
||||
'startsAt': '2022-06-21T23:47:36.453Z',
|
||||
'endsAt': '0001-01-01T00:00:00Z',
|
||||
'fingerprint': '5ef77f1f8a3ecb8d'
|
||||
}
|
||||
|
||||
# function_type mismatch
|
||||
_body_pm_threshold_alert2 = copy.deepcopy(_body_pm_threshold_alert1)
|
||||
_body_pm_threshold_alert2['labels']['function_type'] = 'vnffm'
|
||||
|
||||
# object_instance_id mismatch
|
||||
_body_pm_threshold_alert3 = copy.deepcopy(_body_pm_threshold_alert1)
|
||||
_body_pm_threshold_alert3['labels']['object_instance_id'] = (
|
||||
'obj_instance_mismatch')
|
||||
|
||||
# sub_object_instance_id mismatch
|
||||
_body_pm_threshold_alert4 = copy.deepcopy(_body_pm_threshold_alert1)
|
||||
_body_pm_threshold_alert4['labels']['sub_object_instance_id'] = (
|
||||
'sub_object_mismatch')
|
||||
|
||||
_body_pm_threshold_alert5 = copy.deepcopy(_body_pm_threshold_alert1)
|
||||
_body_pm_threshold_alert5['labels']['metric'] = 'ByteIncomingVnfIntCp'
|
||||
|
||||
_body_pm_threshold_alert6 = copy.deepcopy(_body_pm_threshold_alert1)
|
||||
_body_pm_threshold_alert6['labels']['metric'] = 'InvalidMetric'
|
||||
|
||||
_body_pm1 = copy.deepcopy(_body_base)
|
||||
_body_pm1.update({
|
||||
'alerts': [
|
||||
|
@ -101,6 +138,20 @@ _body_pm2.update({
|
|||
'alerts': [_body_pm_alert5, _body_pm_alert6]
|
||||
})
|
||||
|
||||
_body_pm_threshold1 = copy.deepcopy(_body_base)
|
||||
_body_pm_threshold1.update({
|
||||
'alerts': [
|
||||
_body_pm_threshold_alert1,
|
||||
_body_pm_threshold_alert2,
|
||||
_body_pm_threshold_alert3,
|
||||
_body_pm_threshold_alert4]
|
||||
})
|
||||
|
||||
_body_pm_threshold2 = copy.deepcopy(_body_base)
|
||||
_body_pm_threshold2.update({
|
||||
'alerts': [_body_pm_threshold_alert5, _body_pm_threshold_alert6]
|
||||
})
|
||||
|
||||
_pm_job = {
|
||||
'id': 'job_id',
|
||||
'objectType': 'Vnf',
|
||||
|
@ -108,7 +159,7 @@ _pm_job = {
|
|||
'subObjectInstanceIds': [],
|
||||
'criteria': {
|
||||
'performanceMetric': [
|
||||
'VcpuUsageMeanVnf.25b9b9d0-2461-4109-866e-a7767375415b'
|
||||
'VCpuUsageMeanVnf.25b9b9d0-2461-4109-866e-a7767375415b'
|
||||
],
|
||||
'performanceMetricGroup': [
|
||||
'VirtualisedComputeResource',
|
||||
|
@ -154,6 +205,52 @@ _pm_job2['criteria']['performanceMetric'] = ['ByteIncomingVnfIntCp']
|
|||
_pm_job2['criteria']['performanceMetricGroup'] = [
|
||||
'VnfInternalCp', 'VnfExternalCp']
|
||||
|
||||
_pm_threshold = {
|
||||
'id': 'threshold_id',
|
||||
'objectType': 'Vnf',
|
||||
'objectInstanceId': '25b9b9d0-2461-4109-866e-a7767375415b',
|
||||
'subObjectInstanceIds': ['vnfc_info1'],
|
||||
'criteria': {
|
||||
'performanceMetric':
|
||||
'VCpuUsageMeanVnf.25b9b9d0-2461-4109-866e-a7767375415b',
|
||||
'thresholdType': 'SIMPLE',
|
||||
'simpleThresholdDetails': {
|
||||
'thresholdValue': 500.5,
|
||||
'hysteresis': 10.5
|
||||
}
|
||||
},
|
||||
'callbackUri': '',
|
||||
'metadata': {
|
||||
'monitoring': {
|
||||
'monitorName': 'prometheus',
|
||||
'driverType': 'external',
|
||||
'targetsInfo': [
|
||||
{
|
||||
'prometheusHost':
|
||||
'prometheusHost',
|
||||
'prometheusHostPort': '22',
|
||||
'authInfo': {
|
||||
'ssh_username': 'ssh_username',
|
||||
'ssh_password': 'ssh_password'
|
||||
},
|
||||
'alertRuleConfigPath':
|
||||
'alertRuleConfigPath',
|
||||
'prometheusReloadApiEndpoint':
|
||||
'prometheusReloadApiEndpoint'
|
||||
},
|
||||
{
|
||||
# invalid access info
|
||||
'prometheusHost':
|
||||
'prometheusHost',
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
_pm_threshold2 = copy.deepcopy(_pm_threshold)
|
||||
_pm_threshold2['objectType'] = 'VnfIntCp'
|
||||
_pm_threshold2['criteria']['performanceMetric'] = 'ByteIncomingVnfIntCp'
|
||||
|
||||
_pm_report = {
|
||||
'id': 'report_id',
|
||||
'jobId': 'pm_job_id',
|
||||
|
@ -221,6 +318,14 @@ _pm_report = {
|
|||
}]
|
||||
}
|
||||
|
||||
_pm_threshold_state = {
|
||||
'thresholdId': 'threshold_id',
|
||||
'subObjectInstanceId': 'sub_id_1',
|
||||
'metrics': 'VCpuUsageMeanVnf.25b9b9d0-2461-4109-866e-a7767375415b',
|
||||
'performanceValue': '200.5',
|
||||
'crossingDirection': 'UP'
|
||||
}
|
||||
|
||||
_pm_report2 = {
|
||||
'id': 'report_id',
|
||||
'jobId': 'pm_job_id',
|
||||
|
@ -382,10 +487,10 @@ class TestPrometheusPluginPm(base.TestCase):
|
|||
prometheus_plugin.PrometheusPluginPm)
|
||||
unload_uuidsentinel()
|
||||
with freezegun.freeze_time(datetime_test):
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp._alert, self.request, body=_body_pm2
|
||||
)
|
||||
result = pp._alert(self.request, body=_body_pm2)
|
||||
self.assertTrue(len(result) == 1)
|
||||
self.assertEqual(result[0]["performanceMetric"],
|
||||
'ByteIncomingVnfIntCp')
|
||||
|
||||
@mock.patch.object(pm_job_utils, 'get_pm_report')
|
||||
@mock.patch.object(pm_job_utils, 'get_pm_job')
|
||||
|
@ -814,6 +919,479 @@ class TestPrometheusPluginPm(base.TestCase):
|
|||
)
|
||||
|
||||
|
||||
class TestPrometheusPluginThreshold(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPrometheusPluginThreshold, self).setUp()
|
||||
objects.register_all()
|
||||
self.context = context.get_admin_context()
|
||||
self.request = mock.Mock()
|
||||
self.request.context = self.context
|
||||
prometheus_plugin.PrometheusPluginThreshold._instance = None
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPrometheusPluginThreshold, self).tearDown()
|
||||
# delete singleton object
|
||||
prometheus_plugin.PrometheusPluginThreshold._instance = None
|
||||
|
||||
def test_constructor_error(self):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', auto_scaling=False)
|
||||
mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
self.assertRaises(
|
||||
SystemError,
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
def test_constructor_stub(self):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', auto_scaling=False)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
self.assertIsInstance(pp._instance, mon_base.MonitoringPluginStub)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
self.assertIsInstance(pp._instance, mon_base.MonitoringPluginStub)
|
||||
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold_state')
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold')
|
||||
def test_pm_threshold(self, mock_pm_threshold, mock_pm_threshold_state):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
mock_pm_threshold.return_value = (
|
||||
objects.ThresholdV2.from_dict(_pm_threshold))
|
||||
mock_pm_threshold_state.return_value = _pm_threshold_state
|
||||
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
unload_uuidsentinel()
|
||||
with freezegun.freeze_time(datetime_test):
|
||||
result = pp._alert(self.request, body=_body_pm_threshold1)
|
||||
self.assertTrue(len(result) > 0)
|
||||
self.assertEqual(result[0]['performanceValue'], 510)
|
||||
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold_state')
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold')
|
||||
def test_pm_threshold_metrics(
|
||||
self, mock_pm_threshold, mock_pm_threshold_state):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
mock_pm_threshold.return_value = (
|
||||
objects.ThresholdV2.from_dict(_pm_threshold))
|
||||
mock_pm_threshold_state.return_value = _pm_threshold_state
|
||||
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
unload_uuidsentinel()
|
||||
with freezegun.freeze_time(datetime_test):
|
||||
result = pp._alert(self.request, body=_body_pm_threshold2)
|
||||
self.assertTrue(len(result) == 0)
|
||||
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold_state')
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold')
|
||||
def test_pm_threshold_with_threshold_state(
|
||||
self, mock_pm_threshold, mock_pm_threshold_state):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
mock_pm_threshold.return_value = (
|
||||
objects.ThresholdV2.from_dict(_pm_threshold))
|
||||
mock_pm_threshold_state.return_value = None
|
||||
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
unload_uuidsentinel()
|
||||
with freezegun.freeze_time(datetime_test):
|
||||
result = pp._alert(self.request, body=_body_pm_threshold1)
|
||||
self.assertTrue(len(result) > 0)
|
||||
self.assertEqual(result[0]['performanceValue'], 510)
|
||||
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold_state')
|
||||
@mock.patch.object(pm_threshold_utils, 'get_pm_threshold')
|
||||
def test_pm_threshold_set_callback(
|
||||
self, mock_pm_threshold, mock_pm_threshold_state):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
mock_pm_threshold.return_value = (
|
||||
objects.ThresholdV2.from_dict(_pm_threshold))
|
||||
mock_pm_threshold_state.return_value = _pm_threshold_state
|
||||
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
pp.set_callback(None)
|
||||
|
||||
unload_uuidsentinel()
|
||||
with freezegun.freeze_time(datetime_test):
|
||||
result = pp._alert(self.request, body=_body_pm_threshold1)
|
||||
self.assertTrue(len(result) > 0)
|
||||
self.assertEqual(result[0]['performanceValue'], 510)
|
||||
|
||||
def test_pm_threshold_error_access_info(self):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
del threshold['metadata']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.delete_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
threshold2 = copy.deepcopy(_pm_threshold)
|
||||
threshold2['metadata'] = dict({'monitoring': {}})
|
||||
threshold2 = objects.ThresholdV2.from_dict(threshold2)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.delete_threshold, context=self.context, pm_threshold=threshold2
|
||||
)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
def test_delete_pm_threshold(
|
||||
self, mock_paramiko, mock_sftp, mock_do_request):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
# normal
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
pp.delete_threshold(context=self.context, pm_threshold=threshold)
|
||||
# error
|
||||
resp.status_code = 503
|
||||
pp.delete_threshold(context=self.context, pm_threshold=threshold)
|
||||
# paramiko error
|
||||
resp.status_code = 202
|
||||
mock_paramiko.return_value = _ParamikoTest(
|
||||
exp=sol_ex.PrometheusPluginError())
|
||||
pp.delete_threshold(context=self.context, pm_threshold=threshold)
|
||||
|
||||
@mock.patch.object(paramiko, 'SSHClient')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_create_pm_threshold(
|
||||
self, mock_inst, mock_paramiko, mock_sftp,
|
||||
mock_do_request, mock_sshclient):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
mock_sshclient.return_value = _ParamikoTest()
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', test_rule_with_promtool=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
# VirtualisedComputeResource
|
||||
pm_threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=pm_threshold)
|
||||
self.assertTrue(len(rule['groups'][0]['rules']) > 0)
|
||||
# VnfInternalCp
|
||||
pm_threshold2 = objects.ThresholdV2.from_dict(_pm_threshold2)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=pm_threshold2)
|
||||
self.assertTrue(len(rule['groups'][0]['rules']) > 0)
|
||||
self.assertTrue('interface=' in str(rule))
|
||||
# namespace
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=threshold)
|
||||
self.assertTrue('namespace="default"' in str(rule))
|
||||
self.assertFalse('namespace="test"' in str(rule))
|
||||
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst2)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=threshold)
|
||||
self.assertFalse('namespace="default"' in str(rule))
|
||||
self.assertTrue('namespace="test"' in str(rule))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_create_pm_threshold_subobj(
|
||||
self, mock_inst, mock_paramiko, mock_sftp, mock_do_request):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
inst = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
mock_inst.return_value = inst
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
# VirtualisedComputeResource
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['subObjectInstanceIds'] = ['vnfc_info1']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=threshold)
|
||||
self.assertTrue(len(rule['groups'][0]['rules']) > 0)
|
||||
self.assertEqual(
|
||||
rule['groups'][0]['rules'][0]['labels']['sub_object_instance_id'],
|
||||
threshold['subObjectInstanceIds'][0])
|
||||
|
||||
# VnfInternalCp
|
||||
threshold = copy.deepcopy(_pm_threshold2)
|
||||
threshold['subObjectInstanceIds'] = ['test_if0']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
rule = pp.create_threshold(
|
||||
context=self.context, pm_threshold=threshold)
|
||||
self.assertTrue(len(rule['groups'][0]['rules']) > 0)
|
||||
self.assertTrue('interface="test_if0"' in str(rule))
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_create_pm_threshold_error(
|
||||
self, mock_inst, mock_paramiko, mock_sftp, mock_do_request):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
# invalid object type
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['objectType'] = 'invalid_type'
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
# invalid performanceMetric.
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['criteria']['performanceMetric'] = (
|
||||
"invalid performanceMetric")
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
# no instantiatedVnfInfo
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst_base)
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
# no instantiatedVnfInfo with subObjectInstanceIds
|
||||
threshold = copy.deepcopy(_pm_threshold2)
|
||||
threshold['subObjectInstanceIds'] = ['test_if0']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
# no valid computeResource
|
||||
ins = copy.deepcopy(_inst1)
|
||||
_ = ins['instantiatedVnfInfo']['vnfcResourceInfo'][0]
|
||||
_['computeResource'] = {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
# no vnfcInfo
|
||||
ins = copy.deepcopy(_inst1)
|
||||
del ins['instantiatedVnfInfo']['vnfcInfo']
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['subObjectInstanceIds'] = ['vnfc_info1']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
# vnfcInfo mismatch
|
||||
ins = copy.deepcopy(_inst1)
|
||||
ins['instantiatedVnfInfo']['vnfcInfo'][0]['vnfcResourceInfoId'] = 'ng'
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['subObjectInstanceIds'] = ['vnfc_info1']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
# vnfcInfo mismatch
|
||||
ins = copy.deepcopy(_inst1)
|
||||
del ins['instantiatedVnfInfo']['vnfcInfo'][0]['vnfcResourceInfoId']
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['subObjectInstanceIds'] = ['vnfc_info1']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
# resourcename mismatch: VirtualisedComputeResource
|
||||
ins = copy.deepcopy(_inst1)
|
||||
_ = ins['instantiatedVnfInfo']['vnfcResourceInfo']
|
||||
del _[0]['computeResource']['resourceId']
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = copy.deepcopy(_pm_threshold)
|
||||
threshold['subObjectInstanceIds'] = ['vnfc_info1']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
ins = copy.deepcopy(_inst1)
|
||||
_ = ins['instantiatedVnfInfo']['vnfcResourceInfo'][0]
|
||||
_['computeResource']['vimLevelResourceType'] = 'ng'
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(ins)
|
||||
threshold = copy.deepcopy(_pm_threshold2)
|
||||
threshold['subObjectInstanceIds'] = ['test_if0']
|
||||
threshold = objects.ThresholdV2.from_dict(threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
@mock.patch.object(paramiko, 'SSHClient')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_create_pm_threshold_error2(
|
||||
self, mock_inst, mock_paramiko,
|
||||
mock_sftp, mock_do_request, mock_sshclient):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
exp = ValueError("test_create_pm_threshold_error2")
|
||||
mock_sshclient.return_value = _ParamikoTest(
|
||||
exp=exp, recv_exit_status_value=1)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', test_rule_with_promtool=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
# upload error
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold)
|
||||
exp = sol_ex.PrometheusPluginError("test_create_pm_threshold_error2")
|
||||
mock_paramiko.return_value = _ParamikoTest(exp=exp)
|
||||
mock_sshclient.return_value = _ParamikoTest(
|
||||
exp=exp, recv_exit_status_value=1)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold)
|
||||
|
||||
@mock.patch.object(utils, 'find_config_file')
|
||||
@mock.patch.object(paramiko, 'SSHClient')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_promql(
|
||||
self, mock_inst, mock_paramiko, mock_sftp, mock_do_request,
|
||||
mock_sshclient, mock_utils):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
mock_sshclient.return_value = _ParamikoTest(recv_exit_status_value=1)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', test_rule_with_promtool=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
# no config file
|
||||
mock_utils.return_value = None
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
# Type check
|
||||
mock_utils.return_value = None
|
||||
pp.make_rule("Threshold", "id", "id", "id", "metric", "exp")
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.make_rule, "TypeError", "id", "id", "id", "metric", "exp"
|
||||
)
|
||||
|
||||
@mock.patch.object(paramiko, 'SSHClient')
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
@mock.patch.object(paramiko.SFTPClient, 'from_transport')
|
||||
@mock.patch.object(paramiko, 'Transport')
|
||||
@mock.patch.object(inst_utils, 'get_inst')
|
||||
def test_promql2(
|
||||
self, mock_inst, mock_paramiko, mock_sftp, mock_do_request,
|
||||
mock_sshclient):
|
||||
mock_paramiko.return_value = _ParamikoTest()
|
||||
mock_sftp.return_value = _ParamikoTest()
|
||||
mock_sshclient.return_value = _ParamikoTest(recv_exit_status_value=1)
|
||||
resp = webob.Response()
|
||||
resp.status_code = 202
|
||||
mock_do_request.return_value = resp, {}
|
||||
mock_inst.return_value = objects.VnfInstanceV2.from_dict(_inst1)
|
||||
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', test_rule_with_promtool=True)
|
||||
pp = mon_base.MonitoringPlugin.get_instance(
|
||||
prometheus_plugin.PrometheusPluginThreshold)
|
||||
|
||||
# no config file
|
||||
threshold = objects.ThresholdV2.from_dict(_pm_threshold)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginError,
|
||||
pp.create_threshold, context=self.context, pm_threshold=threshold
|
||||
)
|
||||
|
||||
|
||||
class TestPrometheusPluginFm(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestPrometheusPluginFm, self).setUp()
|
||||
|
|
|
@ -12,16 +12,13 @@
|
|||
# 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 requests
|
||||
from unittest import mock
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
||||
from tacker.sol_refactored import objects
|
||||
from tacker.tests import base
|
||||
|
@ -56,179 +53,6 @@ class TestSubscriptionUtils(base.BaseTestCase):
|
|||
result = subsc_utils.get_subsc_all(context)
|
||||
self.assertEqual('subsc-1', result[0].id)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-2', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['BASIC'],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test')))
|
||||
|
||||
# execute basic_auth
|
||||
subsc_utils.send_notification(subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = objects.LccnSubscriptionV2(
|
||||
id='sub-3', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['OAUTH2_CLIENT_CREDENTIALS'],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'))))
|
||||
|
||||
# execute oauth2
|
||||
subsc_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2_mtls = objects.LccnSubscriptionV2(
|
||||
id='sub-4', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CERT"],
|
||||
paramsOauth2ClientCert=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2ClientCert(
|
||||
clientId='test',
|
||||
certificateRef=objects.
|
||||
ParamsOauth2ClientCert_CertificateRef(
|
||||
type='x5t#256',
|
||||
value='03c6e188d1fe5d3da8c9bc9a8dc531a2'
|
||||
'b3ecf812b03aede9bec7ba1b410b6b64'
|
||||
),
|
||||
tokenEndpoint='http://127.0.0.1/token'))))
|
||||
|
||||
# execute oauth2 mtls
|
||||
subsc_utils.send_notification(subsc_oauth2_mtls, notif_data_no_auth)
|
||||
|
||||
cfg.CONF.set_override("notification_verify_cert", "True",
|
||||
group="v2_vnfm")
|
||||
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-5', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
subsc_basic_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-6', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['BASIC'],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test', password='test')))
|
||||
|
||||
# execute basic_auth
|
||||
subsc_utils.send_notification(subsc_basic_auth, notif_data_no_auth)
|
||||
|
||||
subsc_oauth2 = objects.LccnSubscriptionV2(
|
||||
id='sub-7', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=['OAUTH2_CLIENT_CREDENTIALS'],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test', clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'))))
|
||||
|
||||
# execute oauth2
|
||||
subsc_utils.send_notification(subsc_oauth2, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_send_notification_error(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
notif_data_no_auth = objects.VnfLcmOperationOccurrenceNotificationV2(
|
||||
id=uuidutils.generate_uuid()
|
||||
)
|
||||
resp_no_auth = Exception()
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.send_notification(subsc_no_auth, notif_data_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 204
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
subsc_utils.test_notification(subsc_no_auth)
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, 'do_request')
|
||||
def test_test_notification_error_code(self, mock_resp):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
resp_no_auth = requests.Response()
|
||||
resp_no_auth.status_code = 200
|
||||
mock_resp.return_value = (resp_no_auth, None)
|
||||
|
||||
# execute no_auth
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
subsc_utils.test_notification, subsc_no_auth)
|
||||
|
||||
class mock_session():
|
||||
|
||||
def request(url, method, raise_exc=False, **kwargs):
|
||||
resp = requests.Response()
|
||||
resp.status_code = 400
|
||||
resp.headers['Content-Type'] = 'application/zip'
|
||||
return resp
|
||||
|
||||
@mock.patch.object(http_client.HttpClient, '_decode_body')
|
||||
@mock.patch.object(http_client.NoAuthHandle, 'get_session')
|
||||
def test_test_notification_error(self, mock_session, mock_decode_body):
|
||||
subsc_no_auth = objects.LccnSubscriptionV2(
|
||||
id='sub-1', verbosity='SHORT',
|
||||
callbackUri='http://127.0.0.1/callback')
|
||||
mock_session.return_value = self.mock_session
|
||||
mock_decode_body.return_value = None
|
||||
|
||||
self.assertRaises(sol_ex.TestNotificationFailed,
|
||||
subsc_utils.test_notification, subsc_no_auth)
|
||||
|
||||
def test_match_version(self):
|
||||
inst = objects.VnfInstanceV2(
|
||||
id='test-instance', vnfSoftwareVersion='1.1.1', vnfdVersion='1.2')
|
||||
|
|
|
@ -18,6 +18,7 @@ from unittest import mock
|
|||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import fm_subscription_utils
|
||||
from tacker.sol_refactored.common import vnf_instance_utils as inst_utils
|
||||
from tacker.sol_refactored.conductor import vnffm_driver_v1
|
||||
|
@ -34,7 +35,7 @@ class TestVnffmDriverV1(base.BaseTestCase):
|
|||
self.driver = vnffm_driver_v1.VnfFmDriverV1()
|
||||
self.context = context.get_admin_context()
|
||||
|
||||
@mock.patch.object(fm_subscription_utils, 'send_notification')
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(fm_subscription_utils, 'get_alarm_subscs')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'update')
|
||||
|
|
|
@ -18,7 +18,9 @@ from unittest import mock
|
|||
from tacker.tests import base
|
||||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import pm_threshold_utils
|
||||
from tacker.sol_refactored.conductor.vnfpm_driver_v2 import VnfPmDriverV2
|
||||
from tacker.sol_refactored.nfvo.nfvo_client import NfvoClient
|
||||
from tacker.sol_refactored import objects
|
||||
|
@ -70,6 +72,40 @@ class TestVnfPmDriverV2(base.BaseTestCase):
|
|||
VnfPmDriverV2().store_job_info(context=self.context,
|
||||
report=report)
|
||||
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
@mock.patch.object(pm_threshold_utils,
|
||||
'update_threshold_state_data')
|
||||
def test_store_threshold_info(self,
|
||||
mock_update_threshold_state_data,
|
||||
mock_pm, mock_send):
|
||||
threshold_states = [{
|
||||
'thresholdId': 'pm_threshold_1',
|
||||
'subObjectInstanceId': "sub_id_1",
|
||||
'performanceValue': '200.5',
|
||||
'metrics': 'VCpuUsageMeanVnf.VNF',
|
||||
'crossingDirection': 'UP'
|
||||
}]
|
||||
mock_pm.return_value = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
objectType='Vnf',
|
||||
objectInstanceId='id_1',
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["OAUTH2_CLIENT_CREDENTIALS"],
|
||||
paramsOauth2ClientCredentials=(
|
||||
objects.SubscriptionAuthentication_ParamsOauth2(
|
||||
clientId='test',
|
||||
clientPassword='test',
|
||||
tokenEndpoint='http://127.0.0.1/token'
|
||||
))
|
||||
)
|
||||
)
|
||||
mock_send.return_value = None
|
||||
mock_update_threshold_state_data.return_value = threshold_states[0]
|
||||
VnfPmDriverV2().store_threshold_info(context=self.context,
|
||||
threshold_states=threshold_states)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
def test_store_report(self, mock_create):
|
||||
mock_create.return_value = None
|
||||
|
|
|
@ -287,9 +287,9 @@ datetime_test = datetime.datetime.fromisoformat(
|
|||
'2022-06-22T01:23:45.678Z'.replace('Z', '+00:00'))
|
||||
|
||||
|
||||
class TestPrometheusPluginPm(base.TestCase):
|
||||
class TestPrometheusPluginPmEvent(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestPrometheusPluginPm, self).setUp()
|
||||
super(TestPrometheusPluginPmEvent, self).setUp()
|
||||
objects.register_all()
|
||||
self.context = context.get_admin_context()
|
||||
self.request = mock.Mock()
|
||||
|
@ -298,7 +298,7 @@ class TestPrometheusPluginPm(base.TestCase):
|
|||
plugin.PrometheusPluginPm._instance = None
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPrometheusPluginPm, self).tearDown()
|
||||
super(TestPrometheusPluginPmEvent, self).tearDown()
|
||||
# delete singleton object
|
||||
plugin.PrometheusPluginPm._instance = None
|
||||
|
||||
|
@ -316,6 +316,35 @@ class TestPrometheusPluginPm(base.TestCase):
|
|||
self.assertEqual(204, result.status)
|
||||
|
||||
|
||||
class TestPrometheusPluginPmThreshold(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestPrometheusPluginPmThreshold, 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.PmThresholdController()
|
||||
plugin.PrometheusPluginThreshold._instance = None
|
||||
|
||||
def tearDown(self):
|
||||
super(TestPrometheusPluginPmThreshold, self).tearDown()
|
||||
# delete singleton object
|
||||
plugin.PrometheusPluginThreshold._instance = None
|
||||
|
||||
def test_pm_threshold_config_false(self):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=False)
|
||||
self.assertRaises(
|
||||
sol_ex.PrometheusPluginNotEnabled,
|
||||
self.controller.pm_threshold, self.request, {})
|
||||
|
||||
def test_pm_exception(self):
|
||||
self.config_fixture.config(
|
||||
group='prometheus_plugin', performance_management=True)
|
||||
result = self.controller.pm_threshold(self.request, {})
|
||||
self.assertEqual(204, result.status)
|
||||
|
||||
|
||||
class TestPrometheusPluginFm(base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestPrometheusPluginFm, self).setUp()
|
||||
|
|
|
@ -19,6 +19,7 @@ from unittest import mock
|
|||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import fm_alarm_utils as alarm_utils
|
||||
from tacker.sol_refactored.common import fm_subscription_utils as subsc_utils
|
||||
|
@ -109,7 +110,7 @@ class TestVnffmV1(base.BaseTestCase):
|
|||
request=self.request, id=SAMPLE_ALARM_ID, body=body)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
@mock.patch.object(subsc_utils, 'test_notification')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
def test_subscription_create(self, mock_test, mock_create):
|
||||
body_1 = {
|
||||
"callbackUri": "http://127.0.0.1:6789/notification",
|
||||
|
|
|
@ -20,6 +20,7 @@ from oslo_utils import uuidutils
|
|||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
|
||||
|
@ -575,7 +576,7 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||
self.assertEqual("paramsOauth2ClientCert must be specified.",
|
||||
ex.detail)
|
||||
|
||||
@mock.patch.object(subsc_utils, 'test_notification')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
def test_subscription_create_201(self, mock_test):
|
||||
body_1 = {
|
||||
"callbackUri": "http://127.0.0.1:6789/notification",
|
||||
|
|
|
@ -18,9 +18,12 @@ from unittest import mock
|
|||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common.prometheus_plugin import (
|
||||
PrometheusPluginThreshold)
|
||||
from tacker.sol_refactored.controller.vnflcm_view import BaseViewBuilder
|
||||
from tacker.sol_refactored.controller.vnflcm_view import Pager
|
||||
from tacker.sol_refactored.controller import vnfpm_v2
|
||||
|
@ -244,7 +247,7 @@ class TestVnfpmV2(base.BaseTestCase):
|
|||
self.controller.create, request=self.request, body=body)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
@mock.patch.object(pm_job_utils, 'test_notification')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_create_201(self, mock_inst, mock_notifi, mock_create):
|
||||
mock_inst.return_value = objects.VnfInstanceV2(
|
||||
|
@ -332,7 +335,7 @@ class TestVnfpmV2(base.BaseTestCase):
|
|||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'update')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
@mock.patch.object(pm_job_utils, 'test_notification')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
def test_update(self, mock_notifi, mock_pm, mock_update):
|
||||
mock_notifi.return_value = None
|
||||
mock_pm.return_value = objects.PmJobV2(id='pm_job_1')
|
||||
|
@ -386,3 +389,235 @@ class TestVnfpmV2(base.BaseTestCase):
|
|||
def test_supported_api_version(self):
|
||||
result = self.controller.supported_api_versions('create')
|
||||
self.assertEqual(['2.1.0'], result)
|
||||
|
||||
# 'performanceMetric' doesn't match objectType
|
||||
def test_create_pm_threshold_error_1(self):
|
||||
_ThresholdCriteria_V2 = {
|
||||
'performanceMetric': 'ByteIncomingVnfIntCp.VNF',
|
||||
'thresholdType': 'SIMPLE',
|
||||
'simpleThresholdDetails': {
|
||||
'thresholdValue': 500.5,
|
||||
'hysteresis': 10.5
|
||||
}
|
||||
}
|
||||
_SubscriptionAuthentication = {
|
||||
'authType': ['BASIC'],
|
||||
'paramsBasic': {
|
||||
'userName': 'test_name',
|
||||
'password': 'test_pwd'
|
||||
}
|
||||
}
|
||||
body = {
|
||||
"objectType": "Vnf",
|
||||
"objectInstanceId": "id_1",
|
||||
"subObjectInstanceIds": ["sub_id_1", "sub_id_2"],
|
||||
"criteria": _ThresholdCriteria_V2,
|
||||
"callbackUri": 'callbackuri',
|
||||
"authentication": _SubscriptionAuthentication,
|
||||
'metadata': {"metadata": "example"}
|
||||
}
|
||||
self.assertRaises(sol_ex.PMThresholdInvalidRequest,
|
||||
self.controller.create_threshold,
|
||||
request=self.request, body=body)
|
||||
|
||||
# 'simpleThresholdDetails' is not assigned
|
||||
def test_create_pm_threshold_error_2(self):
|
||||
_ThresholdCriteria_V2 = {
|
||||
'performanceMetric': 'VCpuUsageMeanVnf.VNF',
|
||||
'thresholdType': 'SIMPLE',
|
||||
}
|
||||
_SubscriptionAuthentication = {
|
||||
'authType': ['BASIC'],
|
||||
'paramsBasic': {
|
||||
'userName': 'test_name',
|
||||
'password': 'test_pwd'
|
||||
}
|
||||
}
|
||||
body = {
|
||||
"objectType": "Vnf",
|
||||
"objectInstanceId": "id_1",
|
||||
"subObjectInstanceIds": ["sub_id_1", "sub_id_2"],
|
||||
"criteria": _ThresholdCriteria_V2,
|
||||
"callbackUri": 'callbackuri',
|
||||
"authentication": _SubscriptionAuthentication,
|
||||
'metadata': {"metadata": "example"}
|
||||
}
|
||||
self.assertRaises(sol_ex.PMThresholdInvalidRequest,
|
||||
self.controller.create_threshold,
|
||||
request=self.request, body=body)
|
||||
|
||||
# vnf instance status: "NOT_INSTANTIATED"
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_create_pm_threshold_error_5(self, mock_inst):
|
||||
mock_inst.return_value = objects.VnfInstanceV2(
|
||||
id='dummy-vnfInstanceId-1', vnfdId='dummy-vnfdId-1',
|
||||
vnfProvider='dummy-vnfProvider-1',
|
||||
instantiationState='NOT_INSTANTIATED',
|
||||
vnfProductName='dummy-vnfProductName-1-1',
|
||||
vnfSoftwareVersion='1.0', vnfdVersion='1.0',
|
||||
vnfInstanceName='dummy-vnfInstanceName-1')
|
||||
_ThresholdCriteria_V2 = {
|
||||
'performanceMetric': 'VCpuUsageMeanVnf.VNF',
|
||||
'thresholdType': 'SIMPLE',
|
||||
'simpleThresholdDetails': {
|
||||
'thresholdValue': 100.5,
|
||||
'hysteresis': 10.5
|
||||
}
|
||||
}
|
||||
_SubscriptionAuthentication = {
|
||||
'authType': ['BASIC'],
|
||||
'paramsBasic': {
|
||||
'userName': 'test_name',
|
||||
'password': 'test_pwd'
|
||||
}
|
||||
}
|
||||
body = {
|
||||
"objectType": "Vnf",
|
||||
"objectInstanceId": "id_1",
|
||||
"subObjectInstanceIds": ["sub_id_1", "sub_id_2"],
|
||||
"criteria": _ThresholdCriteria_V2,
|
||||
"callbackUri": 'callbackuri',
|
||||
"authentication": _SubscriptionAuthentication,
|
||||
'metadata': {"metadata": "example"}
|
||||
}
|
||||
self.assertRaises(sol_ex.VnfInstanceIsNotInstantiated,
|
||||
self.controller.create_threshold,
|
||||
request=self.request, body=body)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'create')
|
||||
@mock.patch.object(PrometheusPluginThreshold, 'create_threshold')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_create_pm_threshold(
|
||||
self, mock_inst, mock_notifi, mock_create_threshold, mock_create):
|
||||
mock_inst.return_value = objects.VnfInstanceV2(
|
||||
id='dummy-vnfInstanceId-1', vnfdId='dummy-vnfdId-1',
|
||||
vnfProvider='dummy-vnfProvider-1',
|
||||
instantiationState='INSTANTIATED',
|
||||
vnfProductName='dummy-vnfProductName-1-1',
|
||||
vnfSoftwareVersion='1.0', vnfdVersion='1.0',
|
||||
vnfInstanceName='dummy-vnfInstanceName-1')
|
||||
mock_notifi.return_value = None
|
||||
mock_create_threshold.return_value = None
|
||||
mock_create.return_value = None
|
||||
|
||||
_ThresholdCriteria_V2 = {
|
||||
'performanceMetric': 'VCpuUsageMeanVnf.VNF',
|
||||
'thresholdType': 'SIMPLE',
|
||||
'simpleThresholdDetails': {
|
||||
'thresholdValue': 100.5,
|
||||
'hysteresis': 10.5
|
||||
}
|
||||
}
|
||||
_SubscriptionAuthentication = {
|
||||
'authType': ['BASIC'],
|
||||
'paramsBasic': {
|
||||
'userName': 'test_name',
|
||||
'password': 'test_pwd'
|
||||
}
|
||||
}
|
||||
body = {
|
||||
"objectType": "Vnf",
|
||||
"objectInstanceId": "id_1",
|
||||
"subObjectInstanceIds": ["sub_id_1", "sub_id_2"],
|
||||
"criteria": _ThresholdCriteria_V2,
|
||||
"callbackUri": 'callbackuri',
|
||||
"authentication": _SubscriptionAuthentication,
|
||||
'metadata': {"metadata": "example"}
|
||||
}
|
||||
|
||||
result = self.controller.create_threshold(
|
||||
request=self.request, body=body)
|
||||
self.assertEqual(201, result.status)
|
||||
|
||||
@mock.patch.object(Pager, 'get_link')
|
||||
@mock.patch.object(BaseViewBuilder, 'detail_list')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
@mock.patch.object(vnfpm_view.PmThresholdViewBuilder, 'parse_pager')
|
||||
@mock.patch.object(vnfpm_view.PmThresholdViewBuilder, 'parse_filter')
|
||||
@mock.patch.object(vnfpm_view.PmThresholdViewBuilder, 'parse_selector')
|
||||
def test_pm_threshold_index(self, mock_parse_selector, mock_parse_filter,
|
||||
mock_parse_pager, mock_pm, mock_detail_list,
|
||||
mock_get_link):
|
||||
mock_parse_selector.return_value = 'selector'
|
||||
mock_parse_filter.return_value = 'filter'
|
||||
|
||||
request = requests.Request()
|
||||
request.GET = {
|
||||
'filter': 'threshold', 'nextpage_opaque_marker': 'marker'}
|
||||
request.url = 'url'
|
||||
page_size = CONF.v2_vnfm.vnfpm_pmthreshold_page_size
|
||||
pager = Pager(request.GET.get('nextpage_opaque_marker'),
|
||||
request.url,
|
||||
page_size)
|
||||
mock_parse_pager.return_value = pager
|
||||
|
||||
mock_pm.return_value = [objects.ThresholdV2(id='pm_threshold_1')]
|
||||
mock_detail_list.return_value = 1
|
||||
mock_get_link.return_value = 'url'
|
||||
|
||||
result = self.controller.index_threshold(self.request)
|
||||
self.assertEqual(200, result.status)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_pm_threshold_show(self, mock_pm):
|
||||
mock_pm.return_value = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
objectInstanceId="id_1",
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
)
|
||||
)
|
||||
result = self.controller.show_threshold(self.request, 'pm_threshold_1')
|
||||
self.assertEqual(200, result.status)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'update')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
@mock.patch.object(common_script_utils, 'test_notification')
|
||||
def test_pm_threshold_update(self, mock_notifi, mock_pm, mock_update):
|
||||
mock_notifi.return_value = None
|
||||
mock_pm.return_value = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
objectInstanceId="id_1",
|
||||
callbackUri='http://127.0.0.1/callback',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
)
|
||||
)
|
||||
mock_update.return_value = None
|
||||
|
||||
_SubscriptionAuthentication = {
|
||||
'authType': ['BASIC'],
|
||||
'paramsBasic': {
|
||||
'userName': 'test_name',
|
||||
'password': 'test_pwd'
|
||||
}
|
||||
}
|
||||
body = {
|
||||
'callbackUri': 'callbackuri_update',
|
||||
'authentication': _SubscriptionAuthentication
|
||||
}
|
||||
|
||||
result = self.controller.update_threshold(
|
||||
request=self.request, thresholdId='id',
|
||||
body=body)
|
||||
self.assertEqual(200, result.status)
|
||||
|
||||
@mock.patch.object(PrometheusPluginThreshold, 'create_threshold')
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_by_id')
|
||||
def test_pm_threshold_delete(
|
||||
self, mock_pm, mock_create_threshold):
|
||||
mock_pm.return_value = objects.ThresholdV2(id='pm_threshold_1')
|
||||
mock_create_threshold.return_value = None
|
||||
|
||||
result = self.controller.delete_threshold(
|
||||
self.request, 'pm_threshold_1')
|
||||
self.assertEqual(204, result.status)
|
||||
|
|
|
@ -20,6 +20,7 @@ from unittest import mock
|
|||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.controller.vnflcm_view import BaseViewBuilder
|
||||
from tacker.sol_refactored.controller.vnfpm_view import PmJobViewBuilder
|
||||
from tacker.sol_refactored.controller.vnfpm_view import PmThresholdViewBuilder
|
||||
from tacker.sol_refactored import objects
|
||||
|
||||
|
||||
|
@ -71,3 +72,44 @@ class TestPmJobViewBuilder(base.BaseTestCase):
|
|||
result = PmJobViewBuilder(self.endpoint).detail_list(
|
||||
'pm_jobs', 'filters', 'selector', 'pager')
|
||||
self.assertEqual(1, result)
|
||||
|
||||
|
||||
class TestPmThresholdViewBuilder(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPmThresholdViewBuilder, self).setUp()
|
||||
objects.register_all()
|
||||
self.context = context.get_admin_context()
|
||||
self.request = mock.Mock()
|
||||
self.request.context = self.context
|
||||
self.endpoint = CONF.v2_vnfm.endpoint
|
||||
|
||||
@mock.patch.object(BaseViewBuilder, 'parse_filter')
|
||||
def test_parse_filter(self, mock_parse_filter):
|
||||
mock_parse_filter.return_value = 1
|
||||
result = PmThresholdViewBuilder(
|
||||
self.endpoint).parse_filter('filter_param')
|
||||
self.assertEqual(1, result)
|
||||
|
||||
@mock.patch.object(BaseViewBuilder, 'parse_pager')
|
||||
def test_parse_pager(self, mock_parse_pager):
|
||||
mock_parse_pager.return_value = 1
|
||||
page_size = CONF.v2_vnfm.vnfpm_pmthreshold_page_size
|
||||
result = PmThresholdViewBuilder(self.endpoint).parse_pager(
|
||||
self.request, page_size)
|
||||
self.assertEqual(1, result)
|
||||
|
||||
def test_detail(self):
|
||||
pm_threshold = objects.ThresholdV2(
|
||||
id='pm_threshold_1',
|
||||
objectInstanceId='id_1',
|
||||
authentication=objects.SubscriptionAuthentication(
|
||||
authType=["BASIC"],
|
||||
paramsBasic=objects.SubscriptionAuthentication_ParamsBasic(
|
||||
userName='test',
|
||||
password='test'
|
||||
),
|
||||
)
|
||||
)
|
||||
result = PmThresholdViewBuilder(self.endpoint).detail(pm_threshold)
|
||||
self.assertEqual('pm_threshold_1', result.get('id'))
|
||||
|
|
|
@ -23,10 +23,10 @@ from oslo_utils import uuidutils
|
|||
|
||||
from tacker import context
|
||||
from tacker.sol_refactored.api import api_version
|
||||
from tacker.sol_refactored.common import common_script_utils
|
||||
from tacker.sol_refactored.common import config
|
||||
from tacker.sol_refactored.common import http_client
|
||||
from tacker.sol_refactored.common import pm_job_utils
|
||||
from tacker.sol_refactored.common import subscription_utils as subsc_utils
|
||||
from tacker.sol_refactored.common import vnfd_utils
|
||||
from tacker.sol_refactored.nfvo import local_nfvo
|
||||
from tacker.sol_refactored.nfvo import nfvo_client
|
||||
|
@ -465,7 +465,7 @@ class TestNfvoClient(base.BaseTestCase):
|
|||
'vimAssets']['softwareImages'][0]['vimSoftwareImageId'])
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
@mock.patch.object(subsc_utils, 'send_notification')
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_inst_create_notification')
|
||||
def test_send_inst_create_notification(
|
||||
self, mock_recv, mock_send, mock_subscs):
|
||||
|
@ -479,7 +479,7 @@ class TestNfvoClient(base.BaseTestCase):
|
|||
self.assertEqual(1, mock_send.call_count)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
@mock.patch.object(subsc_utils, 'send_notification')
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_inst_delete_notification')
|
||||
def test_send_inst_delete_notification(
|
||||
self, mock_recv, mock_send, mock_subscs):
|
||||
|
@ -493,7 +493,7 @@ class TestNfvoClient(base.BaseTestCase):
|
|||
self.assertEqual(1, mock_send.call_count)
|
||||
|
||||
@mock.patch.object(objects.base.TackerPersistentObject, 'get_all')
|
||||
@mock.patch.object(subsc_utils, 'send_notification')
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(local_nfvo.LocalNfvo, 'recv_lcmocc_notification')
|
||||
def test_send_lcmocc_notification(self, mock_recv, mock_send, mock_subscs):
|
||||
inst = objects.VnfInstanceV2(id='test-instance')
|
||||
|
@ -505,7 +505,7 @@ class TestNfvoClient(base.BaseTestCase):
|
|||
self.assertEqual(1, mock_recv.call_count)
|
||||
self.assertEqual(1, mock_send.call_count)
|
||||
|
||||
@mock.patch.object(pm_job_utils, 'send_notification')
|
||||
@mock.patch.object(common_script_utils, 'send_notification')
|
||||
@mock.patch.object(pm_job_utils, 'make_pm_notif_data')
|
||||
def test_send_pm_job_notification(self, mock_notif, mock_send):
|
||||
mock_notif.return_value = 'mock_notif'
|
||||
|
|
Loading…
Reference in New Issue