Add 'subcloud deploy delete' command to dcmanager

This commit adds the subcloud deploy delete command to dcmanager.
It is used to delete deploy files under /opt/platform/deploy.

Test Cases:

1) PASS: dcmanager subcloud deploy delete with no release
2) PASS: dcmanager subcloud deploy delete --release 23.09
3) PASS: dcmanager subcloud deploy delete --release 22.12
4) PASS: dcmanager subcloud deploy delete --release 21.12
         fails using the load 23.09 we support n-1.
5) PASS: dcmanager subcloud deploy delete --prestage-image
6) PASS: dcmanager subcloud deploy delete --deployment-files
7) PASS: dcmanager subcloud deploy delete fails when
	 deploy files doesn't exist already.

Story: 2010718
Task: 49190

Change-Id: I94d629009b185f67e3da45d010a6492b9b6d6f17
Signed-off-by: Swapna Gorre <swapna.gorre@windriver.com>
This commit is contained in:
Swapna Gorre 2023-11-30 09:13:24 -05:00
parent f9a86d7f8c
commit eeb70d47c2
6 changed files with 249 additions and 1 deletions

View File

@ -1791,6 +1791,37 @@ Response Example
:language: json
************************************
Delete Subcloud Deploy Files
************************************
.. rest_method:: DELETE /v1.0/subcloud-deploy
**Normal response codes**
200
**Error response codes**
badRequest (400), unauthorized (401), forbidden (403), notFound (404),
HTTPUnprocessableEntity (422), internalServerError (500),
serviceUnavailable (503)
**Request parameters**
.. rest_parameters:: parameters.yaml
- release: release_uri
- deployment_files: delete_subcloud_deployment_files
- prestage_images: delete_subcloud_deploy_prestage_images
Request Example
----------------
.. literalinclude:: samples/subcloud-deploy/subcloud-deploy-delete-request.json
:language: json
----------------------
Phased Subcloud Deploy
----------------------

View File

@ -233,6 +233,20 @@ default_instance_action:
in: body
required: true
type: string
delete_subcloud_deploy_prestage_images:
description: |
The flag to indicate the deployment manager prestage images
file to be deleted.
in: body
required: false
type: boolean
delete_subcloud_deployment_files:
description: |
The flag to indicate the deploy playbook, deploy overrides,
deploy chart files to be deleted.
in: body
required: false
type: boolean
deploy_config:
description: |
The content of a file containing the resource definitions describing

View File

@ -0,0 +1,5 @@
{
"release": 23.09,
"prestage_images": false,
"deployment_files": false
}

View File

@ -149,3 +149,44 @@ class SubcloudDeployController(object):
filename = filename.replace(prefix, '', 1)
deploy_dicts.update({f: filename})
return dict(subcloud_deploy=deploy_dicts)
@index.when(method='DELETE', template='json')
def delete(self, release=None):
"""Delete the subcloud deploy files.
:param release: release version
"""
policy.authorize(subcloud_deploy_policy.POLICY_ROOT % "delete", {},
restcomm.extract_credentials_for_policy())
is_prestage_images = request.params.get('prestage_images', '').lower() == 'true'
is_deployment_files = request.params.get('deployment_files', '').lower() == 'true'
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, utils.get_sw_version(release))
if not os.path.isdir(dir_path):
pecan.abort(httpclient.NOT_FOUND,
_("Directory not found: %s" % dir_path))
try:
file_options = []
if is_prestage_images:
file_options.append(consts.DEPLOY_PRESTAGE)
if is_deployment_files:
file_options.extend([consts.DEPLOY_OVERRIDES, consts.DEPLOY_CHART,
consts.DEPLOY_PLAYBOOK])
if not (is_deployment_files or is_prestage_images):
file_options.extend(consts.DEPLOY_COMMON_FILE_OPTIONS)
for file_option in file_options:
prefix = file_option + '_'
file_name = utils.get_filename_by_prefix(dir_path, prefix)
if file_name:
os.remove(os.path.join(dir_path, file_name))
else:
LOG.warning('%s file not present' % file_option)
except Exception as e:
pecan.abort(httpclient.INTERNAL_SERVER_ERROR,
_("Failed to delete file: %s" % e))
return None

View File

