From 84ee07bf7850b7301e5fbc48071599ab496fca90 Mon Sep 17 00:00:00 2001 From: Yan Chen Date: Thu, 24 Jan 2019 00:39:06 +0800 Subject: [PATCH] Add support for k8s labels to inventory panel add k8s labels tab and assign/delete actions there are no limit rules for labels_key and labels_value in this patch test reproduction: in a duplex all-in-one contrainerized env: list all k8s labels: click "labels" tap host detail page on sysinv hosts panel assign new k8s labels: click "Assign Kube Label" button on "labels" tap (shown on locked controllers only) delete k8s labels: click "Delete Labels" button on each related row (shown on locked controllers only) in a simpex all-in-one contrainerized env: need edit related code on env and restart service "horizon" to show the assige/delete buttons. Story: 2004470 Task: 28160 Change-Id: I4d86fcca3c2fca9c6902f376f28b7519d8bbde39 Signed-off-by: Yan Chen Signed-off-by: SidneyAn --- .../starlingx_dashboard/api/sysinv.py | 33 ++++++ .../inventory/kubernetes_labels/__init__.py | 0 .../inventory/kubernetes_labels/forms.py | 100 ++++++++++++++++++ .../inventory/kubernetes_labels/tables.py | 93 ++++++++++++++++ .../inventory/kubernetes_labels/views.py | 50 +++++++++ .../dashboards/admin/inventory/tables.py | 2 +- .../dashboards/admin/inventory/tabs.py | 16 ++- .../templates/inventory/_detail_labels.html | 9 ++ .../kubelabels/_assignkubelabel.html | 24 +++++ .../inventory/kubelabels/assignkubelabel.html | 11 ++ .../dashboards/admin/inventory/urls.py | 7 +- .../dashboards/admin/inventory/views.py | 6 +- 12 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/__init__.py create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/forms.py create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/tables.py create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/views.py create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/_detail_labels.html create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/_assignkubelabel.html create mode 100644 starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/assignkubelabel.html diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py index f5396af4..88fa2400 100644 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/api/sysinv.py @@ -136,6 +136,39 @@ def cgtsclient(request): insecure=insecure, cacert=cacert) +class Label(base.APIResourceWrapper): + """Wrapper for Inventory Labels""" + + _attrs = ['uuid', + 'label_key', + 'label_value', + 'host_uuid' + ] + + def __init__(self, apiresource): + super(Label, self).__init__(apiresource) + + +def host_label_list(request, host_id): + labels = cgtsclient(request).label.list(host_id) + return [Label(n) for n in labels] + + +def host_label_get(request, label_id): + label = cgtsclient(request).label.get(label_id) + if not label: + raise ValueError('No match found for label ID "%s".' % label_id) + return Label(label) + + +def host_label_assign(request, host_uuid, label): + return cgtsclient(request).label.assign(host_uuid, label) + + +def host_label_remove(request, label_id): + return cgtsclient(request).label.remove(label_id) + + class Memory(base.APIResourceWrapper): """Wrapper for Inventory System""" diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/__init__.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/forms.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/forms.py new file mode 100644 index 00000000..f3dbb710 --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/forms.py @@ -0,0 +1,100 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import logging + +from cgtsclient import exc +from django.core.urlresolvers import reverse # noqa +from django import shortcuts +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 as stx_api + +LOG = logging.getLogger(__name__) + + +class AssignLabel(forms.SelfHandlingForm): + host_uuid = forms.CharField( + label=_("host_uuid"), + initial='host_uuid', + required=False, + widget=forms.widgets.HiddenInput) + + host_id = forms.CharField( + label=_("host_id"), + initial='host_id', + required=False, + widget=forms.widgets.HiddenInput) + + labelkey = forms.CharField( + label=_("Label Key"), + required=True) + + labelvalue = forms.CharField( + label=_("Label Value"), + required=True) + + failure_url = 'horizon:admin:inventory:detail' + + def __init__(self, *args, **kwargs): + super(AssignLabel, self).__init__(*args, **kwargs) + + def clean(self): + cleaned_data = super(AssignLabel, self).clean() + return cleaned_data + + def handle(self, request, data): + labelkey = data['labelkey'] + labelvalue = data['labelvalue'] + attributes = {} + attributes[labelkey] = labelvalue + try: + new_labels = stx_api.sysinv.host_label_assign( + request, + data['host_uuid'], + attributes) + + # Check if the label is successfully assigned + if not new_labels.labels: + raise exc.ClientException( + "Label Not Created: %s" % labelkey) + uuid = new_labels.labels[0]['uuid'] + label_obj = stx_api.sysinv.host_label_get( + request, + uuid) + + msg = _('Label "%s" was successfully created.') % \ + getattr(label_obj, 'label_key') + LOG.debug(msg) + messages.success(request, msg) + return label_obj + except exc.HTTPNotFound: + msg = _("Label Not Created: %s" % labelkey) + LOG.error(msg) + + # Redirect to failure pg + redirect = reverse(self.failure_url, args=[data['host_id']]) + return shortcuts.redirect(redirect) + except exc.ClientException as ce: + # Display REST API error message on UI + messages.error(request, ce) + LOG.error(ce) + + # Redirect to failure pg + redirect = reverse(self.failure_url, args=[data['host_id']]) + return shortcuts.redirect(redirect) + except Exception: + msg = _('Failed to assign label "%s" to "%s".') % \ + (labelkey, data['host_uuid']) + LOG.error(msg) + redirect = reverse(self.failure_url, + args=[data['host_id']]) + exceptions.handle(request, msg, redirect=redirect) diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/tables.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/tables.py new file mode 100644 index 00000000..29744dbb --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/tables.py @@ -0,0 +1,93 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +import logging + +from django.core.urlresolvers import reverse # noqa +from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ungettext_lazy + +from horizon import exceptions +from horizon import tables +from starlingx_dashboard import api as stx_api + +LOG = logging.getLogger(__name__) + + +def host_locked(host=None): + if not host: + return False + return host._administrative == 'locked' + + +class AssignKubeLabel(tables.LinkAction): + name = "assignKubelabel" + verbose_name = _("Assign Kube Label") + url = "horizon:admin:inventory:assignlabel" + classes = ("ajax-modal", "btn-create") + + def get_link_url(self, datum=None): + host_id = self.table.kwargs['host_id'] + return reverse(self.url, args=(host_id,)) + + def allowed(self, request, datum): + host = self.table.kwargs['host'] + return host_locked(host) + + +class RemoveLabel(tables.DeleteAction): + @staticmethod + def action_present(count): + return ungettext_lazy( + "Delete Label", + "Delete Labels", + count + ) + + @staticmethod + def action_past(count): + return ungettext_lazy( + "Deleted Label", + "Deleted Labels", + count + ) + + def allowed(self, request, datum): + host = self.table.kwargs['host'] + return host_locked(host) + + def delete(self, request, label_id): + host_id = self.table.kwargs['host_id'] + try: + stx_api.sysinv.host_label_remove(request, label_id) + except Exception: + msg = _('Failed to delete host %(hid)s label %(lid)s') % { + 'hid': host_id, 'lid': label_id} + LOG.error(msg) + redirect = reverse('horizon:admin:inventory:detail', + args=(host_id,)) + exceptions.handle(request, msg, redirect=redirect) + + +class LabelTable(tables.DataTable): + uuid = tables.Column('uuid', + verbose_name=_('UUID')) + + label_key = tables.Column('label_key', + verbose_name=_('Label Key')) + + label_value = tables.Column('label_value', + verbose_name=_('Label Value')) + + def get_object_id(self, datum): + return str(datum.uuid) + + class Meta(object): + name = "labels" + verbose_name = _("Label") + multi_select = False + row_actions = (RemoveLabel, ) + table_actions = (AssignKubeLabel, ) diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/views.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/views.py new file mode 100644 index 00000000..edb3aa8f --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/kubernetes_labels/views.py @@ -0,0 +1,50 @@ +# +# Copyright (c) 2019 Wind River Systems, Inc. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +import logging + +from django.core.urlresolvers import reverse +from django.utils.translation import ugettext_lazy as _ + +from horizon import exceptions +from horizon import forms +from starlingx_dashboard import api as stx_api +from starlingx_dashboard.dashboards.admin.inventory.kubernetes_labels.forms \ + import AssignLabel + +LOG = logging.getLogger(__name__) + + +class AssignLabelView(forms.ModalFormView): + form_class = AssignLabel + template_name = 'admin/inventory/kubelabels/assignkubelabel.html' + success_url = 'horizon:admin:inventory:detail' + failure_url = 'horizon:admin:inventory:detail' + + def get_success_url(self): + return reverse(self.success_url, + args=(self.kwargs['host_id'],)) + + def get_failure_url(self): + return reverse(self.failure_url, + args=(self.kwargs['host_id'],)) + + def get_context_data(self, **kwargs): + context = super(AssignLabelView, self).get_context_data(**kwargs) + context['host_id'] = self.kwargs['host_id'] + return context + + def get_initial(self): + initial = super(AssignLabelView, self).get_initial() + initial['host_id'] = self.kwargs['host_id'] + try: + host = stx_api.sysinv.host_get(self.request, initial['host_id']) + except Exception: + exceptions.handle(self.request, _('Unable to retrieve host.')) + initial['host_uuid'] = host.uuid + return initial diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tables.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tables.py index 2764a72c..362f97c1 100755 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tables.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tables.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tabs.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tabs.py index 79206e0d..e1e678aa 100755 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tabs.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/tabs.py @@ -22,6 +22,8 @@ from starlingx_dashboard.dashboards.admin.inventory.devices import \ tables as device_tables from starlingx_dashboard.dashboards.admin.inventory.interfaces import \ tables as interface_tables +from starlingx_dashboard.dashboards.admin.inventory.kubernetes_labels import \ + tables as label_tables from starlingx_dashboard.dashboards.admin.inventory.lldp import \ tables as lldp_tables from starlingx_dashboard.dashboards.admin.inventory.memories import \ @@ -677,8 +679,20 @@ class LldpTab(tabs.TableTab): return host.lldpneighbours +class LabelsTab(tabs.TableTab): + table_classes = (label_tables.LabelTable, ) + name = _("Labels") + slug = "labels" + template_name = ("admin/inventory/_detail_labels.html") + + def get_labels_data(self): + host = self.tab_group.kwargs['host'] + host.labels.sort(key=lambda f: (f.host_uuid)) + return host.labels + + class HostDetailTabs(tabs.TabGroup): slug = "inventory_details" tabs = (OverviewTab, CpuFunctionsTab, MemorysTab, StorageTab, PortsTab, - InterfacesTab, LldpTab, SensorTab, DevicesTab, ) + InterfacesTab, LldpTab, SensorTab, DevicesTab, LabelsTab, ) sticky = True diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/_detail_labels.html b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/_detail_labels.html new file mode 100644 index 00000000..3a284cdb --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/_detail_labels.html @@ -0,0 +1,9 @@ +{% load i18n sizeformat %} + +{% block main %} + {% autoescape off %} +
+ {{ labels_table.render }} +
+ {% endautoescape %} +{% endblock %} diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/_assignkubelabel.html b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/_assignkubelabel.html new file mode 100644 index 00000000..80dc8fb3 --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/_assignkubelabel.html @@ -0,0 +1,24 @@ +{% extends "horizon/common/_modal_form.html" %} +{% load i18n %} + +{% block form_id %}assign_label_form{% endblock %} +{% block form_action %}{% url 'horizon:admin:inventory:assignlabel' host_id %}{% endblock %} + +{% block modal-header %}{% trans "Assign Kubenetes Label" %}{% endblock %} + +{% block modal-body %} +
+
+ {% include "horizon/common/_form_fields.html" %} +
+
+
+

