diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py index 3761cdf0..f5ca0528 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py @@ -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] diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/forms.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/forms.py index 3480021b..a1a103a1 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/forms.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/forms.py @@ -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") diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_cloud_strategy_orchestration.html b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_cloud_strategy_orchestration.html index 08aa24c1..29a6d7ce 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_cloud_strategy_orchestration.html +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_cloud_strategy_orchestration.html @@ -17,9 +17,14 @@
{{ strategy.max_parallel_subclouds }}
{% trans "Stop On Failure" %}
{{ strategy.stop_on_failure }}
- {% if strategy.strategy_type == 'patch' and strategy.extra_args %} -
{% trans "Upload Only" %}
-
{{ strategy.extra_args|get_value:"upload-only" }}
+ {% if strategy.extra_args %} + {% if strategy.strategy_type == 'patch' %} +
{% trans "Upload Only" %}
+
{{ strategy.extra_args|get_value:"upload-only" }}
+ {% elif strategy.strategy_type == 'prestage' %} +
{% trans "Prestage Software Version" %}
+
{{ strategy.extra_args|get_value:"prestage-software-version" }}
+ {% endif %} {% endif %}
{% trans "State" %}
{{ strategy.state }}
diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_create_cloud_strategy.html b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_create_cloud_strategy.html index 13e497e9..3431aeaa 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_create_cloud_strategy.html +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/dc_admin/dc_orchestration/templates/dc_orchestration/_create_cloud_strategy.html @@ -28,6 +28,9 @@

{% trans "Firmware: specify how the firmware should be updated." %}

+

+ {% trans "Prestage: specify how the software should be prestaged." %} +

{% endblock %} diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/local/local_settings.d/_30_stx_local_settings.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/local/local_settings.d/_30_stx_local_settings.py index cdca901e..47951e1c 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/local/local_settings.d/_30_stx_local_settings.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/local/local_settings.d/_30_stx_local_settings.py @@ -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]"