@ -36,6 +36,21 @@ subcloud_deploy_rules = [
'path': '/v1.0/subcloud-deploy/{release}'
}
]
),
policy.DocumentedRuleDefault(
name=POLICY_ROOT % 'delete',
check_str='rule:' + base.ADMIN_IN_SYSTEM_PROJECTS,
description="Delete subcloud deploy files.",
operations=[
{
'method': 'DELETE',
'path': '/v1.0/subcloud-deploy'
},
{
'method': 'DELETE',
'path': '/v1.0/subcloud-deploy/{release}'
}
]
)
]

View File

@ -14,8 +14,10 @@
# under the License.
#
import os
from os import path as os_path
import mock
import six
from six.moves import http_client
import webtest
@ -30,7 +32,7 @@ from dcmanager.tests import utils
from tsconfig.tsconfig import SW_VERSION
FAKE_SOFTWARE_VERSION = '21.12'
FAKE_SOFTWARE_VERSION = '22.12'
FAKE_TENANT = utils.UUID1
FAKE_ID = '1'
FAKE_URL = '/v1.0/subcloud-deploy'
@ -40,6 +42,7 @@ FAKE_HEADERS = {'X-Tenant-Id': FAKE_TENANT, 'X_ROLE': 'admin,member,reader',
FAKE_DEPLOY_PLAYBOOK_PREFIX = consts.DEPLOY_PLAYBOOK + '_'
FAKE_DEPLOY_OVERRIDES_PREFIX = consts.DEPLOY_OVERRIDES + '_'
FAKE_DEPLOY_CHART_PREFIX = consts.DEPLOY_CHART + '_'
FAKE_PRESTAGE_IMAGES_PREFIX = consts.DEPLOY_PRESTAGE + '_'
FAKE_DEPLOY_PLAYBOOK_FILE = 'deployment-manager.yaml'
FAKE_DEPLOY_OVERRIDES_FILE = 'deployment-manager-overrides-subcloud.yaml'
FAKE_DEPLOY_CHART_FILE = 'deployment-manager.tgz'
@ -48,6 +51,21 @@ FAKE_DEPLOY_FILES = {
FAKE_DEPLOY_OVERRIDES_PREFIX: FAKE_DEPLOY_OVERRIDES_FILE,
FAKE_DEPLOY_CHART_PREFIX: FAKE_DEPLOY_CHART_FILE,
}
FAKE_DEPLOY_DELETE_FILES = {
FAKE_DEPLOY_PLAYBOOK_PREFIX: '/opt/platform/deploy/22.12/deployment-manager.yaml',
FAKE_DEPLOY_OVERRIDES_PREFIX:
'/opt/platform/deploy/22.12/deployment-manager-overrides-subcloud.yaml',
FAKE_DEPLOY_CHART_PREFIX: '/opt/platform/deploy/22.12/deployment-manager.tgz',
FAKE_PRESTAGE_IMAGES_PREFIX: '/opt/platform/deploy/22.12/prestage_images.yml'
}
def get_filename_by_prefix_side_effect(dir_path, prefix):
filename = FAKE_DEPLOY_FILES.get(prefix)
if filename:
return prefix + FAKE_DEPLOY_FILES.get(prefix)
else:
return None
class TestSubcloudDeploy(testroot.DCManagerApiTest):
@ -269,3 +287,127 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
f'{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1/install_values.yml')
self.assertEqual(deploy_config,
f'{dccommon_consts.ANSIBLE_OVERRIDES_PATH}/subcloud1_deploy_config.yml')
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
def test_subcloud_deploy_delete_directory_not_found(self,
mock_get_sw_version,
mock_path_isdir):
mock_get_sw_version.return_value = '21.12'
url = FAKE_URL + '?prestage_images=' + \
str(False) + '&deployment_files=' + str(False)
mock_path_isdir.side_effect = lambda x: True \
if x == '/opt/platform/deploy/22.12' else False
six.assertRaisesRegex(self, webtest.app.AppError, "404 *",
self.app.delete, url,
headers=FAKE_HEADERS)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
def test_subcloud_deploy_delete_internal_server_error(self,
mock_get_sw_version,
mock_path_isdir):
mock_get_sw_version.return_value = '22.12'
mock_path_isdir.side_effect = lambda x: True \
if x == '/opt/platform/deploy/22.12' else False
six.assertRaisesRegex(self, webtest.app.AppError, "500 *",
self.app.delete, FAKE_URL,
headers=FAKE_HEADERS)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
@mock.patch.object(dutils, 'get_filename_by_prefix')
@mock.patch.object(os, 'remove')
def test_subcloud_deploy_delete_with_release(self, mock_os_remove,
mock_get_filename_by_prefix,
mock_get_sw_version,
mock_path_isdir):
mock_os_remove.return_value = None
mock_get_sw_version.return_value = '22.12'
mock_get_filename_by_prefix.side_effect = \
get_filename_by_prefix_side_effect
mock_path_isdir.return_value = True
url = FAKE_URL + '/' + FAKE_SOFTWARE_VERSION + \
'?prestage_images=' + str(False) + '&deployment_files=' + str(False)
response = self.app.delete(url, headers=FAKE_HEADERS)
self.assertEqual(response.status_code, http_client.OK)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
@mock.patch.object(dutils, 'get_filename_by_prefix')
@mock.patch.object(os, 'remove')
def test_subcloud_deploy_delete_without_release(self, mock_os_remove,
mock_get_filename_by_prefix,
mock_get_sw_version,
mock_path_isdir):
mock_os_remove.return_value = None
mock_get_sw_version.return_value = '22.12'
url = FAKE_URL + '?prestage_images=' + \
str(True) + '&deployment_files=' + str(True)
mock_get_filename_by_prefix.side_effect = \
get_filename_by_prefix_side_effect
mock_path_isdir.return_value = True
response = self.app.delete(url, headers=FAKE_HEADERS)
self.assertEqual(response.status_code, http_client.OK)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
@mock.patch.object(dutils, 'get_filename_by_prefix')
@mock.patch.object(os, 'remove')
def test_subcloud_deploy_delete_deployment_files(self, mock_os_remove,
mock_get_filename_by_prefix,
mock_get_sw_version,
mock_path_isdir):
mock_os_remove.return_value = None
mock_get_sw_version.return_value = '22.12'
url = FAKE_URL + '?prestage_images=' + \
str(False) + '&deployment_files=' + str(True)
mock_get_filename_by_prefix.side_effect = \
get_filename_by_prefix_side_effect
mock_path_isdir.side_effect = lambda x: True \
if x == '/opt/platform/deploy/22.12' else False
response = self.app.delete(url, headers=FAKE_HEADERS)
self.assertEqual(response.status_code, http_client.OK)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
@mock.patch.object(dutils, 'get_filename_by_prefix')
@mock.patch.object(os, 'remove')
def test_subcloud_deploy_delete_prestage_images(self, mock_os_remove,
mock_get_filename_by_prefix,
mock_get_sw_version,
mock_path_isdir):
mock_os_remove.return_value = None
mock_get_sw_version.return_value = '22.12'
url = FAKE_URL + '?prestage_images=' + \
str(True) + '&deployment_files=' + str(False)
mock_get_filename_by_prefix.side_effect = \
get_filename_by_prefix_side_effect
mock_path_isdir.side_effect = lambda x: True \
if x == '/opt/platform/deploy/22.12' else False
response = self.app.delete(url, headers=FAKE_HEADERS)
self.assertEqual(response.status_code, http_client.OK)
@mock.patch.object(os_path, 'isdir')
@mock.patch.object(dutils, 'get_sw_version')
@mock.patch.object(dutils, 'get_filename_by_prefix')
@mock.patch.object(os, 'remove')
def test_subcloud_deploy_delete_with_both_parameters(self, mock_os_remove,
mock_get_filename_by_prefix,
mock_get_sw_version,
mock_path_isdir):
mock_os_remove.return_value = None
mock_get_sw_version.return_value = '22.12'
url = FAKE_URL + '?prestage_images=' + \
str(True) + '&deployment_files=' + str(True)
mock_get_filename_by_prefix.side_effect = \
get_filename_by_prefix_side_effect
mock_path_isdir.side_effect = lambda x: True \
if x == '/opt/platform/deploy/22.12' else False
response = self.app.delete(url, headers=FAKE_HEADERS)
self.assertEqual(response.status_code, http_client.OK)