Add toggle to recreate Cinder volumes in v1 Heal
Extend v1 Heal to accept an optional additionalParams key that toggles recreation of Cinder volumes. Add two [vnf_lcm] options in tacker.conf: - heal_vnfc_block_storage: default when the request omits the key - heal_include_block_storage_key: key name in additionalParams If neither is set, existing behavior is unchanged. Implements: blueprint v1-heal-cinder-volume Change-Id: I29ca4dceac40d3d0fe1fcb1f6bf4c4f1729e883b Signed-off-by: Hitomi Koba <hi-koba@kddi.com>
This commit is contained in:
@@ -702,6 +702,14 @@ grant_id:
|
||||
in: body
|
||||
required: false
|
||||
type: string
|
||||
heal_additional_params:
|
||||
description: |
|
||||
Additional parameters passed by the NFVO as input to the healing process,
|
||||
specific to the VNF being healed, as declared in the VNFD as part of
|
||||
"HealVnfOpConfig".
|
||||
in: body
|
||||
required: false
|
||||
type: key value pairs
|
||||
instantiated_vnf_info:
|
||||
description: |
|
||||
Information specific to an instantiated VNF instance. This attribute shall
|
||||
@@ -985,6 +993,23 @@ subscription_id_response:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
tacker_extension_heal_include_block_storage:
|
||||
description: |
|
||||
** Admin-configurable parameter name. **
|
||||
Controls whether related Cinder block storage is recreated when
|
||||
healing specified VNFC instance(s).
|
||||
|
||||
The key name is configured by ``[vnf_lcm] heal_include_block_storage_key``
|
||||
in ``tacker.conf`` (default: ``tacker_extension_heal_include_block_storage``).
|
||||
If the key is omitted, the default behavior is controlled by
|
||||
``[vnf_lcm] heal_vnfc_block_storage``.
|
||||
|
||||
**Recommendation:** Because both the key name and the default behavior
|
||||
depend on your deployment settings, it is recommended to confirm them
|
||||
with your environment administrator.
|
||||
in: body
|
||||
required: false
|
||||
type: boolean
|
||||
termination_type:
|
||||
description: |
|
||||
Indicates whether forceful or graceful termination is requested.
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"cause": "healing",
|
||||
"vnfcInstanceId": ["c51c98dc-b918-4681-a9eb-4f32a57c4e08"]
|
||||
"vnfcInstanceId": ["c51c98dc-b918-4681-a9eb-4f32a57c4e08"],
|
||||
"additionalParams": {
|
||||
"tacker_extension_heal_include_block_storage": false
|
||||
}
|
||||
}
|
||||
@@ -229,6 +229,8 @@ Request Parameters
|
||||
- vnfInstanceId: vnf_instance_id
|
||||
- cause: cause
|
||||
- vnfcInstanceId: vnfc_resource_info_ids
|
||||
- additionalParams: heal_additional_params
|
||||
- tacker_extension_heal_include_block_storage: tacker_extension_heal_include_block_storage
|
||||
|
||||
|
||||
Request Example
|
||||
|
||||
@@ -733,8 +733,14 @@ VDU1 information before healing:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack stack resource show HEAT_STACK_ID \
|
||||
VDU_NAME -c physical_resource_id -c resource_status
|
||||
$ openstack stack resource show HEAT_STACK_ID VDU1_SERVER_NAME \
|
||||
-c physical_resource_id -c resource_name -c resource_status -c resource_type
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack stack resource show HEAT_STACK_ID VDU1_VOLUME_NAME \
|
||||
-c physical_resource_id -c resource_name -c resource_status -c resource_type
|
||||
|
||||
|
||||
Result:
|
||||
@@ -744,17 +750,75 @@ Result:
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| physical_resource_id | 6b89f9c9-ebd8-49ca-8e2c-c01838daeb95 |
|
||||
| physical_resource_id | ed781426-c59d-4c32-8bc3-e26144167220 |
|
||||
| resource_name | VDU1 |
|
||||
| resource_status | CREATE_COMPLETE |
|
||||
| resource_type | OS::Nova::Server |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| physical_resource_id | 2d4715e6-1e0e-449e-91b5-a6c162adbb39 |
|
||||
| resource_name | VDU1-VirtualStorage |
|
||||
| resource_status | CREATE_COMPLETE |
|
||||
| resource_type | OS::Cinder::Volume |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
Heal Specified with VNFC Instances can control whether related Cinder
|
||||
block storage is recreated for the healed VNFC(s).
|
||||
|
||||
This is done via an optional key inside ``additionalParams`` in the Heal
|
||||
request. The key name is operator-configurable, and when the request
|
||||
omits the key, a configurable default is applied [Conf-Options_].
|
||||
|
||||
* **Request key (configurable)**:
|
||||
the key to read from ``additionalParams`` is configured by
|
||||
``[vnf_lcm] heal_include_block_storage_key`` on the VNFM.
|
||||
By default it is ``tacker_extension_heal_include_block_storage``.
|
||||
* **Default behavior (configurable)**:
|
||||
when the request does not include the key, the behavior is decided by
|
||||
``[vnf_lcm] heal_vnfc_block_storage``.
|
||||
|
||||
.. warning::
|
||||
|
||||
The default behavior (when the key is not provided) and the key name
|
||||
itself depend on ``tacker.conf``. If you are unsure, contact your
|
||||
environment administrator.
|
||||
|
||||
In the examples below, the parameter name is shown as
|
||||
``tacker_extension_heal_include_block_storage``.
|
||||
|
||||
*Example: recreate storage (boolean true)*
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"additionalParams": {
|
||||
"tacker_extension_heal_include_block_storage": true
|
||||
}
|
||||
}
|
||||
|
||||
*Example: do not recreate storage (boolean false)*
|
||||
|
||||
.. code-block:: json
|
||||
|
||||
{
|
||||
"additionalParams": {
|
||||
"tacker_extension_heal_include_block_storage": false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Healing execution of VDU1:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack vnflcm heal VNF_INSTANCE_ID --vnfc-instance VNFC_INSTANCE_ID
|
||||
|
||||
$ openstack vnflcm heal VNF_INSTANCE_ID --vnfc-instance VNFC_INSTANCE_ID \
|
||||
--additional-param-file heal-params.json \
|
||||
|
||||
Result:
|
||||
|
||||
@@ -772,8 +836,14 @@ VDU1 information after healing:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack stack resource show HEAT_STACK_ID \
|
||||
VDU_NAME -c physical_resource_id -c resource_status
|
||||
$ openstack stack resource show HEAT_STACK_ID VDU1_SERVER_NAME \
|
||||
-c physical_resource_id -c resource_name -c resource_status -c resource_type
|
||||
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ openstack stack resource show HEAT_STACK_ID VDU1_VOLUME_NAME \
|
||||
-c physical_resource_id -c resource_name -c resource_status -c resource_type
|
||||
|
||||
|
||||
Result:
|
||||
@@ -783,11 +853,39 @@ Result:
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| physical_resource_id | 6b89f9c9-ebd8-49ca-8e2c-c01838daeb95 |
|
||||
| physical_resource_id | ed781426-c59d-4c32-8bc3-e26144167220 |
|
||||
| resource_name | VDU1 |
|
||||
| resource_status | UPDATE_COMPLETE |
|
||||
| resource_type | OS::Nova::Server |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
|
||||
*recreate storage*
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| physical_resource_id | 2d4715e6-1e0e-449e-91b5-a6c162adbb39 |
|
||||
| resource_name | VDU1-VirtualStorage |
|
||||
| resource_status | UPDATE_COMPLETE |
|
||||
| resource_type | OS::Cinder::Volume |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
*do not recreate storage*
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
+----------------------+--------------------------------------+
|
||||
| Field | Value |
|
||||
+----------------------+--------------------------------------+
|
||||
| physical_resource_id | 2d4715e6-1e0e-449e-91b5-a6c162adbb39 |
|
||||
| resource_name | VDU1-VirtualStorage |
|
||||
| resource_status | CREATE_COMPLETE |
|
||||
| resource_type | OS::Cinder::Volume |
|
||||
+----------------------+--------------------------------------+
|
||||
|
||||
.. note::
|
||||
|
||||
'physical_resource_id' has not changed from the ID before healing.
|
||||
@@ -797,3 +895,4 @@ Result:
|
||||
.. _NFV-SOL002 v2.6.1 : https://www.etsi.org/deliver/etsi_gs/NFV-SOL/001_099/002/02.06.01_60/gs_NFV-SOL002v020601p.pdf
|
||||
.. _Heat API reference : https://docs.openstack.org/api-ref/orchestration/v1/index.html
|
||||
.. _Heat CLI reference : https://docs.openstack.org/python-openstackclient/latest/cli/plugin-commands/heat.html
|
||||
.. _Conf-Options : https://docs.openstack.org/tacker/latest/configuration/config.html#vnf-lcm
|
||||
|
||||
@@ -20,6 +20,33 @@ OPTS = [
|
||||
'endpoint_url',
|
||||
default='http://localhost:9890/',
|
||||
help="endpoint_url"),
|
||||
cfg.StrOpt(
|
||||
'heal_include_block_storage_key',
|
||||
default='tacker_extension_heal_include_block_storage',
|
||||
help="""
|
||||
Name of the boolean key in ``additionalParams`` that toggles including
|
||||
block storage for a v1 heal request.
|
||||
|
||||
Example payload::
|
||||
|
||||
{
|
||||
"additionalParams": {
|
||||
"tacker_extension_heal_include_block_storage": true
|
||||
}
|
||||
}
|
||||
"""),
|
||||
cfg.BoolOpt(
|
||||
'heal_vnfc_block_storage',
|
||||
default=True,
|
||||
help="""
|
||||
Default behaviour when a v1 heal request omits the per-request key.
|
||||
If ``additionalParams[<heal_include_block_storage_key>]`` is present,
|
||||
that value takes precedence over this option.
|
||||
|
||||
In ``tacker.conf`` (``[vnf_lcm]`` section)::
|
||||
|
||||
heal_vnfc_block_storage = false
|
||||
"""),
|
||||
cfg.IntOpt(
|
||||
'subscription_num',
|
||||
default=100,
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
# Copyright (C) 2025 KDDI
|
||||
# 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_config import cfg
|
||||
from oslo_config import fixture as config
|
||||
from tacker.conf import vnf_lcm as vnf_lcm_conf
|
||||
from tacker.objects import fields
|
||||
from tacker.vnfm.infra_drivers.openstack import openstack as os_drv
|
||||
from types import SimpleNamespace
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
|
||||
class TestHealStorageToggleMin(unittest.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.conf_fix = config.Config(cfg.CONF)
|
||||
self.conf_fix.setUp()
|
||||
vnf_lcm_conf.register_opts(cfg.CONF)
|
||||
|
||||
self.driver = os_drv.OpenStack()
|
||||
self.recorded = []
|
||||
|
||||
hc_patch = mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.hc.HeatClient')
|
||||
self.addCleanup(hc_patch.stop)
|
||||
FakeHC = hc_patch.start()
|
||||
hc = FakeHC.return_value
|
||||
hc.get.return_value = SimpleNamespace(stack_status='CREATE_COMPLETE')
|
||||
|
||||
def rec_mark_unhealthy(*, stack_id, resource_name, **_):
|
||||
self.recorded.append(resource_name)
|
||||
|
||||
hc.resource_mark_unhealthy.side_effect = rec_mark_unhealthy
|
||||
hc.update.return_value = None
|
||||
|
||||
mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.vnflcm_utils.'
|
||||
'get_base_nest_hot_dict',
|
||||
return_value=({}, {})
|
||||
).start()
|
||||
mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.vnflcm_utils.'
|
||||
'get_stack_param',
|
||||
return_value={}
|
||||
).start()
|
||||
mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.manager.'
|
||||
'TackerManager.get_service_plugins',
|
||||
return_value={'VNFM': SimpleNamespace(
|
||||
get_vnf=lambda *a, **k: {'vnfd_id': 'vnfd-1'})}
|
||||
).start()
|
||||
mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.objects.'
|
||||
'VnfLcmOpOcc.get_by_vnf_instance_id',
|
||||
return_value=SimpleNamespace(
|
||||
error_point=fields.ErrorPoint.PRE_VIM_CONTROL)
|
||||
).start()
|
||||
|
||||
mock.patch.object(
|
||||
os_drv.OpenStack, '_get_stack_resources',
|
||||
return_value={
|
||||
'stack-123': {
|
||||
'VDU1': {'physical_resource_id': 'srv-001'},
|
||||
'VDU1-volume': {'physical_resource_id': 'vol-001'},
|
||||
'child_stack': False
|
||||
}
|
||||
}
|
||||
).start()
|
||||
|
||||
mock.patch(
|
||||
'tacker.vnfm.infra_drivers.openstack.openstack.vnflcm_utils.'
|
||||
'get_vnfd_dict',
|
||||
return_value={
|
||||
'flavours': {'flv': {}},
|
||||
'topology_template': {}
|
||||
}
|
||||
).start()
|
||||
|
||||
vnfc = SimpleNamespace(
|
||||
id='vnfc-1',
|
||||
vdu_id='VDU1',
|
||||
compute_resource=SimpleNamespace(resource_id='srv-001'),
|
||||
storage_resource_ids=['s-1'],
|
||||
)
|
||||
vstorage = SimpleNamespace(
|
||||
id='s-1',
|
||||
virtual_storage_desc_id='VDU1-volume',
|
||||
storage_resource=SimpleNamespace(resource_id='vol-001')
|
||||
)
|
||||
self.vnf_instance = SimpleNamespace(
|
||||
id='vnf-1',
|
||||
vnfd_id='vnfd-1',
|
||||
instantiated_vnf_info=SimpleNamespace(
|
||||
instance_id='stack-123',
|
||||
flavour_id='flv',
|
||||
vnfc_resource_info=[vnfc],
|
||||
virtual_storage_resource_info=[vstorage],
|
||||
additional_params=None
|
||||
)
|
||||
)
|
||||
from tacker.objects import vim_connection
|
||||
self.vim_info = vim_connection.VimConnectionInfo(
|
||||
vim_id='vim-1',
|
||||
vim_type='openstack',
|
||||
interface_info={},
|
||||
access_info={'region': 'RegionOne'},
|
||||
)
|
||||
|
||||
def _heal(self, *, default=None, req_val=None, custom_key=None):
|
||||
if default is not None:
|
||||
self.conf_fix.config(group='vnf_lcm',
|
||||
heal_vnfc_block_storage=bool(default))
|
||||
if custom_key is not None:
|
||||
self.conf_fix.config(group='vnf_lcm',
|
||||
heal_include_block_storage_key=custom_key)
|
||||
key = custom_key or cfg.CONF.vnf_lcm.heal_include_block_storage_key
|
||||
add_params = {} if req_val is None else {key: req_val}
|
||||
heal_req = SimpleNamespace(vnfc_instance_id=['vnfc-1'],
|
||||
cause=None,
|
||||
additional_params=add_params)
|
||||
self.recorded.clear()
|
||||
self.driver.heal_vnf(None, self.vnf_instance, self.vim_info, heal_req)
|
||||
return set(self.recorded)
|
||||
|
||||
def test_default_true_when_key_omitted(self):
|
||||
marked = self._heal(default=True, req_val=None)
|
||||
self.assertEqual({'VDU1', 'VDU1-volume'}, marked)
|
||||
|
||||
def test_default_false_when_key_omitted(self):
|
||||
marked = self._heal(default=False, req_val=None)
|
||||
self.assertEqual({'VDU1'}, marked)
|
||||
|
||||
def test_request_overrides_true(self):
|
||||
marked = self._heal(default=False, req_val=True)
|
||||
self.assertEqual({'VDU1', 'VDU1-volume'}, marked)
|
||||
|
||||
def test_request_overrides_false(self):
|
||||
marked = self._heal(default=True, req_val=False)
|
||||
self.assertEqual({'VDU1'}, marked)
|
||||
|
||||
def test_custom_key(self):
|
||||
marked = self._heal(
|
||||
default=False, req_val=True, custom_key='recreate_block')
|
||||
self.assertEqual({'VDU1', 'VDU1-volume'}, marked)
|
||||
@@ -54,7 +54,6 @@ from tacker.vnfm.lcm_user_data.constants import USER_DATA_TIMEOUT
|
||||
from tacker.vnfm.lcm_user_data import utils as user_data_utils
|
||||
from toscaparser import tosca_template
|
||||
|
||||
|
||||
eventlet.monkey_patch(time=True)
|
||||
|
||||
SCALING_GROUP_RESOURCE = "OS::Heat::AutoScalingGroup"
|
||||
@@ -1495,6 +1494,9 @@ class OpenStack(abstract_driver.VnfAbstractDriver,
|
||||
vnfc_resource.compute_resource.resource_id}
|
||||
vdu_resources.append(resource_details)
|
||||
|
||||
if heal_vnf_request.additional_params.get(
|
||||
CONF.vnf_lcm.heal_include_block_storage_key,
|
||||
CONF.vnf_lcm.heal_vnfc_block_storage):
|
||||
# Get storage resources
|
||||
for resource_name, resource_id in \
|
||||
_get_storage_resources(vnfc_resource):
|
||||
|
||||
Reference in New Issue
Block a user