This patch adds below functionality: - New policy action ``vdu_autoheal`` for recovering failed VDUs as reported by the respective monitoring driver configured in the monitoring policy of the VNFD template. - Add unit/functional tests. - Added oslo_versioned library to implement HealVnfRequest object. Note: The implementation of vdu_autoheal policy action will support HealVnfRequest interface as mentioned in the ETSI standard [1] [1]: https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/003/02.05.01_60/gs_NFV-SOL003v020501p.pdf Implements: blueprint vdu-auto-healing Change-Id: If62acbdac41c92842de0ae3b7dedcda9fd1f86e6changes/95/612595/16
parent
4b9bcfeeef
commit
5f1e48ff46
@ -0,0 +1,11 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added a new monitoring policy action ``vdu_autoheal`` to bring back the
|
||||
failed VDU. If a VNF contains one or more VDUs with monitoring policy
|
||||
action set to `vdu_autoheal` and if any one of the VDU is unreachable,
|
||||
it will simply delete the resources of that particular VDUs and re-create
|
||||
them again.
|
||||
|
||||
The `vdu_autoheal` monitoring policy action is implemented only for
|
||||
openstack infra driver.
|
@ -0,0 +1,27 @@
|
||||
# Copyright 2018 NTT DATA.
|
||||
#
|
||||
# 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.
|
||||
|
||||
# NOTE(bhagyashris): You may scratch your head as you see code that imports
|
||||
# this module and then accesses attributes for objects such as Instance,
|
||||
# etc, yet you do not see these attributes in here. Never fear, there is
|
||||
# a little bit of magic. When objects are registered, an attribute is set
|
||||
# on this module automatically, pointing to the newest/latest version of
|
||||
# the object.
|
||||
|
||||
|
||||
def register_all():
|
||||
# NOTE(bhagyashris): You must make sure your object gets imported in this
|
||||
# function in order for it to be registered by services that may
|
||||
# need to receive it via RPC.
|
||||
__import__('tacker.objects.heal_vnf_request')
|
@ -0,0 +1,48 @@
|
||||
# Copyright 2018 NTT DATA.
|
||||
#
|
||||
# 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_utils import versionutils
|
||||
from oslo_versionedobjects import base as ovoo_base
|
||||
|
||||
from tacker import objects
|
||||
|
||||
|
||||
def get_attrname(name):
|
||||
"""Return the mangled name of the attribute's underlying storage."""
|
||||
return '_obj_' + name
|
||||
|
||||
|
||||
class TackerObjectRegistry(ovoo_base.VersionedObjectRegistry):
|
||||
notification_classes = []
|
||||
|
||||
def registration_hook(self, cls, index):
|
||||
# NOTE(bhagyashris): This is called when an object is registered,
|
||||
# and is responsible for maintaining tacker.objects.$OBJECT
|
||||
# as the highest-versioned implementation of a given object.
|
||||
version = versionutils.convert_version_to_tuple(cls.VERSION)
|
||||
if not hasattr(objects, cls.obj_name()):
|
||||
setattr(objects, cls.obj_name(), cls)
|
||||
else:
|
||||
cur_version = versionutils.convert_version_to_tuple(
|
||||
getattr(objects, cls.obj_name()).VERSION)
|
||||
if version >= cur_version:
|
||||
setattr(objects, cls.obj_name(), cls)
|
||||
|
||||
|
||||
class TackerObject(ovoo_base.VersionedObject):
|
||||
# NOTE(bhagyashris): OBJ_PROJECT_NAMESPACE needs to be set so that nova,
|
||||
# tacker, and other objects can exist on the same bus and be distinguished
|
||||
# from one another.
|
||||
OBJ_SERIAL_NAMESPACE = 'tacker_object'
|
||||
OBJ_PROJECT_NAMESPACE = 'tacker'
|
@ -0,0 +1,22 @@
|
||||
# Copyright 2018 NTT Data.
|
||||
# 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_versionedobjects import fields
|
||||
|
||||
|
||||
# Import fields from oslo.versionedobjects
|
||||
StringField = fields.StringField
|
||||
ListOfObjectsField = fields.ListOfObjectsField
|
||||
ListOfStringsField = fields.ListOfStringsField
|
@ -0,0 +1,42 @@
|
||||
# Copyright 2018 NTT Data.
|
||||
# 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.objects import base
|
||||
from tacker.objects import fields
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class HealVnfAdditionalParams(base.TackerObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'parameter': fields.StringField(),
|
||||
'cause': fields.ListOfStringsField()
|
||||
}
|
||||
|
||||
|
||||
@base.TackerObjectRegistry.register
|
||||
class HealVnfRequest(base.TackerObject):
|
||||
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'cause': fields.StringField(),
|
||||
'additional_params': fields.ListOfObjectsField(
|
||||
'HealVnfAdditionalParams')
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
description: Demo example
|
||||
|
||||
metadata:
|
||||
template_name: sample-tosca-vnfd
|
||||
|
||||
topology_template:
|
||||
node_templates:
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.VDU.Tacker
|
||||
capabilities:
|
||||
nfv_compute:
|
||||
properties:
|
||||
disk_size: 1 GB
|
||||
mem_size: 512 MB
|
||||
num_cpus: 1
|
||||
properties:
|
||||
image: cirros-0.4.0-x86_64-disk
|
||||
mgmt_driver: noop
|
||||
availability_zone: nova
|
||||
mgmt_driver: noop
|
||||
monitoring_policy:
|
||||
name: ping
|
||||
parameters:
|
||||
monitoring_delay: 45
|
||||
count: 3
|
||||
interval: 1
|
||||
timeout: 2
|
||||
actions:
|
||||
failure: vdu_autoheal
|
||||
|
||||
CP1:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
management: true
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL1
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
VDU2:
|
||||
type: tosca.nodes.nfv.VDU.Tacker
|
||||
capabilities:
|
||||
nfv_compute:
|
||||
properties:
|
||||
disk_size: 1 GB
|
||||
mem_size: 512 MB
|
||||
num_cpus: 1
|
||||
properties:
|
||||
image: cirros-0.4.0-x86_64-disk
|
||||
mgmt_driver: noop
|
||||
availability_zone: nova
|
||||
mgmt_driver: noop
|
||||
monitoring_policy:
|
||||
name: ping
|
||||
parameters:
|
||||
monitoring_delay: 45
|
||||
count: 3
|
||||
interval: 1
|
||||
timeout: 2
|
||||
actions:
|
||||
failure: vdu_autoheal
|
||||
user_data_format: RAW
|
||||
user_data: |
|
||||
#!/bin/sh
|
||||
echo "my hostname is `hostname`" > /tmp/hostname
|
||||
df -h > /home/cirros/diskinfo
|
||||
sleep 90
|
||||
sudo ifdown eth0
|
||||
|
||||
CP2:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
management: true
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL1
|
||||
- virtualBinding:
|
||||
node: VDU2
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net_mgmt
|
||||
vendor: Tacker
|
@ -0,0 +1,55 @@
|
||||
tosca_definitions_version: tosca_simple_profile_for_nfv_1_0_0
|
||||
description: Demo example
|
||||
|
||||
metadata:
|
||||
template_name: sample-tosca-vnfd
|
||||
|
||||
topology_template:
|
||||
node_templates:
|
||||
VDU1:
|
||||
type: tosca.nodes.nfv.VDU.Tacker
|
||||
capabilities:
|
||||
nfv_compute:
|
||||
properties:
|
||||
disk_size: 1 GB
|
||||
mem_size: 512 MB
|
||||
num_cpus: 1
|
||||
properties:
|
||||
image: cirros-0.4.0-x86_64-disk
|
||||
mgmt_driver: noop
|
||||
availability_zone: nova
|
||||
mgmt_driver: noop
|
||||
monitoring_policy:
|
||||
name: ping
|
||||
parameters:
|
||||
monitoring_delay: 45
|
||||
count: 3
|
||||
interval: 1
|
||||
timeout: 2
|
||||
actions:
|
||||
failure: vdu_autoheal
|
||||
user_data_format: RAW
|
||||
user_data: |
|
||||
#!/bin/sh
|
||||
echo "my hostname is `hostname`" > /tmp/hostname
|
||||
df -h > /home/cirros/diskinfo
|
||||
sleep 90
|
||||
sudo ifdown eth0
|
||||
|
||||
CP1:
|
||||
type: tosca.nodes.nfv.CP.Tacker
|
||||
properties:
|
||||
management: true
|
||||
anti_spoofing_protection: false
|
||||
requirements:
|
||||
- virtualLink:
|
||||
node: VL1
|
||||
- virtualBinding:
|
||||
node: VDU1
|
||||
|
||||
VL1:
|
||||
type: tosca.nodes.nfv.VL
|
||||
properties:
|
||||
network_name: net_mgmt
|
||||
vendor: Tacker
|
||||
|
@ -0,0 +1,61 @@
|
||||
# Copyright 2018 NTT DATA.
|
||||
#
|
||||
# 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 six
|
||||
|
||||
from tacker.objects import base
|
||||
from tacker.objects import fields
|
||||
from tacker.tests.unit import base as test_base
|
||||
|
||||
|
||||
class TestString(test_base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestString, self).setUp()
|
||||
self.field = fields.StringField()
|
||||
self.coerce_good_values = [('foo', 'foo'), (1, '1'), (True, 'True')]
|
||||
if six.PY2:
|
||||
self.coerce_good_values.append((int(1), '1'))
|
||||
self.coerce_bad_values = [None]
|
||||
|
||||
def test_stringify(self):
|
||||
self.assertEqual("'123'", self.field.stringify(123))
|
||||
|
||||
|
||||
class TestListOfStrings(test_base.TestCase):
|
||||
def setUp(self):
|
||||
super(TestListOfStrings, self).setUp()
|
||||
self.field = fields.ListOfStringsField()
|
||||
|
||||
def test_list_of_string(self):
|
||||
self.assertEqual("['abc']", self.field.stringify(['abc']))
|
||||
|
||||
|
||||
class TestListOfObjects(test_base.TestCase):
|
||||
|
||||
def test_list_of_obj(self):
|
||||
@base.TackerObjectRegistry.register_if(False)
|
||||
class MyObjElement(base.TackerObject):
|
||||
fields = {'foo': fields.StringField()}
|
||||
|
||||
def __init__(self, foo):
|
||||
super(MyObjElement, self).__init__()
|
||||
self.foo = foo
|
||||
|
||||
@base.TackerObjectRegistry.register_if(False)
|
||||
class MyList(base.TackerObject):
|
||||
fields = {'objects': fields.ListOfObjectsField('MyObjElement')}
|
||||
|
||||
mylist = MyList()
|
||||
mylist.objects = [MyObjElement('a'), MyObjElement('b')]
|
||||
self.assertEqual(['a', 'b'], [x.foo for x in mylist.objects])
|
@ -0,0 +1,162 @@
|
||||
# Copyright 2018 NTT DATA
|
||||
# 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 mock
|
||||
|
||||
from tacker import context
|
||||
from tacker.db.common_services import common_services_db_plugin
|
||||
from tacker.objects import heal_vnf_request
|
||||
from tacker.plugins.common import constants
|
||||
from tacker.tests.unit import base
|
||||
from tacker.vnfm.infra_drivers.openstack import vdu
|
||||
|
||||
|
||||
vnf_dict = {
|
||||
'attributes': {
|
||||
'heat_template': {
|
||||
'outputs': {
|
||||
'mgmt_ip-VDU1': {
|
||||
'value': {
|
||||
'get_attr': [
|
||||
'CP1', 'fixed_ips', 0, 'ip_address']
|
||||
}
|
||||
}
|
||||
},
|
||||
'description': 'Demo example\n',
|
||||
'parameters': {},
|
||||
'resources': {
|
||||
'VDU1': {
|
||||
'type': 'OS::Nova::Server',
|
||||
'properties': {
|
||||
'user_data_format': 'SOFTWARE_CONFIG',
|
||||
'availability_zone': 'nova',
|
||||
'image': 'cirros-0.4.0-x86_64-disk',
|
||||
'config_drive': False,
|
||||
'flavor': {'get_resource': 'VDU1_flavor'},
|
||||
'networks': [{'port': {'get_resource': 'CP1'}}]
|
||||
}
|
||||
},
|
||||
'CP1': {
|
||||
'type': 'OS::Neutron::Port',
|
||||
'properties': {
|
||||
'port_security_enabled': False,
|
||||
'network': 'net_mgmt'
|
||||
}
|
||||
},
|
||||
'VDU1_flavor': {
|
||||
'type': 'OS::Nova::Flavor',
|
||||
'properties': {'vcpus': 1, 'disk': 1, 'ram': 512}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
'status': 'ACTIVE',
|
||||
'vnfd_id': '576acf48-b9df-491d-a57c-342de660ec78',
|
||||
'tenant_id': '13d2ca8de70d48b2a2e0dbac2c327c0b',
|
||||
'vim_id': '3f41faa7-5630-47d2-9d4a-1216953c8887',
|
||||
'instance_id': 'd1121d3c-368b-4ac2-b39d-835aa3e4ccd8',
|
||||
'placement_attr': {'vim_name': 'openstack-vim'},
|
||||
'id': 'a27fc58e-66ae-4031-bba4-efede318c60b',
|
||||
'name': 'vnf_create_1'
|
||||
}
|
||||
|
||||
|
||||
class FakeHeatClient(mock.Mock):
|
||||
|
||||
class Stack(mock.Mock):
|
||||
stack_status = 'CREATE_COMPLETE'
|
||||
outputs = [{u'output_value': u'192.168.120.31', u'description':
|
||||
u'management ip address', u'output_key': u'mgmt_ip-vdu1'}]
|
||||
|
||||
def create(self, *args, **kwargs):
|
||||
return {'stack': {'id': '4a4c2d44-8a52-4895-9a75-9d1c76c3e738'}}
|
||||
|
||||
def get(self, id):
|
||||
return self.Stack()
|
||||
|
||||
def update(self, stack_id, **kwargs):
|
||||
return self.Stack()
|
||||
|
||||
def resource_mark_unhealthy(self, stack_id, resource_name,
|
||||
mark_unhealthy, resource_status_reason):
|
||||
return self.Stack()
|
||||
|
||||
|
||||
class TestVDU(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVDU, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self._mock_heat_client()
|
||||
|
||||
mock.patch('tacker.vnfm.vim_client.VimClient.get_vim').start()
|
||||
self.additional_paramas_obj = heal_vnf_request.HealVnfAdditionalParams(
|
||||
parameter='VDU1',
|
||||
cause=["Unable to reach while monitoring resource: 'VDU1'"])
|
||||
self.heal_request_data_obj = heal_vnf_request.HealVnfRequest(
|
||||
cause='VNF monitoring fails.',
|
||||
additional_params=[self.additional_paramas_obj])
|
||||
self.heal_vdu = vdu.Vdu(self.context, vnf_dict,
|
||||
self.heal_request_data_obj)
|
||||
|
||||
mock.patch('tacker.db.common_services.common_services_db_plugin.'
|
||||
'CommonServicesPluginDb.create_event'
|
||||
).start()
|
||||
self._cos_db_plugin = \
|
||||
common_services_db_plugin.CommonServicesPluginDb()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
def _mock_heat_client(self):
|
||||
self.heat_client = mock.Mock(wraps=FakeHeatClient())
|
||||
fake_heat_client = mock.Mock()
|
||||
fake_heat_client.return_value = self.heat_client
|
||||
self._mock(
|
||||
'tacker.vnfm.infra_drivers.openstack.heat_client.HeatClient',
|
||||
fake_heat_client)
|
||||
|
||||
@mock.patch('tacker.vnfm.vim_client.VimClient.get_vim')
|
||||
def test_heal_vdu(self, mock_get_vim):
|
||||
mock_get_vim.return_value = mock.MagicMock()
|
||||
|
||||
self.heal_vdu.heal_vdu()
|
||||
|
||||
self.heat_client.update.assert_called_once_with(
|
||||
stack_id=vnf_dict['instance_id'], existing=True)
|
||||
|
||||
self._cos_db_plugin.create_event.assert_called_with(
|
||||
self.context, res_id=vnf_dict['id'],
|
||||
res_type=constants.RES_TYPE_VNF, res_state=vnf_dict['status'],
|
||||
evt_type=constants.RES_EVT_HEAL, tstamp=mock.ANY,
|
||||
details=("HealVnfRequest invoked to update the stack '%s'" %
|
||||
vnf_dict['instance_id']))
|
||||
|
||||
@mock.patch('tacker.vnfm.vim_client.VimClient.get_vim')
|
||||
def test_resource_mark_unhealthy(self, mock_get_vim):
|
||||
mock_get_vim.return_value = mock.MagicMock()
|
||||
|
||||
self.heal_vdu._resource_mark_unhealthy()
|
||||
|
||||
self.heat_client.resource_mark_unhealthy.assert_called_once_with(
|
||||
stack_id=vnf_dict['instance_id'],
|
||||
resource_name=self.additional_paramas_obj.parameter,
|
||||
mark_unhealthy=True,
|
||||
resource_status_reason=self.additional_paramas_obj.cause)
|
||||
|
||||
self._cos_db_plugin.create_event.assert_called_with(
|
||||
self.context, res_id=vnf_dict['id'],
|
||||
res_type=constants.RES_TYPE_VNF, res_state=vnf_dict['status'],
|
||||
evt_type=constants.RES_EVT_HEAL, tstamp=mock.ANY,
|
||||
details="HealVnfRequest invoked to mark resource 'VDU1' "
|
||||
"to unhealthy.")
|
@ -0,0 +1,147 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import mock
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from tacker import context
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
from tacker.objects import heal_vnf_request
|
||||
from tacker.tests.unit.db import base as db_base
|
||||
from tacker.vnfm import plugin
|
||||
from tacker.vnfm.policy_actions.vdu_autoheal import vdu_autoheal
|
||||
|
||||
|
||||
vnf_dict = {
|
||||
'id': uuidutils.generate_uuid(),
|
||||
'mgmt_url': '{"VDU1": "a.b.c.d"}',
|
||||
'vim_id': '6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
'instance_id': 'a737497c-761c-11e5-89c3-9cb6541d805d',
|
||||
'attributes': {
|
||||
'heat_template': {
|
||||
'resources': {
|
||||
'VDU1': {
|
||||
'properties': {
|
||||
'networks': [{'port': {'get_resource': 'CP1'}}]}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class FakeDriverManager(mock.Mock):
|
||||
def invoke(self, *args, **kwargs):
|
||||
if 'create' in args:
|
||||
return uuidutils.generate_uuid()
|
||||
|
||||
if 'get_resource_info' in args:
|
||||
return {'resources': {'name': 'dummy_vnf',
|
||||
'type': 'dummy',
|
||||
'id': uuidutils.generate_uuid()}}
|
||||
|
||||
|
||||
class FakeVNFMonitor(mock.Mock):
|
||||
pass
|
||||
|
||||
|
||||
class TestVNFActionVduAutoheal(db_base.SqlTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestVNFActionVduAutoheal, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self._mock_device_manager()
|
||||
self._mock_vnf_monitor()
|
||||
self._insert_dummy_vim()
|
||||
self.vnfm_plugin = plugin.VNFMPlugin()
|
||||
self.vdu_autoheal = vdu_autoheal.VNFActionVduAutoheal()
|
||||
self.addCleanup(mock.patch.stopall)
|
||||
|
||||
def _mock_device_manager(self):
|
||||
self._device_manager = mock.Mock(wraps=FakeDriverManager())
|
||||
self._device_manager.__contains__ = mock.Mock(
|
||||
return_value=True)
|
||||
fake_device_manager = mock.Mock()
|
||||
fake_device_manager.return_value = self._device_manager
|
||||
self._mock(
|
||||
'tacker.common.driver_manager.DriverManager', fake_device_manager)
|
||||
|
||||
def _mock_vnf_monitor(self):
|
||||
self._vnf_monitor = mock.Mock(wraps=FakeVNFMonitor())
|
||||
fake_vnf_monitor = mock.Mock()
|
||||
fake_vnf_monitor.return_value = self._vnf_monitor
|
||||
self._mock(
|
||||
'tacker.vnfm.monitor.VNFMonitor', fake_vnf_monitor)
|
||||
|
||||
def _insert_dummy_vim(self):
|
||||
session = self.context.session
|
||||
vim_db = nfvo_db.Vim(
|
||||
id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||
name='fake_vim',
|
||||
description='fake_vim_description',
|
||||
type='test_vim',
|
||||
status='Active',
|
||||
deleted_at=datetime.min,
|
||||
placement_attr={'regions': ['RegionOne']})
|
||||
vim_auth_db = nfvo_db.VimAuth(
|
||||
vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
password='encrypted_pw',
|
||||
auth_url='http://localhost:5000',
|
||||
vim_project={'name': 'test_project'},
|
||||
auth_cred={'username': 'test_user', 'user_domain_id': 'default',
|
||||
'project_domain_id': 'default'})
|
||||
session.add(vim_db)
|
||||
session.add(vim_auth_db)
|
||||
session.flush()
|
||||
|
||||
@mock.patch('tacker.vnfm.plugin.VNFMPlugin.heal_vnf')
|
||||
@mock.patch('yaml.safe_load')
|
||||
@mock.patch('tacker.objects.HealVnfRequest')
|
||||
def test_vdu_autoheal_execute_action(self, mock_heal_vnf_request,
|
||||
mock_safe_load,
|
||||
mock_heal_vnf):
|
||||
# Here yaml.safe_load is mock as in the test case i am passing
|
||||
# vnf_dict containing having vnf_dict['attributes']['heat_template']
|
||||
# value in json format so while excution it giving the error as
|
||||
# dict object has no read attribute where as in actual execution the
|
||||
# value of vnf_dict['attributes']['heat_template'] is in ymal format.
|
||||
mock_safe_load.return_value = vnf_dict['attributes']['heat_template']
|
||||
resource_list = ['VDU1', 'CP1']
|
||||
additional_params = []
|
||||
for resource in resource_list:
|
||||
additional_paramas_obj = heal_vnf_request.HealVnfAdditionalParams(
|
||||
parameter=resource,
|
||||
cause=["Unable to reach while monitoring resource: '%s'" %
|
||||
resource])
|
||||
additional_params.append(additional_paramas_obj)
|
||||
heal_request_data_obj = heal_vnf_request.HealVnfRequest(
|
||||
cause='VNF monitoring fails.',
|
||||
additional_params=additional_params)
|
||||
mock_heal_vnf_request.return_value = heal_request_data_obj
|
||||
self.vdu_autoheal.execute_action(self.vnfm_plugin, self.context,
|
||||
vnf_dict, args={'vdu_name': 'VDU1'})
|
||||
mock_heal_vnf.assert_called_once_with(self.context, vnf_dict['id'],
|
||||
heal_request_data_obj)
|
||||
|
||||
@mock.patch('tacker.vnfm.policy_actions.vdu_autoheal.'
|
||||
'vdu_autoheal.LOG')
|
||||
def test_vdu_autoheal_action_with_no_vdu_name(self, mock_log):
|
||||
expected_error_msg = ("VDU resource of vnf '%s' is not present for "
|
||||
"autoheal." % vnf_dict['id'])
|
||||
self.vdu_autoheal.execute_action(self.vnfm_plugin, self.context,
|
||||
vnf_dict, args={})
|
||||
mock_log.error.assert_called_with(expected_error_msg)
|
@ -0,0 +1,23 @@
|
||||
# Copyright 2018 OpenStack Foundation.
|
||||
# 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.
|
||||
|
||||
# openstack infra constants
|
||||
|
||||
STACK_CREATE_IN_PROGRESS = "CREATE_IN_PROGRESS"
|
||||
STACK_CREATE_COMPLETE = "CREATE_COMPLETE"
|
||||
STACK_UPDATE_IN_PROGRESS = "UPDATE_IN_PROGRESS"
|
||||
STACK_UPDATE_COMPLETE = "UPDATE_COMPLETE"
|
||||
STACK_DELETE_IN_PROGRESS = "DELETE_IN_PROGRESS"
|
||||
STACK_DELETE_COMPLETE = "DELETE_COMPLETE"
|