diff --git a/doc/source/devref/policy_actions_framework.rst b/doc/source/devref/policy_actions_framework.rst new file mode 100644 index 000000000..55db5d04c --- /dev/null +++ b/doc/source/devref/policy_actions_framework.rst @@ -0,0 +1,91 @@ +Tacker Policy Framework +======================= + +This section will introduce framework for tacker policy actions. + +* Introduction +* How to write a new policy action +* Event and Auditing support +* How to combine policy actions with existing monitoring frameworks in Tacker + +Introduction +------------ + +Tacker policy actions framework provides the NFV operators and VNF vendors to +write a pluggable action that manages their own VNFs. Currently Tacker +already provided some common actions like autoscaling, respawning, and +logging. With this framework the custom actions can be easily +applied for the management purpose. + +How to write a new policy action +-------------------------------- + +A policy action for tacker is a python module which contains a class that +inherits from +"tacker.vnfm.policy_actions.abstract_action.AbstractPolicyAction". If the +driver depends/imports more than one module, then create a new python package +under tacker/vnfm/policy_actions folder. After this we have to mention our +driver path in setup.cfg file in root directory. + +For example: +:: + + tacker.tacker.policy.actions = + respawn = tacker.vnfm.policy_actions.respawn.respawn:VNFActionRespawn + +Following methods need to be overridden in the new action: + +``def get_type(self)`` + This method must return the type of action. ex: respawn + +``def get_name(self)`` + This method must return the symbolic name of the vnf policy action. + +``def get_description(self)`` + This method must return the description for the policy action. + +``def execute_action(self, plugin, context, vnf, arguments)`` + This method must expose what will be executed with the policy action. + 'arguments' is used to add more options for policy actions. For example, + if action is scaling, 'arguments' should let you know + 'scaling-out' or 'scaling-in' will be applied. + +Event and Auditing support +-------------------------- + +This function can be used to describe the execution process of policy. +For example: +:: + + _log_monitor_events(context, vnf_dict, "ActionRespawnHeat invoked") + + +How to combine policy with existing monitoring framework in Tacker +------------------------------------------------------------------ + +In the monitoring policy section, you can specify the monitors details with +corresponding action. + +The below example shows how policy is used for alarm monitor. +Example Template +---------------- + +:: + + policies: + - vdu1_cpu_usage_monitoring_policy: + type: tosca.policies.tacker.Alarming + triggers: + resize_compute: + event_type: + type: tosca.events.resource.utilization + implementation: ceilometer + metrics: cpu_util + condition: + threshold: 50 + constraint: utilization greater_than 50% + period: 65 + evaluations: 1 + method: avg + comparison_operator: gt + actions: [respawn] diff --git a/etc/config-generator.conf b/etc/config-generator.conf index 047009862..aa65fee14 100644 --- a/etc/config-generator.conf +++ b/etc/config-generator.conf @@ -13,6 +13,9 @@ namespace = tacker.vnfm.mgmt_drivers.openwrt.openwrt namespace = tacker.vnfm.monitor_drivers.http_ping.http_ping namespace = tacker.vnfm.monitor_drivers.ping.ping namespace = tacker.vnfm.monitor_drivers.ceilometer.ceilometer +namespace = tacker.tacker.policy_actions.autoscaling.autoscaling +namespace = tacker.tacker.policy_actions.respawn.respawn +namespace = tacker.tacker.policy_actions.log.log namespace = tacker.alarm_receiver namespace = keystonemiddleware.auth_token namespace = oslo.middleware diff --git a/setup.cfg b/setup.cfg index 985f47813..6b7f71d69 100644 --- a/setup.cfg +++ b/setup.cfg @@ -59,6 +59,11 @@ tacker.tacker.monitor.drivers = http_ping = tacker.vnfm.monitor_drivers.http_ping.http_ping:VNFMonitorHTTPPing tacker.tacker.alarm_monitor.drivers = ceilometer = tacker.vnfm.monitor_drivers.ceilometer.ceilometer:VNFMonitorCeilometer +tacker.tacker.policy.actions = + autoscaling = tacker.vnfm.policy_actions.autoscaling.autoscaling:VNFActionAutoscaling + respawn = tacker.vnfm.policy_actions.respawn.respawn:VNFActionRespawn + log_only = tacker.vnfm.policy_actions.log.log:VNFActionLogOnly + log_and_kill = tacker.vnfm.policy_actions.log.log:VNFActionLogAndKill oslo.config.opts = tacker.common.config = tacker.common.config:config_opts tacker.wsgi = tacker.wsgi:config_opts diff --git a/tacker/tests/constants.py b/tacker/tests/constants.py index e907f6017..3f2c9fba8 100644 --- a/tacker/tests/constants.py +++ b/tacker/tests/constants.py @@ -15,7 +15,7 @@ DEFAULT_ALARM_ACTIONS = ['respawn', 'log', 'log_and_kill', 'notify'] VNF_CIRROS_CREATE_TIMEOUT = 300 VNFC_CREATE_TIMEOUT = 600 VNF_CIRROS_DELETE_TIMEOUT = 300 -VNF_CIRROS_DEAD_TIMEOUT = 250 +VNF_CIRROS_DEAD_TIMEOUT = 500 ACTIVE_SLEEP_TIME = 3 DEAD_SLEEP_TIME = 1 SCALE_WINDOW_SLEEP_TIME = 120 diff --git a/tacker/tests/etc/samples/sample-tosca-vnfd-multi-vdu-monitoring.yaml b/tacker/tests/etc/samples/sample-tosca-vnfd-multi-vdu-monitoring.yaml index 8bb5985a5..097d0c4fc 100644 --- a/tacker/tests/etc/samples/sample-tosca-vnfd-multi-vdu-monitoring.yaml +++ b/tacker/tests/etc/samples/sample-tosca-vnfd-multi-vdu-monitoring.yaml @@ -63,7 +63,7 @@ topology_template: type: tosca.nodes.nfv.VDU.Tacker properties: image: cirros-0.3.5-x86_64-disk - flavor: m1.medium + flavor: m1.tiny availability_zone: nova mgmt_driver: noop config: | diff --git a/tacker/tests/unit/vnfm/test_plugin.py b/tacker/tests/unit/vnfm/test_plugin.py index fc38a4823..7f5a172bd 100644 --- a/tacker/tests/unit/vnfm/test_plugin.py +++ b/tacker/tests/unit/vnfm/test_plugin.py @@ -434,33 +434,20 @@ class TestVNFMPlugin(db_base.SqlTestCase): self.assertEqual(expected_result, trigger_result) @patch('tacker.db.vnfm.vnfm_db.VNFMPluginDb.get_vnf') - @patch('tacker.vnfm.monitor.ActionPolicy.get_policy') - def test_create_vnf_trigger_respawn(self, mock_get_policy, mock_get_vnf): + def test_create_vnf_trigger_respawn(self, mock_get_vnf): dummy_vnf = self._get_dummy_active_vnf( utils.vnfd_alarm_respawn_tosca_template) mock_get_vnf.return_value = dummy_vnf - mock_action_class = mock.Mock() - mock_get_policy.return_value = mock_action_class self._test_create_vnf_trigger(policy_name="vdu_hcpu_usage_respawning", action_value="respawn") - mock_get_policy.assert_called_once_with('respawn', 'test_vim') - mock_action_class.execute_action.assert_called_once_with( - self.vnfm_plugin, dummy_vnf) @patch('tacker.db.vnfm.vnfm_db.VNFMPluginDb.get_vnf') - @patch('tacker.vnfm.monitor.ActionPolicy.get_policy') - def test_create_vnf_trigger_scale(self, mock_get_policy, mock_get_vnf): + def test_create_vnf_trigger_scale(self, mock_get_vnf): dummy_vnf = self._get_dummy_active_vnf( utils.vnfd_alarm_scale_tosca_template) mock_get_vnf.return_value = dummy_vnf - mock_action_class = mock.Mock() - mock_get_policy.return_value = mock_action_class - scale_body = {'scale': {'policy': 'SP1', 'type': 'out'}} self._test_create_vnf_trigger(policy_name="vdu_hcpu_usage_scaling_out", action_value="SP1-out") - mock_get_policy.assert_called_once_with('scaling', 'test_vim') - mock_action_class.execute_action.assert_called_once_with( - self.vnfm_plugin, dummy_vnf, scale_body) @patch('tacker.db.vnfm.vnfm_db.VNFMPluginDb.get_vnf') def test_get_vnf_policies(self, mock_get_vnf): diff --git a/tacker/vnfm/monitor.py b/tacker/vnfm/monitor.py index dee488550..cb654a6ec 100644 --- a/tacker/vnfm/monitor.py +++ b/tacker/vnfm/monitor.py @@ -14,7 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. -import abc + import inspect import threading import time @@ -23,14 +23,12 @@ from oslo_config import cfg from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import timeutils -import six + from tacker.common import driver_manager from tacker import context as t_context from tacker.db.common_services import common_services_db from tacker.plugins.common import constants -from tacker.vnfm.infra_drivers.openstack import heat_client as hc -from tacker.vnfm import vim_client LOG = logging.getLogger(__name__) CONF = cfg.CONF @@ -281,126 +279,3 @@ class VNFAlarmMonitor(object): def process_alarm(self, driver, vnf_dict, kwargs): return self._invoke(driver, vnf=vnf_dict, kwargs=kwargs) - - -@six.add_metaclass(abc.ABCMeta) -class ActionPolicy(object): - @classmethod - @abc.abstractmethod - def execute_action(cls, plugin, vnf_dict): - pass - - _POLICIES = {} - - @staticmethod - def register(policy, infra_driver=None): - def _register(cls): - cls._POLICIES.setdefault(policy, {})[infra_driver] = cls - return cls - return _register - - @classmethod - def get_policy(cls, policy, infra_driver=None): - action_clses = cls._POLICIES.get(policy) - if not action_clses: - return None - cls = action_clses.get(infra_driver) - if cls: - return cls - return action_clses.get(None) - - @classmethod - def get_supported_actions(cls): - return cls._POLICIES.keys() - - -@ActionPolicy.register('respawn', 'openstack') -class ActionRespawnHeat(ActionPolicy): - @classmethod - def execute_action(cls, plugin, vnf_dict): - vnf_id = vnf_dict['id'] - LOG.info(_('vnf %s is dead and needs to be respawned'), vnf_id) - attributes = vnf_dict['attributes'] - vim_id = vnf_dict['vim_id'] - # TODO(anyone) set the current request ctxt - context = t_context.get_admin_context() - - def _update_failure_count(): - failure_count = int(attributes.get('failure_count', '0')) + 1 - failure_count_str = str(failure_count) - LOG.debug(_("vnf %(vnf_id)s failure count %(failure_count)s"), - {'vnf_id': vnf_id, 'failure_count': failure_count_str}) - attributes['failure_count'] = failure_count_str - attributes['dead_instance_id_' + failure_count_str] = vnf_dict[ - 'instance_id'] - - def _fetch_vim(vim_uuid): - return vim_client.VimClient().get_vim(context, vim_uuid) - - def _delete_heat_stack(vim_auth): - placement_attr = vnf_dict.get('placement_attr', {}) - region_name = placement_attr.get('region_name') - heatclient = hc.HeatClient(auth_attr=vim_auth, - region_name=region_name) - heatclient.delete(vnf_dict['instance_id']) - LOG.debug(_("Heat stack %s delete initiated"), vnf_dict[ - 'instance_id']) - _log_monitor_events(context, vnf_dict, "ActionRespawnHeat invoked") - - def _respin_vnf(): - update_vnf_dict = plugin.create_vnf_sync(context, vnf_dict) - LOG.info(_('respawned new vnf %s'), update_vnf_dict['id']) - plugin.config_vnf(context, update_vnf_dict) - return update_vnf_dict - - if plugin._mark_vnf_dead(vnf_dict['id']): - _update_failure_count() - vim_res = _fetch_vim(vim_id) - if vnf_dict['attributes'].get('monitoring_policy'): - plugin._vnf_monitor.mark_dead(vnf_dict['id']) - _delete_heat_stack(vim_res['vim_auth']) - updated_vnf = _respin_vnf() - plugin.add_vnf_to_monitor(updated_vnf, vim_res['vim_type']) - LOG.debug(_("VNF %s added to monitor thread"), updated_vnf[ - 'id']) - if vnf_dict['attributes'].get('alarming_policy'): - _delete_heat_stack(vim_res['vim_auth']) - vnf_dict['attributes'].pop('alarming_policy') - _respin_vnf() - - -@ActionPolicy.register('scaling') -class ActionAutoscalingHeat(ActionPolicy): - @classmethod - def execute_action(cls, plugin, vnf_dict, scale): - vnf_id = vnf_dict['id'] - _log_monitor_events(t_context.get_admin_context(), - vnf_dict, - "ActionAutoscalingHeat invoked") - plugin.create_vnf_scale(t_context.get_admin_context(), vnf_id, scale) - - -@ActionPolicy.register('log') -class ActionLogOnly(ActionPolicy): - @classmethod - def execute_action(cls, plugin, vnf_dict): - vnf_id = vnf_dict['id'] - LOG.error(_('vnf %s dead'), vnf_id) - _log_monitor_events(t_context.get_admin_context(), - vnf_dict, - "ActionLogOnly invoked") - - -@ActionPolicy.register('log_and_kill') -class ActionLogAndKill(ActionPolicy): - @classmethod - def execute_action(cls, plugin, vnf_dict): - _log_monitor_events(t_context.get_admin_context(), - vnf_dict, - "ActionLogAndKill invoked") - vnf_id = vnf_dict['id'] - if plugin._mark_vnf_dead(vnf_dict['id']): - if vnf_dict['attributes'].get('monitoring_policy'): - plugin._vnf_monitor.mark_dead(vnf_dict['id']) - plugin.delete_vnf(t_context.get_admin_context(), vnf_id) - LOG.error(_('vnf %s dead'), vnf_id) diff --git a/tacker/vnfm/plugin.py b/tacker/vnfm/plugin.py index d60d88a7a..0658e9df2 100644 --- a/tacker/vnfm/plugin.py +++ b/tacker/vnfm/plugin.py @@ -43,7 +43,8 @@ CONF = cfg.CONF def config_opts(): return [('tacker', VNFMMgmtMixin.OPTS), - ('tacker', VNFMPlugin.OPTS)] + ('tacker', VNFMPlugin.OPTS_INFRA_DRIVER), + ('tacker', VNFMPlugin.OPTS_POLICY_ACTION)] class VNFMMgmtMixin(object): @@ -111,12 +112,21 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): Plugin which supports Tacker framework """ - OPTS = [ + OPTS_INFRA_DRIVER = [ cfg.ListOpt( 'infra_driver', default=['noop', 'openstack'], help=_('Hosting vnf drivers tacker plugin will use')), ] - cfg.CONF.register_opts(OPTS, 'tacker') + cfg.CONF.register_opts(OPTS_INFRA_DRIVER, 'tacker') + + OPTS_POLICY_ACTION = [ + cfg.ListOpt( + 'policy_action', default=['autoscaling', 'respawn', + 'log_only', 'log_and_kill'], + help=_('Hosting vnf drivers tacker plugin will use')), + ] + cfg.CONF.register_opts(OPTS_POLICY_ACTION, 'tacker') + supported_extension_aliases = ['vnfm'] def __init__(self): @@ -127,6 +137,9 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): self._vnf_manager = driver_manager.DriverManager( 'tacker.tacker.vnfm.drivers', cfg.CONF.tacker.infra_driver) + self._vnf_action = driver_manager.DriverManager( + 'tacker.tacker.policy.actions', + cfg.CONF.tacker.policy_action) self._vnf_monitor = monitor.VNFMonitor(self.boot_wait) self._vnf_alarm_monitor = monitor.VNFAlarmMonitor() @@ -203,15 +216,15 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): tosca) LOG.debug(_('vnfd %s'), vnfd) - def add_vnf_to_monitor(self, vnf_dict, infra_driver): + def add_vnf_to_monitor(self, context, vnf_dict): dev_attrs = vnf_dict['attributes'] mgmt_url = vnf_dict['mgmt_url'] if 'monitoring_policy' in dev_attrs and mgmt_url: def action_cb(action): - action_cls = monitor.ActionPolicy.get_policy(action, - infra_driver) - if action_cls: - action_cls.execute_action(self, hosting_vnf['vnf']) + LOG.debug('policy action: %s', action) + self._vnf_action.invoke( + action, 'execute_action', plugin=self, context=context, + vnf_dict=hosting_vnf['vnf'], args={}) hosting_vnf = self._vnf_monitor.to_hosting_vnf( vnf_dict, action_cb) @@ -376,7 +389,7 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): def create_vnf_wait(): self._create_vnf_wait(context, vnf_dict, vim_auth, infra_driver) if vnf_dict['status'] is not constants.ERROR: - self.add_vnf_to_monitor(vnf_dict, infra_driver) + self.add_vnf_to_monitor(context, vnf_dict) self.config_vnf(context, vnf_dict) self.spawn_n(create_vnf_wait) return vnf_dict @@ -766,11 +779,9 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): if trigger['action_name'] in constants.DEFAULT_ALARM_ACTIONS: action = trigger['action_name'] LOG.debug(_('vnf for monitoring: %s'), vnf_dict) - infra_driver, vim_auth = self._get_infra_driver(context, vnf_dict) - action_cls = monitor.ActionPolicy.get_policy(action, - infra_driver) - if action_cls: - action_cls.execute_action(self, vnf_dict) + self._vnf_action.invoke( + action, 'execute_action', plugin=self, context=context, + vnf_dict=vnf_dict, args={}) if trigger.get('bckend_policy'): bckend_policy = trigger['bckend_policy'] @@ -782,17 +793,14 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin): {"status": vnf_dict['status'], "vnfid": vnf_dict['id']}) return - action = 'scaling' + action = 'autoscaling' scale = {} scale.setdefault('scale', {}) scale['scale']['type'] = trigger['bckend_action'] scale['scale']['policy'] = bckend_policy['name'] - infra_driver, vim_auth = self._get_infra_driver(context, - vnf_dict) - action_cls = monitor.ActionPolicy.get_policy(action, - infra_driver) - if action_cls: - action_cls.execute_action(self, vnf_dict, scale) + self._vnf_action.invoke( + action, 'execute_action', plugin=self, context=context, + vnf_dict=vnf_dict, args=scale) def create_vnf_trigger( self, context, vnf_id, trigger): diff --git a/tacker/vnfm/policy_actions/__init__.py b/tacker/vnfm/policy_actions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/vnfm/policy_actions/abstract_action.py b/tacker/vnfm/policy_actions/abstract_action.py new file mode 100644 index 000000000..43dc58fe8 --- /dev/null +++ b/tacker/vnfm/policy_actions/abstract_action.py @@ -0,0 +1,38 @@ +# 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 abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class AbstractPolicyAction(object): + @abc.abstractmethod + def get_type(self): + """Return one of predefined type of the hosting vnf drivers.""" + pass + + @abc.abstractmethod + def get_name(self): + """Return a symbolic name for the service VM plugin.""" + pass + + @abc.abstractmethod + def get_description(self): + pass + + @abc.abstractmethod + def execute_action(self, plugin, context, vnf_dict, args): + """args: policy is enabled to execute with additional arguments.""" + pass diff --git a/tacker/vnfm/policy_actions/autoscaling/__init__.py b/tacker/vnfm/policy_actions/autoscaling/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/vnfm/policy_actions/autoscaling/autoscaling.py b/tacker/vnfm/policy_actions/autoscaling/autoscaling.py new file mode 100644 index 000000000..c81d7edde --- /dev/null +++ b/tacker/vnfm/policy_actions/autoscaling/autoscaling.py @@ -0,0 +1,50 @@ +# +# 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 timeutils + +from tacker.db.common_services import common_services_db +from tacker.plugins.common import constants +from tacker.vnfm.policy_actions import abstract_action + +LOG = logging.getLogger(__name__) + + +def _log_monitor_events(context, vnf_dict, evt_details): + _cos_db_plg = common_services_db.CommonServicesPluginDb() + _cos_db_plg.create_event(context, res_id=vnf_dict['id'], + res_type=constants.RES_TYPE_VNF, + res_state=vnf_dict['status'], + evt_type=constants.RES_EVT_MONITOR, + tstamp=timeutils.utcnow(), + details=evt_details) + + +class VNFActionAutoscaling(abstract_action.AbstractPolicyAction): + def get_type(self): + return 'autoscaling' + + def get_name(self): + return 'autoscaling' + + def get_description(self): + return 'Tacker VNF auto-scaling policy' + + def execute_action(self, plugin, context, vnf_dict, args): + vnf_id = vnf_dict['id'] + _log_monitor_events(context, + vnf_dict, + "ActionAutoscalingHeat invoked") + plugin.create_vnf_scale(context, vnf_id, args) diff --git a/tacker/vnfm/policy_actions/log/__init__.py b/tacker/vnfm/policy_actions/log/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/vnfm/policy_actions/log/log.py b/tacker/vnfm/policy_actions/log/log.py new file mode 100644 index 000000000..9fce410ea --- /dev/null +++ b/tacker/vnfm/policy_actions/log/log.py @@ -0,0 +1,72 @@ +# +# 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 timeutils + +from tacker.db.common_services import common_services_db +from tacker.plugins.common import constants +from tacker.vnfm.policy_actions import abstract_action + +LOG = logging.getLogger(__name__) + + +def _log_monitor_events(context, vnf_dict, evt_details): + _cos_db_plg = common_services_db.CommonServicesPluginDb() + _cos_db_plg.create_event(context, res_id=vnf_dict['id'], + res_type=constants.RES_TYPE_VNF, + res_state=vnf_dict['status'], + evt_type=constants.RES_EVT_MONITOR, + tstamp=timeutils.utcnow(), + details=evt_details) + + +class VNFActionLogOnly(abstract_action.AbstractPolicyAction): + def get_type(self): + return 'log_only' + + def get_name(self): + return 'log_only' + + def get_description(self): + return 'Tacker VNF logging policy' + + def execute_action(self, plugin, context, vnf_dict, args): + vnf_id = vnf_dict['id'] + LOG.error(_('vnf %s dead'), vnf_id) + _log_monitor_events(context, + vnf_dict, + "ActionLogOnly invoked") + + +class VNFActionLogAndKill(abstract_action.AbstractPolicyAction): + def get_type(self): + return 'log_and_kill' + + def get_name(self): + return 'log_and_kill' + + def get_description(self): + return 'Tacker VNF log_and_kill policy' + + def execute_action(self, plugin, context, vnf_dict, args): + _log_monitor_events(context, + vnf_dict, + "ActionLogAndKill invoked") + vnf_id = vnf_dict['id'] + if plugin._mark_vnf_dead(vnf_dict['id']): + if vnf_dict['attributes'].get('monitoring_policy'): + plugin._vnf_monitor.mark_dead(vnf_dict['id']) + plugin.delete_vnf(context, vnf_id) + LOG.error(_('vnf %s dead'), vnf_id) diff --git a/tacker/vnfm/policy_actions/respawn/__init__.py b/tacker/vnfm/policy_actions/respawn/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tacker/vnfm/policy_actions/respawn/respawn.py b/tacker/vnfm/policy_actions/respawn/respawn.py new file mode 100644 index 000000000..e315164f9 --- /dev/null +++ b/tacker/vnfm/policy_actions/respawn/respawn.py @@ -0,0 +1,94 @@ +# +# 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 timeutils + +from tacker.db.common_services import common_services_db +from tacker.plugins.common import constants +from tacker.vnfm.infra_drivers.openstack import heat_client as hc +from tacker.vnfm.policy_actions import abstract_action +from tacker.vnfm import vim_client + +LOG = logging.getLogger(__name__) + + +def _log_monitor_events(context, vnf_dict, evt_details): + _cos_db_plg = common_services_db.CommonServicesPluginDb() + _cos_db_plg.create_event(context, res_id=vnf_dict['id'], + res_type=constants.RES_TYPE_VNF, + res_state=vnf_dict['status'], + evt_type=constants.RES_EVT_MONITOR, + tstamp=timeutils.utcnow(), + details=evt_details) + + +class VNFActionRespawn(abstract_action.AbstractPolicyAction): + def get_type(self): + return 'respawn' + + def get_name(self): + return 'respawn' + + def get_description(self): + return 'Tacker VNF respawning policy' + + def execute_action(self, plugin, context, vnf_dict, args): + vnf_id = vnf_dict['id'] + LOG.info(_('vnf %s is dead and needs to be respawned'), vnf_id) + attributes = vnf_dict['attributes'] + vim_id = vnf_dict['vim_id'] + + def _update_failure_count(): + failure_count = int(attributes.get('failure_count', '0')) + 1 + failure_count_str = str(failure_count) + LOG.debug(_("vnf %(vnf_id)s failure count %(failure_count)s"), + {'vnf_id': vnf_id, 'failure_count': failure_count_str}) + attributes['failure_count'] = failure_count_str + attributes['dead_instance_id_' + failure_count_str] = vnf_dict[ + 'instance_id'] + + def _fetch_vim(vim_uuid): + return vim_client.VimClient().get_vim(context, vim_uuid) + + def _delete_heat_stack(vim_auth): + placement_attr = vnf_dict.get('placement_attr', {}) + region_name = placement_attr.get('region_name') + heatclient = hc.HeatClient(auth_attr=vim_auth, + region_name=region_name) + heatclient.delete(vnf_dict['instance_id']) + LOG.debug(_("Heat stack %s delete initiated"), vnf_dict[ + 'instance_id']) + _log_monitor_events(context, vnf_dict, "ActionRespawnHeat invoked") + + def _respin_vnf(): + update_vnf_dict = plugin.create_vnf_sync(context, vnf_dict) + LOG.info(_('respawned new vnf %s'), update_vnf_dict['id']) + plugin.config_vnf(context, update_vnf_dict) + return update_vnf_dict + + if plugin._mark_vnf_dead(vnf_dict['id']): + _update_failure_count() + vim_res = _fetch_vim(vim_id) + if vnf_dict['attributes'].get('monitoring_policy'): + plugin._vnf_monitor.mark_dead(vnf_dict['id']) + _delete_heat_stack(vim_res['vim_auth']) + updated_vnf = _respin_vnf() + plugin.add_vnf_to_monitor(context, updated_vnf) + LOG.debug(_("VNF %s added to monitor thread"), updated_vnf[ + 'id']) + if vnf_dict['attributes'].get('alarming_policy'): + _delete_heat_stack(vim_res['vim_auth']) + vnf_dict['attributes'].pop('alarming_policy') + _respin_vnf()