Merge "Fix Terraform infra-driver instantiate rollback"

This commit is contained in:
Zuul
2024-01-22 15:49:00 +00:00
committed by Gerrit Code Review
4 changed files with 134 additions and 25 deletions
@@ -99,7 +99,24 @@ class Terraform():
def instantiate_rollback(self, req, inst, grant_req, grant, vnfd):
'''Calls terminate'''
self.terminate(req, inst, grant_req, grant, vnfd)
vim_conn_info = inst_utils.select_vim_info(inst.vimConnectionInfo)
tf_dir_path = req.additionalParams.get('tf_dir_path')
tf_var_path = req.additionalParams.get('tf_var_path')
working_dir = self._get_tf_vnfpkg(
inst.id, grant_req.vnfdId, tf_dir_path)
# NOTE: Checks the terraform.tfstate file state to call
# the terminate function. The reason is as follows.
# - For fails terraform apply command, the size of
# the terraform.tfstate file is 0.
# - For fails before terraform apply command,
# terraform.tfstate file does not exist.
tfstate_file = f"{working_dir}/terraform.tfstate"
if os.path.exists(tfstate_file):
if os.path.getsize(tfstate_file) != 0:
self._terminate(vim_conn_info, working_dir, tf_var_path)
else:
shutil.rmtree(working_dir)
def change_vnfpkg(self, req, inst, grant_req, grant, vnfd):
'''Calls Terraform Apply and replicates new files'''
@@ -13,12 +13,21 @@
# License for the specific language governing permissions and limitations
# under the License.
import functools
import os
import pickle
import sys
class SampleScript(object):
class FailScript(object):
"""Define error method for each operation
For example:
def instantiate_start(self):
if os.path.exists('/tmp/instantiate_start')
raise Exception('test instantiate_start error')
"""
def __init__(self, req, inst, grant_req, grant, csar_dir):
self.req = req
@@ -27,17 +36,12 @@ class SampleScript(object):
self.grant = grant
self.csar_dir = csar_dir
def instantiate_start(self):
pass
def _fail(self, method):
if os.path.exists(f'/tmp/{method}'):
raise Exception(f'test {method} error')
def instantiate_end(self):
pass
def terminate_start(self):
pass
def terminate_end(self):
pass
def __getattr__(self, name):
return functools.partial(self._fail, name)
def main():
@@ -50,11 +54,8 @@ def main():
grant = script_dict['grant_response']
csar_dir = script_dict['tmp_csar_dir']
script = SampleScript(req, inst, grant_req, grant, csar_dir)
try:
getattr(script, operation)()
except AttributeError:
raise Exception("{} is not included in the script.".format(operation))
script = FailScript(req, inst, grant_req, grant, csar_dir)
getattr(script, operation)()
if __name__ == "__main__":
@@ -333,4 +333,55 @@ class VnfLcmTerraformTest(base_v2.BaseVnfLcmTerraformV2Test):
self.assertEqual(404, resp.status_code)
self.check_package_usage(self.basic_pkg, state='NOT_IN_USE')
# TODO(yasufum) consider to add a test for instantiate_rollback here.
def test_instantiate_rollback(self):
"""Test rollback operation for instantiation.
* About LCM operations:
This test includes the following operations.
- 1. Create VNF instance
- 2. Instantiate VNF => FAILED_TEMP
- 3. Show VNF instance
- 4. Rollback instantiate
- 5. Show VNF instance
- 6. Delete a VNF instance
"""
# 1. Create VNF instance
create_req = tf_paramgen.create_req_by_vnfd_id(self.basic_vnfd_id)
resp, body = self.create_vnf_instance(create_req)
self.assertEqual(201, resp.status_code)
self.check_resp_headers_in_create(resp)
inst_id = body['id']
# 2. Instantiate VNF => FAILED_TEMP
self.put_fail_file('instantiate_end')
instantiate_req = tf_paramgen.instantiate_req()
resp, body = self.instantiate_vnf_instance(inst_id, instantiate_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_failed_temp(lcmocc_id)
self.rm_fail_file('instantiate_end')
# 3. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.assertEqual('NOT_INSTANTIATED', body['instantiationState'])
# 4. Rollback instantiate
resp, body = self.rollback_lcmocc(lcmocc_id)
self.assertEqual(202, resp.status_code)
self.wait_lcmocc_rolled_back(lcmocc_id)
# 5. Show VNF instance
resp, body = self.show_vnf_instance(inst_id)
self.assertEqual(200, resp.status_code)
self.check_resp_headers_in_get(resp)
self.assertEqual('NOT_INSTANTIATED', body['instantiationState'])
# 6. Delete a VNF instance
resp, body = self.delete_vnf_instance(inst_id)
self.assertEqual(204, resp.status_code)
self.check_resp_headers_in_delete(resp)
@@ -166,8 +166,10 @@ class TestTerraform(base.BaseTestCase):
f"/var/lib/tacker/terraform/{inst.id}",
inst.instantiatedVnfInfo.metadata['tf_var_path'])
@mock.patch.object(terraform.Terraform, '_get_tf_vnfpkg')
@mock.patch.object(terraform.Terraform, '_terminate')
def test_instantiate_rollback(self, mock_instantiate_rollback):
def test_instantiate_rollback(self, mock_instantiate_rollback,
mock_working_dir):
'''Verifies instantiate_rollback is called once'''
req = objects.InstantiateVnfRequest.from_dict(_instantiate_req_example)
@@ -190,19 +192,57 @@ class TestTerraform(base.BaseTestCase):
)
grant_req = objects.GrantRequestV1(
operation=fields.LcmOperationType.INSTANTIATE
operation=fields.LcmOperationType.INSTANTIATE,
vnfdId=SAMPLE_VNFD_ID
)
grant = objects.GrantV1()
# Execute
self.driver.instantiate_rollback(req, inst, grant_req,
grant, self.vnfd_2)
# Create a temporary directory
with tempfile.TemporaryDirectory() as temp_dir:
# Create the tfstate content
provider = "provider[\"registry.terraform.io/hashicorp/aws\"]"
tfstate_content = {
"version": 4,
"terraform_version": "1.4.4",
"serial": 4,
"lineage": "5745b992-04a2-5811-2e02-19d64f6f4b44",
"outputs": {},
"resources": [
{
"mode": "managed",
"type": "aws_instance",
"name": "vdu1",
"provider": provider
},
{
"mode": "managed",
"type": "aws_subnet",
"name": "hoge-subnet01",
"provider": provider
}
]
}
# Set the desired return value for _get_tf_vnfpkg
mock_working_dir.return_value = f"{temp_dir}/{inst.id}"
os.mkdir(mock_working_dir())
# Write the tfstate content to a temporary file
tfstate_file_path = os.path.join(mock_working_dir(),
'terraform.tfstate')
with open(tfstate_file_path, "w") as tfstate_file:
json.dump(tfstate_content, tfstate_file)
# Execute
self.driver.instantiate_rollback(req, inst, grant_req,
grant, self.vnfd_2)
# Verify _terminate is called once
mock_instantiate_rollback.assert_called_once_with(
req.vimConnectionInfo["vim1"],
f"/var/lib/tacker/terraform/{inst.id}",
inst.instantiatedVnfInfo.metadata['tf_var_path'])
f"{temp_dir}/{inst.id}",
req.additionalParams.get('tf_var_path'))
@mock.patch.object(terraform.Terraform, '_get_tf_vnfpkg')
@mock.patch.object(terraform.Terraform, '_generate_provider_tf')