Merge "Distributed Cloud Patching Dashboard"
This commit is contained in:
commit
42b54d543e
|
@ -1,2 +1,2 @@
|
|||
SRC_DIR="starlingx-dashboard"
|
||||
TIS_PATCH_VER=12
|
||||
TIS_PATCH_VER=13
|
||||
|
|
|
@ -1,79 +1,163 @@
|
|||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copyright (c) 2017 Wind River Systems, Inc.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from dcmanagerclient.api.v1 import client
|
||||
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@memoized
|
||||
def dcmanagerclient(request):
|
||||
endpoint = base.url_for(request, 'dcmanager', 'adminURL')
|
||||
c = client.Client(project_id=request.user.project_id,
|
||||
user_id=request.user.id,
|
||||
auth_token=request.user.token.id,
|
||||
dcmanager_url=endpoint)
|
||||
return c
|
||||
|
||||
|
||||
class Summary(base.APIResourceWrapper):
|
||||
_attrs = ['name', 'critical', 'major', 'minor', 'warnings', 'status']
|
||||
|
||||
|
||||
def alarm_summary_list(request):
|
||||
summaries = dcmanagerclient(request).alarm_manager.list_alarms()
|
||||
return [Summary(summary) for summary in summaries]
|
||||
|
||||
|
||||
class Subcloud(base.APIResourceWrapper):
|
||||
_attrs = ['subcloud_id', 'name', 'description', 'location',
|
||||
'software_version', 'management_subnet', 'management_state',
|
||||
'availability_status', 'management_start_ip',
|
||||
'management_end_ip', 'management_gateway_ip',
|
||||
'systemcontroller_gateway_ip', 'created_at', 'updated_at',
|
||||
'sync_status', 'endpoint_sync_status', ]
|
||||
|
||||
|
||||
def subcloud_list(request):
|
||||
subclouds = dcmanagerclient(request).subcloud_manager.list_subclouds()
|
||||
return [Subcloud(subcloud) for subcloud in subclouds]
|
||||
|
||||
|
||||
def subcloud_create(request, data):
|
||||
return dcmanagerclient(request).subcloud_manager.add_subcloud(
|
||||
**data.get('data'))
|
||||
|
||||
|
||||
def subcloud_update(request, subcloud_id, changes):
|
||||
response = dcmanagerclient(request).subcloud_manager.update_subcloud(
|
||||
subcloud_id, **changes.get('updated'))
|
||||
# Updating returns a list of subclouds for some reason
|
||||
return [Subcloud(subcloud) for subcloud in response]
|
||||
|
||||
|
||||
def subcloud_delete(request, subcloud_id):
|
||||
return dcmanagerclient(request).subcloud_manager.delete_subcloud(
|
||||
subcloud_id)
|
||||
|
||||
|
||||
def subcloud_generate_config(request, subcloud_id, data):
|
||||
return dcmanagerclient(request).subcloud_manager.generate_config_subcloud(
|
||||
subcloud_id, **data)
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# Copyright (c) 2017-2018 Wind River Systems, Inc.
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from dcmanagerclient.api.v1 import client
|
||||
from dcmanagerclient.exceptions import APIException
|
||||
|
||||
from horizon.utils.memoized import memoized # noqa
|
||||
|
||||
from openstack_dashboard.api import base
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DEFAULT_CONFIG_NAME = "all clouds default"
|
||||
|
||||
|
||||
@memoized
|
||||
def dcmanagerclient(request):
|
||||
endpoint = base.url_for(request, 'dcmanager', 'adminURL')
|
||||
c = client.Client(project_id=request.user.project_id,
|
||||
user_id=request.user.id,
|
||||
auth_token=request.user.token.id,
|
||||
dcmanager_url=endpoint)
|
||||
return c
|
||||
|
||||
|
||||
class Summary(base.APIResourceWrapper):
|
||||
_attrs = ['name', 'critical', 'major', 'minor', 'warnings', 'status']
|
||||
|
||||
|
||||
def alarm_summary_list(request):
|
||||
summaries = dcmanagerclient(request).alarm_manager.list_alarms()
|
||||
return [Summary(summary) for summary in summaries]
|
||||
|
||||
|
||||
class Subcloud(base.APIResourceWrapper):
|
||||
_attrs = ['subcloud_id', 'name', 'description', 'location',
|
||||
'software_version', 'management_subnet', 'management_state',
|
||||
'availability_status', 'management_start_ip',
|
||||
'management_end_ip', 'management_gateway_ip',
|
||||
'systemcontroller_gateway_ip', 'created_at', 'updated_at',
|
||||
'sync_status', 'endpoint_sync_status', ]
|
||||
|
||||
|
||||
def subcloud_list(request):
|
||||
subclouds = dcmanagerclient(request).subcloud_manager.list_subclouds()
|
||||
return [Subcloud(subcloud) for subcloud in subclouds]
|
||||
|
||||
|
||||
def subcloud_create(request, data):
|
||||
return dcmanagerclient(request).subcloud_manager.add_subcloud(
|
||||
**data.get('data'))
|
||||
|
||||
|
||||
def subcloud_update(request, subcloud_id, changes):
|
||||
response = dcmanagerclient(request).subcloud_manager.update_subcloud(
|
||||
subcloud_id, **changes.get('updated'))
|
||||
# Updating returns a list of subclouds for some reason
|
||||
return [Subcloud(subcloud) for subcloud in response]
|
||||
|
||||
|
||||
def subcloud_delete(request, subcloud_id):
|
||||
return dcmanagerclient(request).subcloud_manager.delete_subcloud(
|
||||
subcloud_id)
|
||||
|
||||
|
||||
def subcloud_generate_config(request, subcloud_id, data):
|
||||
return dcmanagerclient(request).subcloud_manager.generate_config_subcloud(
|
||||
subcloud_id, **data)
|
||||
|
||||
|
||||
class Strategy(base.APIResourceWrapper):
|
||||
_attrs = ['subcloud_apply_type', 'max_parallel_subclouds',
|
||||
'stop_on_failure', 'state', 'created_at', 'updated_at']
|
||||
|
||||
|
||||
def get_strategy(request):
|
||||
try:
|
||||
response = dcmanagerclient(request).sw_update_manager.\
|
||||
patch_strategy_detail()
|
||||
except APIException as e:
|
||||
if e.error_code == 404:
|
||||
return None
|
||||
else:
|
||||
raise e
|
||||
|
||||
if response and len(response):
|
||||
return Strategy(response[0])
|
||||
|
||||
|
||||
def strategy_create(request, data):
|
||||
response = dcmanagerclient(request).sw_update_manager.\
|
||||
create_patch_strategy(**data)
|
||||
return Strategy(response)
|
||||
|
||||
|
||||
def strategy_apply(request):
|
||||
return dcmanagerclient(request).sw_update_manager.apply_patch_strategy()
|
||||
|
||||
|
||||
def strategy_abort(request):
|
||||
return dcmanagerclient(request).sw_update_manager.abort_patch_strategy()
|
||||
|
||||
|
||||
def strategy_delete(request):
|
||||
return dcmanagerclient(request).sw_update_manager.delete_patch_strategy()
|
||||
|
||||
|
||||
class Step(base.APIResourceWrapper):
|
||||
_attrs = ['cloud', 'stage', 'state', 'details', 'started_at',
|
||||
'finished_at']
|
||||
|
||||
|
||||
def step_list(request):
|
||||
response = dcmanagerclient(request).strategy_step_manager.\
|
||||
list_strategy_steps()
|
||||
return [Step(step) for step in response]
|
||||
|
||||
|
||||
class Config(base.APIResourceWrapper):
|
||||
_attrs = ['cloud', 'storage_apply_type', 'compute_apply_type',
|
||||
'max_parallel_computes', 'alarm_restriction_type',
|
||||
'default_instance_action']
|
||||
|
||||
|
||||
def config_list(request):
|
||||
response = dcmanagerclient(request).sw_update_options_manager.\
|
||||
sw_update_options_list()
|
||||
return [Config(config) for config in response]
|
||||
|
||||
|
||||
def config_update(request, subcloud, data):
|
||||
response = dcmanagerclient(request).sw_update_options_manager.\
|
||||
sw_update_options_update(subcloud, **data)
|
||||
return Config(response)
|
||||
|
||||
|
||||
def config_delete(request, subcloud):
|
||||
return dcmanagerclient(request).sw_update_options_manager.\
|
||||
sw_update_options_delete(subcloud)
|
||||
|
||||
|
||||
def config_get(request, subcloud):
|
||||
if subcloud == DEFAULT_CONFIG_NAME:
|
||||
subcloud = None
|
||||
response = dcmanagerclient(request).sw_update_options_manager.\
|
||||
sw_update_options_detail(subcloud)
|
||||
if response and len(response):
|
||||
return Config(response[0])
|
||||
|
|
|
@ -19,12 +19,16 @@ class SoftwareManagement(horizon.Panel):
|
|||
def allowed(self, context):
|
||||
if not base.is_service_enabled(context['request'], 'platform'):
|
||||
return False
|
||||
elif context['request'].user.services_region == 'SystemController':
|
||||
return False
|
||||
else:
|
||||
return super(SoftwareManagement, self).allowed(context)
|
||||
|
||||
def nav(self, context):
|
||||
if not base.is_service_enabled(context['request'], 'platform'):
|
||||
return False
|
||||
elif context['request'].user.services_region == 'SystemController':
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ class IndexView(tabs.TabbedTableView):
|
|||
|
||||
class DetailPatchView(views.HorizonTemplateView):
|
||||
template_name = 'admin/software_management/_detail_patches.html'
|
||||
failure_url = 'horizon:admin:software_management:index'
|
||||
page_title = 'Patch Detail'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
@ -59,7 +60,7 @@ class DetailPatchView(views.HorizonTemplateView):
|
|||
patch.requires_display = "%s" % "\n".join(
|
||||
filter(None, patch.requires))
|
||||
except Exception:
|
||||
redirect = reverse('horizon:admin:software_management:index')
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve details for '
|
||||
'patch "%s".') % patch_id,
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from dcmanagerclient import exceptions as exc
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import messages
|
||||
|
||||
from starlingx_dashboard import api
|
||||
from starlingx_dashboard.dashboards.admin.software_management.forms \
|
||||
import UploadPatchForm as AdminPatchForm
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UploadPatchForm(AdminPatchForm):
|
||||
failure_url = 'horizon:dc_admin:dc_software_management:index'
|
||||
|
||||
|
||||
class CreateCloudPatchStrategyForm(forms.SelfHandlingForm):
|
||||
failure_url = 'horizon:dc_admin:dc_software_management:index'
|
||||
|
||||
SUBCLOUD_APPLY_TYPES = (
|
||||
('parallel', _("Parallel")),
|
||||
('serial', _("Serial")),
|
||||
)
|
||||
|
||||
subcloud_apply_type = forms.ChoiceField(
|
||||
label=_("Subcloud Apply Type"),
|
||||
required=True,
|
||||
choices=SUBCLOUD_APPLY_TYPES,
|
||||
widget=forms.Select())
|
||||
|
||||
max_parallel_subclouds = forms.IntegerField(
|
||||
label=_("Maximum Parallel Subclouds"),
|
||||
initial=20,
|
||||
min_value=2,
|
||||
max_value=100,
|
||||
required=True,
|
||||
error_messages={'invalid': _('Maximum Parallel Subclouds must be '
|
||||
'between 2 and 100.')},
|
||||
widget=forms.TextInput())
|
||||
|
||||
stop_on_failure = forms.BooleanField(
|
||||
label=_("Stop on Failure"),
|
||||
required=False,
|
||||
initial=True,
|
||||
help_text=_("Determines whether patch orchestration failure in a "
|
||||
"subcloud prevents application to subsequent subclouds"))
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
# convert keys to use dashes
|
||||
for k in data.keys():
|
||||
if '_' in k:
|
||||
data[k.replace('_', '-')] = data[k]
|
||||
del data[k]
|
||||
|
||||
data['stop-on-failure'] = str(data['stop-on-failure']).lower()
|
||||
|
||||
response = api.dc_manager.strategy_create(request, data)
|
||||
if not response:
|
||||
messages.error(request, "Strategy creation failed")
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request, "Strategy creation failed",
|
||||
redirect=redirect)
|
||||
return True
|
||||
|
||||
|
||||
class CreateCloudPatchConfigForm(forms.SelfHandlingForm):
|
||||
failure_url = 'horizon:dc_admin:dc_software_management:index'
|
||||
|
||||
APPLY_TYPES = (
|
||||
('parallel', _("Parallel")),
|
||||
('serial', _("Serial")),
|
||||
)
|
||||
|
||||
INSTANCE_ACTIONS = (
|
||||
('migrate', _("Migrate")),
|
||||
('stop-start', _("Stop-Start")),
|
||||
)
|
||||
|
||||
ALARM_RESTRICTION_TYPES = (
|
||||
('relaxed', _("Relaxed")),
|
||||
('strict', _("Strict")),
|
||||
)
|
||||
|
||||
subcloud = forms.ChoiceField(
|
||||
label=_("Subcloud"),
|
||||
required=True,
|
||||
widget=forms.Select())
|
||||
|
||||
storage_apply_type = forms.ChoiceField(
|
||||
label=_("Storage Apply Type"),
|
||||
required=True,
|
||||
choices=APPLY_TYPES,
|
||||
widget=forms.Select())
|
||||
|
||||
compute_apply_type = forms.ChoiceField(
|
||||
label=_("Compute Apply Type"),
|
||||
required=True,
|
||||
choices=APPLY_TYPES,
|
||||
widget=forms.Select(
|
||||
attrs={
|
||||
'class': 'switchable',
|
||||
'data-slug': 'compute_apply_type'}))
|
||||
|
||||
max_parallel_computes = forms.IntegerField(
|
||||
label=_("Maximum Parallel Compute Hosts"),
|
||||
initial=2,
|
||||
min_value=2,
|
||||
max_value=100,
|
||||
required=True,
|
||||
error_messages={'invalid': _('Maximum Parallel Compute Hosts must be '
|
||||
'between 2 and 100.')},
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
'class': 'switched',
|
||||
'data-switch-on': 'compute_apply_type',
|
||||
'data-compute_apply_type-parallel':
|
||||
'Maximum Parallel Compute Hosts'}))
|
||||
|
||||
default_instance_action = forms.ChoiceField(
|
||||
label=_("Default Instance Action"),
|
||||
required=True,
|
||||
choices=INSTANCE_ACTIONS,
|
||||
widget=forms.Select())
|
||||
|
||||
alarm_restriction_type = forms.ChoiceField(
|
||||
label=_("Alarm Restrictions"),
|
||||
required=True,
|
||||
choices=ALARM_RESTRICTION_TYPES,
|
||||
widget=forms.Select())
|
||||
|
||||
def __init__(self, request, *args, **kwargs):
|
||||
super(CreateCloudPatchConfigForm, self).__init__(request, *args,
|
||||
**kwargs)
|
||||
subcloud_list = [(api.dc_manager.DEFAULT_CONFIG_NAME,
|
||||
api.dc_manager.DEFAULT_CONFIG_NAME), ]
|
||||
subclouds = api.dc_manager.subcloud_list(self.request)
|
||||
subcloud_list.extend([(c.name, c.name) for c in subclouds])
|
||||
self.fields['subcloud'].choices = subcloud_list
|
||||
|
||||
if self.initial.get('subcloud', None):
|
||||
self.fields['subcloud'].widget.attrs['disabled'] = 'disabled'
|
||||
|
||||
def handle(self, request, data):
|
||||
try:
|
||||
for k in data.keys():
|
||||
if '_' in k:
|
||||
data[k.replace('_', '-')] = data[k]
|
||||
del data[k]
|
||||
|
||||
subcloud = data['subcloud']
|
||||
if subcloud == api.dc_manager.DEFAULT_CONFIG_NAME:
|
||||
subcloud = None
|
||||
del data['subcloud']
|
||||
|
||||
response = api.dc_manager.config_update(request, subcloud, data)
|
||||
if not response:
|
||||
messages.error(request, "Cloud Patching Configuration "
|
||||
"creation failed")
|
||||
|
||||
except exc.APIException as e:
|
||||
LOG.error(e.error_message)
|
||||
messages.error(request, e.error_message)
|
||||
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request, e.error_message, redirect=redirect)
|
||||
except Exception:
|
||||
redirect = reverse(self.failure_url)
|
||||
exceptions.handle(request,
|
||||
"Cloud Patching Configuration creation failed",
|
||||
redirect=redirect)
|
||||
return True
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
import horizon
|
||||
from starlingx_dashboard.dashboards.dc_admin import dashboard
|
||||
|
||||
|
||||
class DCSoftwareManagement(horizon.Panel):
|
||||
name = _("Software Management")
|
||||
slug = 'dc_software_management'
|
||||
|
||||
def allowed(self, context):
|
||||
if context['request'].user.services_region != 'SystemController':
|
||||
return False
|
||||
|
||||
return super(DCSoftwareManagement, self).allowed(context)
|
||||
|
||||
def nav(self, context):
|
||||
if context['request'].user.services_region != 'SystemController':
|
||||
return False
|
||||
|
||||
return super(DCSoftwareManagement, self).allowed(context)
|
||||
|
||||
|
||||
dashboard.DCAdmin.register(DCSoftwareManagement)
|
|
@ -0,0 +1,327 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse # noqa
|
||||
from django.template.defaultfilters import safe, title # noqa
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import messages
|
||||
from horizon import tables
|
||||
|
||||
from starlingx_dashboard import api
|
||||
from starlingx_dashboard.dashboards.admin.software_management import tables \
|
||||
as AdminTables
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Patching
|
||||
class UploadPatch(AdminTables.UploadPatch):
|
||||
url = "horizon:dc_admin:dc_software_management:dc_patchupload"
|
||||
|
||||
|
||||
class PatchesTable(AdminTables.PatchesTable):
|
||||
patch_id = tables.Column('patch_id',
|
||||
link="horizon:dc_admin:dc_software_management:"
|
||||
"dc_patchdetail",
|
||||
verbose_name=_('Patch ID'))
|
||||
|
||||
class Meta(object):
|
||||
name = "dc_patches"
|
||||
multi_select = True
|
||||
row_class = AdminTables.UpdatePatchRow
|
||||
status_columns = ['patchstate']
|
||||
row_actions = (AdminTables.ApplyPatch, AdminTables.RemovePatch,
|
||||
AdminTables.DeletePatch)
|
||||
table_actions = (
|
||||
AdminTables.PatchFilterAction, UploadPatch, AdminTables.ApplyPatch,
|
||||
AdminTables.RemovePatch, AdminTables.DeletePatch)
|
||||
verbose_name = _("Patches")
|
||||
hidden_title = False
|
||||
|
||||
|
||||
# Cloud Patch Orchestration
|
||||
def get_cached_cloud_strategy(request, table):
|
||||
if 'cloudpatchstrategy' not in table.kwargs:
|
||||
table.kwargs['cloudpatchstrategy'] = \
|
||||
api.dc_manager.get_strategy(request)
|
||||
return table.kwargs['cloudpatchstrategy']
|
||||
|
||||
|
||||
class CreateCloudPatchStrategy(tables.LinkAction):
|
||||
name = "createcloudpatchstrategy"
|
||||
url = "horizon:dc_admin:dc_software_management:createcloudpatchstrategy"
|
||||
verbose_name = _("Create Strategy")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
icon = "plus"
|
||||
|
||||
def allowed(self, request, datum):
|
||||
try:
|
||||
strategy = get_cached_cloud_strategy(request, self.table)
|
||||
|
||||
classes = [c for c in self.classes if c != "disabled"]
|
||||
self.classes = classes
|
||||
if strategy:
|
||||
if "disabled" not in self.classes:
|
||||
self.classes = [c for c in self.classes] + ['disabled']
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return True
|
||||
|
||||
|
||||
class DeleteCloudPatchStrategy(tables.Action):
|
||||
name = "delete_patch_strategy"
|
||||
force = False
|
||||
disabled = False
|
||||
requires_input = False
|
||||
icon = 'trash'
|
||||
action_type = 'danger'
|
||||
verbose_name = _("Delete Strategy")
|
||||
confirm_message = "You have selected Delete Strategy. " \
|
||||
"Please confirm your selection"
|
||||
|
||||
def allowed(self, request, datum):
|
||||
try:
|
||||
strategy = get_cached_cloud_strategy(request, self.table)
|
||||
self.disabled = True
|
||||
if strategy and strategy.state in ['initial', 'complete', 'failed',
|
||||
'aborted']:
|
||||
self.disabled = False
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return True
|
||||
|
||||
def get_default_classes(self):
|
||||
try:
|
||||
if self.disabled:
|
||||
return ['disabled']
|
||||
return super(DeleteCloudPatchStrategy, self).get_default_classes()
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
|
||||
def single(self, table, request, obj_id):
|
||||
try:
|
||||
result = api.dc_manager.strategy_delete(request)
|
||||
if result:
|
||||
messages.success(request, "Strategy Deleted")
|
||||
else:
|
||||
messages.error(request, "Strategy delete failed")
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
messages.error(request, ex.message)
|
||||
|
||||
|
||||
class ApplyCloudPatchStrategy(tables.Action):
|
||||
name = "apply_cloud_patch_strategy"
|
||||
requires_input = False
|
||||
disabled = False
|
||||
verbose_name = _("Apply Strategy")
|
||||
|
||||
def allowed(self, request, datum):
|
||||
try:
|
||||
strategy = get_cached_cloud_strategy(request, self.table)
|
||||
self.disabled = False
|
||||
if not strategy or strategy.state != 'initial':
|
||||
self.disabled = True
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return True
|
||||
|
||||
def get_default_classes(self):
|
||||
try:
|
||||
if self.disabled:
|
||||
return ['disabled']
|
||||
return super(ApplyCloudPatchStrategy, self).get_default_classes()
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
|
||||
def single(self, table, request, obj_id):
|
||||
try:
|
||||
result = api.dc_manager.strategy_apply(request)
|
||||
if result:
|
||||
messages.success(request, "Strategy apply in progress")
|
||||
else:
|
||||
messages.error(request, "Strategy apply failed")
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
messages.error(request, ex.message)
|
||||
|
||||
|
||||
class AbortCloudPatchStrategy(tables.Action):
|
||||
name = "abort_cloud_patch_strategy"
|
||||
requires_input = False
|
||||
disabled = False
|
||||
action_type = 'danger'
|
||||
verbose_name = _("Abort Strategy")
|
||||
confirm_message = "You have selected Abort Strategy. " \
|
||||
"Please confirm your selection"
|
||||
|
||||
def allowed(self, request, datum):
|
||||
try:
|
||||
strategy = get_cached_cloud_strategy(request, self.table)
|
||||
self.disabled = False
|
||||
if not strategy or strategy.state != 'applying':
|
||||
self.disabled = True
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
return True
|
||||
|
||||
def get_default_classes(self):
|
||||
try:
|
||||
if self.disabled:
|
||||
return ['disabled']
|
||||
return super(AbortCloudPatchStrategy, self).get_default_classes()
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
|
||||
def single(self, table, request, obj_id):
|
||||
try:
|
||||
result = api.dc_manager.strategy_abort(request)
|
||||
if result:
|
||||
messages.success(request, "Strategy abort in progress")
|
||||
else:
|
||||
messages.error(request, "Strategy abort failed")
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
messages.error(request, ex.message)
|
||||
|
||||
|
||||
STEP_STATE_CHOICES = (
|
||||
(None, True),
|
||||
("", True),
|
||||
("none", True),
|
||||
("complete", True),
|
||||
("initial", True),
|
||||
("failed", False),
|
||||
("timed-out", False),
|
||||
("aborted", False),
|
||||
)
|
||||
|
||||
|
||||
def get_apply_percent(cell):
|
||||
if '(' in cell and '%)' in cell:
|
||||
percent = cell.split('(')[1].split('%)')[0]
|
||||
return {'percent': "%d%%" % float(percent)}
|
||||
return {}
|
||||
|
||||
|
||||
def get_state(step):
|
||||
state = step.state
|
||||
if '%' in step.details:
|
||||
percent = [s for s in step.details.split(' ') if '%' in s]
|
||||
if percent and len(percent):
|
||||
percent = percent[0]
|
||||
state += " (%s)" % percent
|
||||
return state
|
||||
|
||||
|
||||
class CloudPatchStepsTable(tables.DataTable):
|
||||
cloud = tables.Column('cloud', verbose_name=_('Cloud'))
|
||||
stage = tables.Column('stage', verbose_name=_('Stage'))
|
||||
state = tables.Column(get_state,
|
||||
verbose_name=_('State'),
|
||||
status=True,
|
||||
status_choices=STEP_STATE_CHOICES,
|
||||
filters=(safe,),
|
||||
cell_attributes_getter=get_apply_percent)
|
||||
details = tables.Column('details',
|
||||
verbose_name=_("Details"),)
|
||||
started_at = tables.Column('started_at',
|
||||
verbose_name=_('Started At'))
|
||||
finished_at = tables.Column('finished_at',
|
||||
verbose_name=_('Finished At'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return "%s" % obj.cloud
|
||||
|
||||
class Meta(object):
|
||||
name = "cloudpatchsteps"
|
||||
multi_select = False
|
||||
status_columns = ['state', ]
|
||||
table_actions = (CreateCloudPatchStrategy, ApplyCloudPatchStrategy,
|
||||
AbortCloudPatchStrategy, DeleteCloudPatchStrategy)
|
||||
verbose_name = _("Steps")
|
||||
hidden_title = False
|
||||
|
||||
|
||||
# Cloud Patch Config
|
||||
class CreateCloudPatchConfig(tables.LinkAction):
|
||||
name = "createcloudpatchconfig"
|
||||
url = "horizon:dc_admin:dc_software_management:createcloudpatchconfig"
|
||||
verbose_name = _("Create New Cloud Patching Configuration")
|
||||
classes = ("ajax-modal", "btn-create")
|
||||
icon = "plus"
|
||||
|
||||
|
||||
class EditCloudPatchConfig(tables.LinkAction):
|
||||
name = "editcloudpatchconfig"
|
||||
url = "horizon:dc_admin:dc_software_management:editcloudpatchconfig"
|
||||
verbose_name = _("Edit Configuration")
|
||||
classes = ("ajax-modal",)
|
||||
|
||||
|
||||
class DeleteCloudPatchConfig(tables.DeleteAction):
|
||||
@staticmethod
|
||||
def action_present(count):
|
||||
return ungettext_lazy(
|
||||
u"Delete Cloud Patching Configuration",
|
||||
u"Delete Cloud Patching Configurations",
|
||||
count
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def action_past(count):
|
||||
return ungettext_lazy(
|
||||
u"Deleted Cloud Patching Configuration",
|
||||
u"Deleted Cloud Patching Configurations",
|
||||
count
|
||||
)
|
||||
|
||||
def allowed(self, request, config=None):
|
||||
if config and config.cloud == api.dc_manager.DEFAULT_CONFIG_NAME:
|
||||
return False
|
||||
return True
|
||||
|
||||
def delete(self, request, config):
|
||||
try:
|
||||
api.dc_manager.config_delete(request, config)
|
||||
except Exception:
|
||||
msg = _('Failed to delete configuration for subcloud %(cloud)') % \
|
||||
{'cloud': config, }
|
||||
redirect = reverse('horizon:dc_admin:dc_software_management:index')
|
||||
exceptions.handle(request, msg, redirect=redirect)
|
||||
|
||||
|
||||
class CloudPatchConfigTable(tables.DataTable):
|
||||
cloud = tables.Column('cloud', verbose_name=_('Cloud'))
|
||||
storage_apply_type = tables.Column('storage_apply_type',
|
||||
verbose_name=_('Storage Apply Type'))
|
||||
compute_apply_type = tables.Column('compute_apply_type',
|
||||
verbose_name=_('Compute Apply Type'))
|
||||
max_parallel_computes = tables.Column(
|
||||
'max_parallel_computes', verbose_name=_('Max Parallel Computes'))
|
||||
default_instance_action = tables.Column(
|
||||
'default_instance_action', verbose_name=_('Default Instance Action'))
|
||||
alarm_restriction_type = tables.Column(
|
||||
'alarm_restriction_type', verbose_name=_('Alarm Restrictions'))
|
||||
|
||||
def get_object_id(self, obj):
|
||||
return "%s" % obj.cloud
|
||||
|
||||
def get_object_display(self, obj):
|
||||
return obj.cloud
|
||||
|
||||
class Meta(object):
|
||||
name = "cloudpatchconfig"
|
||||
multi_select = False
|
||||
table_actions = (CreateCloudPatchConfig,)
|
||||
row_actions = (EditCloudPatchConfig, DeleteCloudPatchConfig,)
|
||||
verbose_name = _("Cloud Patching Configurations")
|
||||
hidden_title = False
|
|
@ -0,0 +1,93 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import tabs
|
||||
from starlingx_dashboard import api
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management \
|
||||
import tables as tables
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class PatchesTab(tabs.TableTab):
|
||||
table_classes = (tables.PatchesTable,)
|
||||
name = _("Patches")
|
||||
slug = "patches"
|
||||
template_name = ("dc_admin/dc_software_management/_patches.html")
|
||||
|
||||
def get_dc_patches_data(self):
|
||||
request = self.request
|
||||
patches = []
|
||||
try:
|
||||
patches = api.patch.get_patches(request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve patch list.'))
|
||||
|
||||
return patches
|
||||
|
||||
|
||||
class CloudPatchOrchestrationTab(tabs.TableTab):
|
||||
table_classes = (tables.CloudPatchStepsTable,)
|
||||
name = _("Cloud Patching Orchestration")
|
||||
slug = "cloud_patch_orchestration"
|
||||
template_name = ("dc_admin/dc_software_management/"
|
||||
"_cloud_patch_orchestration.html")
|
||||
|
||||
def get_context_data(self, request):
|
||||
context = super(CloudPatchOrchestrationTab, self).\
|
||||
get_context_data(request)
|
||||
|
||||
strategy = None
|
||||
try:
|
||||
strategy = api.dc_manager.get_strategy(request)
|
||||
except Exception as ex:
|
||||
LOG.exception(ex)
|
||||
exceptions.handle(request,
|
||||
_('Unable to retrieve current strategy.'))
|
||||
context['strategy'] = strategy
|
||||
return context
|
||||
|
||||
def get_cloudpatchsteps_data(self):
|
||||
request = self.request
|
||||
steps = []
|
||||
try:
|
||||
steps = api.dc_manager.step_list(request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve steps list.'))
|
||||
|
||||
return steps
|
||||
|
||||
|
||||
class CloudPatchConfigTab(tabs.TableTab):
|
||||
table_classes = (tables.CloudPatchConfigTable,)
|
||||
name = _("Cloud Patching Configuration")
|
||||
slug = "cloud_patch_config"
|
||||
template_name = ("dc_admin/dc_software_management/"
|
||||
"_cloud_patch_config.html")
|
||||
|
||||
def get_cloudpatchconfig_data(self):
|
||||
request = self.request
|
||||
steps = []
|
||||
try:
|
||||
steps = api.dc_manager.config_list(request)
|
||||
except Exception:
|
||||
exceptions.handle(self.request,
|
||||
_('Unable to retrieve configuration list.'))
|
||||
|
||||
return steps
|
||||
|
||||
|
||||
class DCSoftwareManagementTabs(tabs.TabGroup):
|
||||
slug = "dc_software_management_tabs"
|
||||
tabs = (PatchesTab, CloudPatchOrchestrationTab, CloudPatchConfigTab)
|
||||
sticky = True
|
|
@ -0,0 +1,5 @@
|
|||
{% load i18n sizeformat %}
|
||||
|
||||
{% block main %}
|
||||
{{ cloudpatchconfig_table.render }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,29 @@
|
|||
{% load i18n sizeformat %}
|
||||
|
||||
{% block main %}
|
||||
|
||||
<div class="info row-fluid detail">
|
||||
<h3>{% trans "Cloud Patch Strategy" %}</h3>
|
||||
<hr class="header_rule">
|
||||
<div id="cloud-patch-strategy-detail">
|
||||
|
||||
{% if strategy %}
|
||||
<dl class="dl-horizontal-wide">
|
||||
<dt>{% trans "Subcloud Apply Type" %}</dt>
|
||||
<dd>{{ strategy.subcloud_apply_type }}</dd>
|
||||
<dt>{% trans "Max Parallel Subclouds" %}</dt>
|
||||
<dd>{{ strategy.max_parallel_subclouds }}</dd>
|
||||
<dt>{% trans "Stop On Failure" %}</dt>
|
||||
<dd>{{ strategy.stop_on_failure }}</dd>
|
||||
<dt>{% trans "State" %}</dt>
|
||||
<dd>{{ strategy.state }}</dd>
|
||||
</dl>
|
||||
{% else %}
|
||||
{% trans "No Strategy has been created" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
{{ cloudpatchsteps_table.render }}
|
||||
|
||||
{% endblock %}
|
|
@ -0,0 +1,28 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_action %}{% url 'horizon:dc_admin:dc_software_management:createcloudpatchconfig' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create New Cloud Patching Configuration" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "Create a configuration for a specific subcloud to use the specified patch strategy settings instead of the defaults values." %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "Note: for Simplex systems, the default instance action must be set to stop-start (since migration is not possible on a single-node system)." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">Cancel</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Cloud Patching Configuration" %}" />
|
||||
{% endblock %}
|
|
@ -0,0 +1,25 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_action %}{% url 'horizon:dc_admin:dc_software_management:createcloudpatchstrategy' %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Create Patch Strategy" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "Create a strategy to specify how the clouds should be patched." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">Cancel</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Create Patch Strategy" %}" />
|
||||
{% endblock %}
|
|
@ -0,0 +1,54 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n breadcrumb_nav %}
|
||||
{% block title %}{% trans "Patch Details" %}{% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<h3> {{patch.patch_id }} </h3>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-12">
|
||||
<div class="info row-fluid detail">
|
||||
<h4>{% trans "Info" %}</h4>
|
||||
<hr class="header_rule">
|
||||
<dl>
|
||||
<dt>{% trans "Patch State" %}</dt>
|
||||
<dd>{{ patch.patchstate }}</dd>
|
||||
<dt>{% trans "Platform Release Version" %}</dt>
|
||||
<dd>{{ patch.sw_version }}</dd>
|
||||
<dt>{% trans "Reboot-Required" %}</dt>
|
||||
<dd>{{ patch.reboot_required }}</dd>
|
||||
<dt>{% trans "Patch Status Code" %}</dt>
|
||||
<dd>{{ patch.status }}</dd>
|
||||
{% if patch.unremovable == "Y" %}
|
||||
<dt>{% trans "Unremovable" %}</dt>
|
||||
<dd>{% trans "This patch cannot be removed" %}</dd>
|
||||
{% endif %}
|
||||
<dt>{% trans "Required Patches" %}</dt>
|
||||
<dd>{{ patch.requires_display|linebreaks }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{% trans "Summary" %}</dt>
|
||||
<dd>{{ patch.summary|linebreaks }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{% trans "Description" %}</dt>
|
||||
<dd>{{ patch.description|linebreaks }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{% trans "Warnings" %}</dt>
|
||||
<dd>{{ patch.warnings|linebreaks }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{% trans "Installation Instructions" %}</dt>
|
||||
<dd>{{ patch.install_instructions|linebreaks }}</dd>
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>{% trans "Contents" %}</dt>
|
||||
<dd>{{ patch.contents_display|linebreaks }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_action %}{% url 'horizon:dc_admin:dc_software_management:editcloudpatchconfig' subcloud %}{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Edit Cloud Patch Configuration" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "Edit a configuration for a specific subcloud to use the specified patch strategy settings instead of the defaults values." %}
|
||||
</p>
|
||||
<p>
|
||||
{% trans "Note: for Simplex systems, the default instance action must be set to stop-start (since migration is not possible on a single-node system)." %}
|
||||
</p>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">Cancel</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Edit Cloud Patching Configuration" %}" />
|
||||
{% endblock %}
|
|
@ -0,0 +1,10 @@
|
|||
{% load i18n sizeformat %}
|
||||
|
||||
{% block main %}
|
||||
<div id="patches">
|
||||
{{ dc_patches_table.render }}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "horizon/common/_modal_form.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block form_action %}{% url 'horizon:dc_admin:dc_software_management:dc_patchupload' %}{% endblock %}
|
||||
{% block form_attrs %}enctype="multipart/form-data"{% endblock %}
|
||||
|
||||
{% block modal-header %}{% trans "Upload Patches" %}{% endblock %}
|
||||
|
||||
{% block modal-body %}
|
||||
<div class="left">
|
||||
<fieldset>
|
||||
{% include "horizon/common/_form_fields.html" %}
|
||||
</fieldset>
|
||||
</div>
|
||||
<div class="right">
|
||||
<h3>{% trans "Description:" %}</h3>
|
||||
<p>
|
||||
{% trans "Specify one or more patch files to upload to the Patching Service." %}
|
||||
</p>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block modal-footer %}
|
||||
<a class="btn btn-default cancel" data-dismiss="modal">Cancel</a>
|
||||
<input class="btn btn-primary pull-right" type="submit" value="{% trans "Upload Patches" %}" />
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create New Cloud Patching Configuration" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Create New Cloud Patching Configuration") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'dc_admin/dc_software_management/_create_cloud_patch_config.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Create Patch Strategy" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Create Patch Strategy") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'dc_admin/dc_software_management/_create_cloud_patch_strategy.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Edit Cloud Patching Configuration" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Edit Cloud Patching Configuration") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'dc_admin/dc_software_management/_edit_cloud_patch_config.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,28 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Software Management" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Software Management") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
<div class="row-fluid">
|
||||
<div class="col-sm-12">
|
||||
{{ tab_group.render }}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js %}
|
||||
{{ block.super }}
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
horizon.refresh.addRefreshFunction(function (html) {
|
||||
var $old_strategy = $('#cloud-patch-strategy-detail');
|
||||
var $new_strategy = $(html).find('#cloud-patch-strategy-detail');
|
||||
if ($new_strategy.html() != $old_strategy.html()) {
|
||||
$old_strategy.replaceWith($new_strategy);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
|
@ -0,0 +1,11 @@
|
|||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
{% block title %}{% trans "Upload Patches" %}{% endblock %}
|
||||
|
||||
{% block page_header %}
|
||||
{% include "horizon/common/_page_header.html" with title=_("Upload Patches") %}
|
||||
{% endblock page_header %}
|
||||
|
||||
{% block main %}
|
||||
{% include 'dc_admin/dc_software_management/_upload_patch.html' %}
|
||||
{% endblock %}
|
|
@ -0,0 +1,35 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
from django.conf.urls import url
|
||||
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import CreateCloudPatchConfigView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import CreateCloudPatchStrategyView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import DetailPatchView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import EditCloudPatchConfigView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import IndexView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.views \
|
||||
import UploadPatchView
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^$', IndexView.as_view(), name='index'),
|
||||
url(r'^(?P<patch_id>[^/]+)/patchdetail/$',
|
||||
DetailPatchView.as_view(), name='dc_patchdetail'),
|
||||
url(r'^dc_patchupload/$', UploadPatchView.as_view(),
|
||||
name='dc_patchupload'),
|
||||
url(r'^createcloudpatchstrategy/$', CreateCloudPatchStrategyView.as_view(),
|
||||
name='createcloudpatchstrategy'),
|
||||
url(r'^createcloudpatchconfig/$', CreateCloudPatchConfigView.as_view(),
|
||||
name='createcloudpatchconfig'),
|
||||
url(r'^(?P<subcloud>[^/]+)/editcloudpatchconfig/$',
|
||||
EditCloudPatchConfigView.as_view(),
|
||||
name='editcloudpatchconfig'),
|
||||
]
|
|
@ -0,0 +1,94 @@
|
|||
#
|
||||
# Copyright (c) 2018 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.urlresolvers import reverse_lazy
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from horizon import exceptions
|
||||
from horizon import forms
|
||||
from horizon import tabs
|
||||
|
||||
from starlingx_dashboard import api
|
||||
from starlingx_dashboard.dashboards.admin.software_management.views import \
|
||||
DetailPatchView as AdminDetailPatchView
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.forms \
|
||||
import CreateCloudPatchConfigForm
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.forms \
|
||||
import CreateCloudPatchStrategyForm
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.forms \
|
||||
import UploadPatchForm
|
||||
from starlingx_dashboard.dashboards.dc_admin.dc_software_management.tabs \
|
||||
import DCSoftwareManagementTabs
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IndexView(tabs.TabbedTableView):
|
||||
tab_group_class = DCSoftwareManagementTabs
|
||||
template_name = 'dc_admin/dc_software_management/index.html'
|
||||
page_title = _("Software Management")
|
||||
|
||||
def get_tabs(self, request, *args, **kwargs):
|
||||
return self.tab_group_class(request, **kwargs)
|
||||
|
||||
|
||||
class UploadPatchView(forms.ModalFormView):
|
||||
form_class = UploadPatchForm
|
||||
template_name = 'dc_admin/dc_software_management/upload_patch.html'
|
||||
context_object_name = 'patch'
|
||||
success_url = reverse_lazy("horizon:dc_admin:dc_software_management:index")
|
||||
|
||||
|
||||
class DetailPatchView(AdminDetailPatchView):
|
||||
template_name = 'dc_admin/dc_software_management/_detail_patches.html'
|
||||
failure_url = 'horizon:dc_admin:dc_software_management:index'
|
||||
|
||||
|
||||
class CreateCloudPatchStrategyView(forms.ModalFormView):
|
||||
form_class = CreateCloudPatchStrategyForm
|
||||
template_name = 'dc_admin/dc_software_management/' \
|
||||
'create_cloud_patch_strategy.html'
|
||||
context_object_name = 'strategy'
|
||||
success_url = reverse_lazy("horizon:dc_admin:dc_software_management:index")
|
||||
|
||||
|
||||
class CreateCloudPatchConfigView(forms.ModalFormView):
|
||||
form_class = CreateCloudPatchConfigForm
|
||||
template_name = 'dc_admin/dc_software_management/' \
|
||||
'create_cloud_patch_config.html'
|
||||
context_object_name = 'config'
|
||||
success_url = reverse_lazy("horizon:dc_admin:dc_software_management:index")
|
||||
|
||||
|
||||
class EditCloudPatchConfigView(forms.ModalFormView):
|
||||
form_class = CreateCloudPatchConfigForm
|
||||
template_name = 'dc_admin/dc_software_management/' \
|
||||
'edit_cloud_patch_config.html'
|
||||
context_object_name = 'config'
|
||||
success_url = reverse_lazy("horizon:dc_admin:dc_software_management:index")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(EditCloudPatchConfigView, self).get_context_data(
|
||||
**kwargs)
|
||||
context['subcloud'] = self.kwargs['subcloud']
|
||||
return context
|
||||
|
||||
def get_initial(self):
|
||||
try:
|
||||
config = api.dc_manager.config_get(self.request,
|
||||
self.kwargs['subcloud'])
|
||||
except Exception:
|
||||
exceptions.handle(self.request, _("Unable to retrieve subcloud "
|
||||
"configuration data."))
|
||||
|
||||
return {'subcloud': config.cloud,
|
||||
'storage_apply_type': config.storage_apply_type,
|
||||
'compute_apply_type': config.compute_apply_type,
|
||||
'max_parallel_computes': config.max_parallel_computes,
|
||||
'default_instance_action': config.default_instance_action,
|
||||
'alarm_restriction_type': config.alarm_restriction_type}
|
|
@ -0,0 +1,10 @@
|
|||
# The slug of the panel to be added to HORIZON_CONFIG. Required.
|
||||
PANEL = 'dc_software_management'
|
||||
# The slug of the dashboard the PANEL associated with. Required.
|
||||
PANEL_DASHBOARD = 'dc_admin'
|
||||
# The slug of the panel group the PANEL is associated with.
|
||||
PANEL_GROUP = 'default'
|
||||
|
||||
# Python panel class of the PANEL to be added.
|
||||
ADD_PANEL = 'starlingx_dashboard.dashboards.' \
|
||||
'dc_admin.dc_software_management.panel.DCSoftwareManagement'
|
|
@ -12,6 +12,7 @@
|
|||
# under the License.
|
||||
|
||||
from cgtsclient import exc as cgtsclient
|
||||
from dcmanagerclient import exceptions as dcmanagerclient
|
||||
|
||||
|
||||
UNAUTHORIZED = (
|
||||
|
@ -28,4 +29,5 @@ RECOVERABLE = (
|
|||
cgtsclient.HTTPBadRequest,
|
||||
cgtsclient.HTTPConflict,
|
||||
cgtsclient.CommunicationError,
|
||||
dcmanagerclient.APIException
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue