Update Horizon to support prestage orchestration

Support the operations of create/delete prestage orchestration strategy
on Horizon.

Test Plan:
PASS: Successfully create and apply prestage strategy with specified
      21.12/22.12 release.
PASS: Successfully create and apply prestage strategy with 22.12 release
      (the system controller active release) when the release field
      is absent.
PASS: Successfully abort or delete the prestage strategy.
PASS: Verify no broken functionalities of existing orchestration
      strategies.

Story: 2010611
Task: 48144

Change-Id: Ia560e6b9e8f3fa8f27e9864b204335cd0c6935f1
Signed-off-by: lzhu1 <li.zhu@windriver.com>
This commit is contained in:
Li Zhu 2023-05-29 10:15:03 -04:00
parent c4de9faa26
commit 42f6086bea
5 changed files with 108 additions and 6 deletions

View File

@ -111,6 +111,11 @@ HOST_BM_TYPE_IPMI = "ipmi"
HOST_BM_TYPE_REDFISH = "redfish"
HOST_BM_TYPE_DYNAMIC = "dynamic"
# Load states
ACTIVE_LOAD_STATE = 'active'
INACTIVE_LOAD_STATE = 'inactive'
IMPORTED_LOAD_STATE = 'imported'
LOG = logging.getLogger(__name__)
@ -2533,3 +2538,29 @@ class KubeVersion(base.APIResourceWrapper):
def kube_version_list(request):
kube_versions = cgtsclient(request).kube_version.list()
return [KubeVersion(n) for n in kube_versions]
class Load(base.APIResourceWrapper):
"""Wrapper for Load"""
_attrs = ['software_version', 'compatible_version', 'required_patches',
'state']
def __init__(self, apiresource):
super(Load, self).__init__(apiresource)
def load_list(request):
loads = cgtsclient(request).load.list()
return [Load(n) for n in loads]
def get_sw_versions_for_prestage(request):
valid_states = [
ACTIVE_LOAD_STATE,
IMPORTED_LOAD_STATE,
INACTIVE_LOAD_STATE
]
loads = load_list(request)
return [load.software_version for load in loads
if load.state in valid_states]

View File

@ -4,6 +4,7 @@
# SPDX-License-Identifier: Apache-2.0
#
import base64
import logging
from dcmanagerclient import exceptions as exc
@ -40,6 +41,8 @@ class ApplyCloudStrategyForm(forms.SelfHandlingForm):
class CreateCloudStrategyForm(forms.SelfHandlingForm):
FIELD_LABEL_RELEASE = _("Release")
FIELD_LABEL_SYSADMIN_PASSWORD = _("sysadmin password")
failure_url = 'horizon:dc_admin:dc_orchestration:index'
SUBCLOUD_APPLY_TYPES = (
@ -52,6 +55,7 @@ class CreateCloudStrategyForm(forms.SelfHandlingForm):
('upgrade', _("Upgrade")),
('kubernetes', _("Kubernetes")),
('firmware', _("Firmware")),
('prestage', _("Prestage")),
)
SUBCLOUD_TYPES = (
@ -186,7 +190,7 @@ class CreateCloudStrategyForm(forms.SelfHandlingForm):
initial=False,
required=False,
help_text=_('Force Kube upgrade to a subcloud '
'which is in-sync with system controller'),
'which is in-sync with System Controller'),
widget=forms.CheckboxInput(
attrs={
'class': 'switched',
@ -210,6 +214,37 @@ class CreateCloudStrategyForm(forms.SelfHandlingForm):
)
)
release = forms.ChoiceField(
label=FIELD_LABEL_RELEASE,
required=False,
help_text=_("Select a version for the strategy to apply. \
Otherwise, the System Controller active version \
will be used."),
widget=forms.Select(
attrs={
'class': 'switched',
'data-switch-on': 'strategy_types',
'data-strategy_types-prestage': FIELD_LABEL_RELEASE,
'data-slug': 'release'
}
)
)
sysadmin_password = forms.CharField(
label=FIELD_LABEL_SYSADMIN_PASSWORD,
required=False,
widget=forms.PasswordInput(
attrs={
'autocomplete': 'off',
'class': 'switched',
'data-switch-on': 'strategy_types',
'data-strategy_types-prestage':
FIELD_LABEL_SYSADMIN_PASSWORD,
'data-required-when-shown': 'true'
}
)
)
def __init__(self, request, *args, **kwargs):
super(CreateCloudStrategyForm, self).__init__(request, *args,
**kwargs)
@ -232,6 +267,27 @@ class CreateCloudStrategyForm(forms.SelfHandlingForm):
kube_versions.extend(version)
self.fields['to_version'].choices = kube_versions
release_list = []
sw_versions = api.sysinv.get_sw_versions_for_prestage(self.request)
for version in sw_versions:
release_list.extend([(version, version)])
empty_release = [('', '--')]
release_list[:0] = empty_release
self.fields['release'].choices = release_list
def clean(self):
cleaned_data = super(CreateCloudStrategyForm, self).clean()
if cleaned_data['type'] == 'prestage':
if (('sysadmin_password' not in cleaned_data) or
(not cleaned_data['sysadmin_password'])):
raise forms.ValidationError(
{'sysadmin_password':
forms.ValidationError('sysadmin password is required')})
else:
cleaned_data.pop('release', None)
cleaned_data.pop('sysadmin_password', None)
return cleaned_data
def handle(self, request, data):
try:
# convert keys to use dashes
@ -268,6 +324,13 @@ class CreateCloudStrategyForm(forms.SelfHandlingForm):
else:
del data['upload-only']
if data['type'] == 'prestage':
if data['release'] == '':
data.pop('release', None)
data['sysadmin_password'] = base64.b64encode(
data['sysadmin-password'].encode("utf-8")).decode("utf-8")
data.pop('sysadmin-password', None)
response = api.dc_manager.strategy_create(request, data)
if not response:
messages.error(request, "Strategy creation failed")

View File

@ -17,9 +17,14 @@
<dd>{{ strategy.max_parallel_subclouds }}</dd>
<dt>{% trans "Stop On Failure" %}</dt>
<dd>{{ strategy.stop_on_failure }}</dd>
{% if strategy.strategy_type == 'patch' and strategy.extra_args %}
<dt>{% trans "Upload Only" %}</dt>
<dd>{{ strategy.extra_args|get_value:"upload-only" }}</dd>
{% if strategy.extra_args %}
{% if strategy.strategy_type == 'patch' %}
<dt>{% trans "Upload Only" %}</dt>
<dd>{{ strategy.extra_args|get_value:"upload-only" }}</dd>
{% elif strategy.strategy_type == 'prestage' %}
<dt>{% trans "Prestage Software Version" %}</dt>
<dd>{{ strategy.extra_args|get_value:"prestage-software-version" }}</dd>
{% endif %}
{% endif %}
<dt>{% trans "State" %}</dt>
<dd>{{ strategy.state }}</dd>

View File

@ -28,6 +28,9 @@
<p>
{% trans "<b>Firmware:</b> specify how the firmware should be updated." %}
</p>
<p>
{% trans "<b>Prestage:</b> specify how the software should be prestaged." %}
</p>
</div>
{% endblock %}

View File

@ -1,5 +1,5 @@
#
# Copyright (c) 2019-2021 Wind River Systems, Inc.
# Copyright (c) 2019-2023 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
@ -99,7 +99,7 @@ OPERATION_LOG_ENABLED = True
OPERATION_LOG_OPTIONS = {
'mask_fields': ['password', 'bm_password', 'bm_confirm_password',
'current_password', 'confirm_password', 'new_password',
'fake_password'],
'fake_password', 'sysadmin_password'],
'ignore_urls': [],
'target_methods': ['POST', 'PUT', 'DELETE'],
'format': ("[%(project_name)s %(project_id)s] [%(user_name)s %(user_id)s]"