Add release version parameter validation

Introduce validate_release_version_supported, a dcmanager util
method to check whether a release version is supported by the
current active version. This is necessary as many dcmanager
operations/commands now allow the user to specify a release
version (e.g., subcloud add, deploy, restore). That is, this
check is intended to validate the release version parameter
provided to the various operations that support it.
This implementation is based on sysinv-conductor's
import_load check, which parses the upgrades metadata.xml
file to check for the supported versions.

This validation check will be iteratively added to all the
API endpoints that consume a release parameter. Currently,
it is only used for the subcloud deploy upload endpoint.

Test Plan:
Test the release parameter using deploy upload -
"subcloud-deploy upload ... --release <release>":
    1. PASS: Verify that the current active version is
       accepted as a valid parameter.
    2. PASS: Verify that all the supported upgrade versions
       in /usr/rootdirs/opt/upgrades/metadata.xml are accepted
       as a valid release parameter.
    3. PASS: Verify that any upgrade version that's not included
       in metadata.xml is rejected with an error
       "<release> is not a supported release version". The only
       exception to this is the current active version, it must
       always be valid.
    4. PASS: Delete all the "supported_upgrades" elements in
       metadata.xml and verify the error
       "Unable to validate the release version" is printed.
    5. PASS: Delete metadata.xml and verify that
       "Unable to validate the release version" error is printed.
    6. PASS: Verify that the current active version is valid
       regardless of metadata.xml file and its contents.
    7. PASS: Exclude the release parameter and verify that the
       deploy upload is successful (with an upload completed
       for the current active version).

Closes-Bug: 2031557

Change-Id: Id8b5670b8cfcdf86d36bbf9180abe064c4279d1a
Signed-off-by: Salman Rana <salman.rana@windriver.com>
This commit is contained in:
Salman Rana 2023-08-17 17:19:46 -04:00
parent 6891432a6e
commit 280f22364c
4 changed files with 79 additions and 4 deletions

View File

@ -32,6 +32,7 @@ from dcmanager.api.controllers import restcomm
from dcmanager.api.policies import subcloud_deploy as subcloud_deploy_policy
from dcmanager.api import policy
from dcmanager.common import consts
from dcmanager.common import exceptions
from dcmanager.common.i18n import _
from dcmanager.common import utils
@ -107,7 +108,15 @@ class SubcloudDeployController(object):
software_version = tsc.SW_VERSION
if request.POST.get('release'):
software_version = request.POST.get('release')
try:
utils.validate_release_version_supported(request.POST.get('release'))
software_version = request.POST.get('release')
except exceptions.ValidateFail as e:
pecan.abort(httpclient.BAD_REQUEST,
_("Error: invalid release version parameter. %s" % e))
except Exception:
pecan.abort(httpclient.INTERNAL_SERVER_ERROR,
_('Error: unable to validate the release version.'))
deploy_dicts['software_version'] = software_version
dir_path = os.path.join(dccommon_consts.DEPLOY_DIR, software_version)

View File

@ -413,3 +413,5 @@ HELM_CHART_POSTFIX = 'deployment-manager'
ALTERNATE_DEPLOY_PLAYBOOK_DIR = ALTERNATE_DEPLOY_FILES_DIR + '/playbooks'
DEPLOY_PLAYBOOK_POSTFIX = 'deployment-manager.yaml'
SUPPORTED_UPGRADES_METADATA_FILE_PATH = '/usr/rootdirs/opt/upgrades/metadata.xml'

View File

@ -28,6 +28,7 @@ import six.moves
import string
import subprocess
import tsconfig.tsconfig as tsc
import xml.etree.ElementTree as ElementTree
import yaml
from keystoneauth1 import exceptions as keystone_exceptions
@ -1106,3 +1107,57 @@ def subcloud_is_secondary_state(deploy_state):
def create_subcloud_rehome_data_template():
"""Create a subcloud rehome data template"""
return {'saved_payload': {}}
def validate_release_version_supported(release_version_to_check):
"""Given a release version, check whether it's supported by the current active version.
:param release_version_to_check: version string to validate
returns True to indicate that the version is valid
raise ValidateFail for an invalid/unsupported release version
"""
current_version = tsc.SW_VERSION
if current_version == release_version_to_check:
return True
supported_versions = get_current_supported_upgrade_versions()
if release_version_to_check not in supported_versions:
msg = "%s is not a supported release version" % release_version_to_check
raise exceptions.ValidateFail(msg)
return True
def get_current_supported_upgrade_versions():
"""Parse the upgrades metadata file to build a list of supported versions.
returns a list of supported upgrade versions
raise InternalError exception for a missing/invalid metadata file
"""
supported_versions = []
try:
with open(consts.SUPPORTED_UPGRADES_METADATA_FILE_PATH) as file:
root = ElementTree.fromstring(file.read())
except Exception:
LOG.exception("Error reading the supported upgrades metadata file")
raise exceptions.InternalError()
supported_upgrades = root.find('supported_upgrades')
if not supported_upgrades:
LOG.error("Missing supported upgrades information")
raise exceptions.InternalError()
upgrades = supported_upgrades.findall("upgrade")
for upgrade in upgrades:
version = upgrade.findtext("version")
supported_versions.append(version.strip())
return supported_versions

View File

@ -48,6 +48,12 @@ FAKE_DEPLOY_FILES = {
FAKE_DEPLOY_CHART_PREFIX: FAKE_DEPLOY_CHART_FILE,
}
FAKE_UPGRADES_METADATA = '''
<build>\n<version>0.2</version>\n<supported_upgrades>
\n<upgrade>\n<version>%s</version>\n<required_patches>PATCH_0001</required_patches>
\n</upgrade>\n</supported_upgrades>\n</build>
''' % FAKE_SOFTWARE_VERSION
class TestSubcloudDeploy(testroot.DCManagerApiTest):
def setUp(self):
@ -65,9 +71,12 @@ class TestSubcloudDeploy(testroot.DCManagerApiTest):
fields.append((opt, webtest.Upload(fake_name, fake_content)))
mock_upload_files.return_value = True
params += fields
response = self.app.post(FAKE_URL,
headers=FAKE_HEADERS,
params=params)
with mock.patch('builtins.open', mock.mock_open(read_data=FAKE_UPGRADES_METADATA)):
response = self.app.post(FAKE_URL,
headers=FAKE_HEADERS,
params=params)
self.assertEqual(response.status_code, http_client.OK)
self.assertEqual(FAKE_SOFTWARE_VERSION, response.json['software_version'])