Merge "Fix Terraform infra-driver instantiate rollback"
This commit is contained in:
@@ -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'''
|
||||
|
||||
+17
-16
@@ -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')
|
||||
|
||||
Reference in New Issue
Block a user