{% trans "Description" %}:

+

{% trans "Assign a kubernetes label to the host." %}

+
+{% endblock %} + +{% block modal-footer %} + Cancel + +{% endblock %} diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/assignkubelabel.html b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/assignkubelabel.html new file mode 100644 index 00000000..9c5e77d6 --- /dev/null +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/templates/inventory/kubelabels/assignkubelabel.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} +{% load i18n %} +{% block title %}{% trans "Assign Kubernetes Label" %}{% endblock %} + +{% block page_header %} + {% include "horizon/common/_page_header.html" with title=_("Assign Kubernetes Label") %} +{% endblock page_header %} + +{% block main %} + {% include "admin/inventory/kubelabels/_assignkubelabel.html" %} +{% endblock %} diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/urls.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/urls.py index b1984273..fe983db2 100755 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/urls.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/urls.py @@ -19,6 +19,8 @@ from starlingx_dashboard.dashboards.admin.inventory.interfaces.route import \ views as route_views from starlingx_dashboard.dashboards.admin.inventory.interfaces import \ views as interface_views +from starlingx_dashboard.dashboards.admin.inventory.kubernetes_labels import \ + views as label_views from starlingx_dashboard.dashboards.admin.inventory.lldp import \ views as lldp_views from starlingx_dashboard.dashboards.admin.inventory.memories import \ @@ -150,5 +152,8 @@ urlpatterns = [ url(r'^(?P[^/]+)/storages/(?P[^/]+)/' 'editpartition/$', storage_views.EditPartitionView.as_view(), - name='editpartition') + name='editpartition'), + url(r'^(?P[^/]+)/assignlabel/$', + label_views.AssignLabelView.as_view(), + name='assignlabel') ] diff --git a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/views.py b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/views.py index 6e256f4e..4dad7bb4 100755 --- a/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/views.py +++ b/starlingx-dashboard/starlingx-dashboard/starlingx_dashboard/dashboards/admin/inventory/views.py @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2018 Wind River Systems, Inc. +# Copyright (c) 2013-2019 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # @@ -172,6 +172,10 @@ class DetailView(tabs.TabbedTableView): host.sensorgroups = stx_api.sysinv.host_sensorgroup_list( self.request, host.uuid) + # Get K8s labels + host.labels = stx_api.sysinv.host_label_list(self.request, + host.uuid) + # Add patching status data to hosts phost = stx_api.patch.get_host(self.request, host.hostname) if phost is not None: