From 67cf6e1c04b7ac1d56ff23149639876f74bc2bdb Mon Sep 17 00:00:00 2001 From: Lennart Regebro Date: Wed, 19 Nov 2014 19:24:36 +0100 Subject: [PATCH] Reimplement the Satellite integration in Python The AngularJS implementation proved hard to maintain, and brittle. Angulars obtuse error messages did not help. Reimplementing this in Python with standard Horizon tables proved faster than trying to solve the issues with the JS implementation. Change-Id: I3cd80d14505b278273b19bfd6b875326c3b72f6a --- tuskar_sat_ui/nodes/panel.py | 1 + tuskar_sat_ui/nodes/tables.py | 36 ++++ tuskar_sat_ui/nodes/tabs.py | 76 ++++++++ tuskar_sat_ui/nodes/views.py | 3 +- .../js/angular/horizon.base64.js | 84 -------- .../js/angular/horizon.node_errata.js | 96 --------- .../nodes/_detail_overview_sat.html | 17 ++ .../infrastructure/nodes/sat_detail.html | 183 ------------------ 8 files changed, 132 insertions(+), 364 deletions(-) create mode 100644 tuskar_sat_ui/nodes/tables.py create mode 100644 tuskar_sat_ui/nodes/tabs.py delete mode 100644 tuskar_sat_ui/static/infrastructure/js/angular/horizon.base64.js delete mode 100644 tuskar_sat_ui/static/infrastructure/js/angular/horizon.node_errata.js create mode 100644 tuskar_sat_ui/templates/infrastructure/nodes/_detail_overview_sat.html delete mode 100644 tuskar_sat_ui/templates/infrastructure/nodes/sat_detail.html diff --git a/tuskar_sat_ui/nodes/panel.py b/tuskar_sat_ui/nodes/panel.py index f45f37f..0a1c02e 100644 --- a/tuskar_sat_ui/nodes/panel.py +++ b/tuskar_sat_ui/nodes/panel.py @@ -18,6 +18,7 @@ import horizon from tuskar_ui.infrastructure import dashboard from tuskar_ui.infrastructure.nodes.panel import Nodes as TuskarNodes + class Nodes(horizon.Panel): name = _("Nodes") slug = "nodes" diff --git a/tuskar_sat_ui/nodes/tables.py b/tuskar_sat_ui/nodes/tables.py new file mode 100644 index 0000000..23d8c9a --- /dev/null +++ b/tuskar_sat_ui/nodes/tables.py @@ -0,0 +1,36 @@ +# -*- coding: utf8 -*- +# +# 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. +from django.utils.translation import ugettext_lazy as _ +from horizon import tables + + +def get_errata_link(errata): + return "http://www.google.com" + + +class ErrataTable(tables.DataTable): + title = tables.Column('title', + link=get_errata_link, + verbose_name=_("Title")) + type = tables.Column('type', + verbose_name=_("Type")) + id = tables.Column('id', + verbose_name=_("Errata ID")) + issued = tables.Column('issued', + verbose_name=_("Date Issued")) + + class Meta: + name = "erratatable" + verbose_name = _("Errata") + template = "horizon/common/_enhanced_data_table.html" diff --git a/tuskar_sat_ui/nodes/tabs.py b/tuskar_sat_ui/nodes/tabs.py new file mode 100644 index 0000000..81ef263 --- /dev/null +++ b/tuskar_sat_ui/nodes/tabs.py @@ -0,0 +1,76 @@ +# -*- coding: utf8 -*- +# +# 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. +import requests +import urllib + +from collections import namedtuple +from horizon import tabs +from tuskar_ui.infrastructure.nodes import tabs as nodes_tabs +from .tables import ErrataTable + + +ErrataItem = namedtuple('ErrataItem', ['title', 'type', 'id', 'issued']) + + +class DetailOverviewTab(nodes_tabs.DetailOverviewTab): + template_name = 'infrastructure/nodes/_detail_overview_sat.html' + + def get_context_data(self, request): + result = super(DetailOverviewTab, self).get_context_data(request) + if result['node'].uuid is None: + return result + + # Some currently hardcoded values: + mac = '"52:54:00:4F:D8:65"' # Hardcode for now + host = 'http://sat-perf-04.idm.lab.bos.redhat.com' # Hardcode for now + auth = ('admin', 'changeme') + + # Get the errata here + host = host.strip('/') # Get rid of any trailing slash in the host url + + # Pick up the UUID from the MAC address This makes no sense, as we + # need both MAC address and the interface, and we don't have the + # interface, so we need to make multiple slow searches. If the + # Satellite UUID isn't the same as this one, and it probably isn't we + # need to store a mapping somewhere. + url = '{host}/katello/api/v2/systems'.format(host=host) + for interface in ['eth0', 'eth1', 'en0', 'en1']: + + q = 'facts.net.interface.{iface}.mac_address:{mac}'.format( + iface=interface, mac=mac) + r = requests.get(url, params={'search': q}, auth=auth) + results = r.json()['results'] + if results: + break + else: + # No node found + result['errata'] = None + return result + + uuid = results[0]['uuid'] + errata_url = '{host}/katello/api/v2/systems/{id}/errata' + r = requests.get(errata_url.format(host=host, id=uuid), auth=auth) + errata = r.json()['results'] + if not errata: + result['errata'] = None + else: + data = [ErrataItem(x['title'], x['type'], x['id'], x['issued']) + for x in errata] + result['errata'] = ErrataTable(request, data=data) + return result + + +class NodeDetailTabs(tabs.TabGroup): + slug = "node_details" + tabs = (DetailOverviewTab,) diff --git a/tuskar_sat_ui/nodes/views.py b/tuskar_sat_ui/nodes/views.py index de9a45b..df975a2 100644 --- a/tuskar_sat_ui/nodes/views.py +++ b/tuskar_sat_ui/nodes/views.py @@ -13,7 +13,8 @@ # under the License. from tuskar_ui.infrastructure.nodes import views +from tuskar_sat_ui.nodes import tabs class DetailView(views.DetailView): - template_name = 'infrastructure/nodes/sat_detail.html' + tab_group_class = tabs.NodeDetailTabs diff --git a/tuskar_sat_ui/static/infrastructure/js/angular/horizon.base64.js b/tuskar_sat_ui/static/infrastructure/js/angular/horizon.base64.js deleted file mode 100644 index 75036e0..0000000 --- a/tuskar_sat_ui/static/infrastructure/js/angular/horizon.base64.js +++ /dev/null @@ -1,84 +0,0 @@ -angular.module('hz').factory('Base64', function() { - var keyStr = 'ABCDEFGHIJKLMNOP' + - 'QRSTUVWXYZabcdef' + - 'ghijklmnopqrstuv' + - 'wxyz0123456789+/' + - '='; - return { - encode: function (input) { - var output = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - do { - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - output = output + - keyStr.charAt(enc1) + - keyStr.charAt(enc2) + - keyStr.charAt(enc3) + - keyStr.charAt(enc4); - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - } while (i < input.length); - - return output; - }, - - decode: function (input) { - var output = ""; - var chr1, chr2, chr3 = ""; - var enc1, enc2, enc3, enc4 = ""; - var i = 0; - - // remove all characters that are not A-Z, a-z, 0-9, +, /, or = - var base64test = /[^A-Za-z0-9\+\/\=]/g; - if (base64test.exec(input)) { - alert("There were invalid base64 characters in the input text.\n" + - "Valid base64 characters are A-Z, a-z, 0-9, '+', '/',and '='\n" + - "Expect errors in decoding."); - } - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - do { - enc1 = keyStr.indexOf(input.charAt(i++)); - enc2 = keyStr.indexOf(input.charAt(i++)); - enc3 = keyStr.indexOf(input.charAt(i++)); - enc4 = keyStr.indexOf(input.charAt(i++)); - - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - output = output + String.fromCharCode(chr1); - - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } - - chr1 = chr2 = chr3 = ""; - enc1 = enc2 = enc3 = enc4 = ""; - - } while (i < input.length); - - return output; - } - }; - }); diff --git a/tuskar_sat_ui/static/infrastructure/js/angular/horizon.node_errata.js b/tuskar_sat_ui/static/infrastructure/js/angular/horizon.node_errata.js deleted file mode 100644 index bcd2ad0..0000000 --- a/tuskar_sat_ui/static/infrastructure/js/angular/horizon.node_errata.js +++ /dev/null @@ -1,96 +0,0 @@ -angular.module('hz').factory - ('SatelliteErrata', ['$resource', 'Base64', - function ($resource, Base64) { - - var getAuthToken = function(username, password) { - var tokenize = username + ':' + password; - tokenize = Base64.encode(tokenize); - return "Basic " + tokenize; - }; - - var SatelliteErrata = $resource('https://sat-perf-05.idm.lab.bos.redhat.com/katello/api/v2/systems/:id/errata', {id: '@uuid'}, { - get: { - method: 'GET', - isArray: false, - headers: { 'Authorization': getAuthToken('admin', 'changeme') } - } - } - ); - - return SatelliteErrata; - - }]); - -angular.module('hz').directive({ - satelliteErrata: [ function () { - - return { - restrict: 'A', - // require: '^file', - transclude: true, - scope: { - uuid: '=' - }, - controller: ['$scope', 'SatelliteErrata', '$http', 'Base64', 'ngTableParams', '$filter', - function ($scope, SatelliteErrata, $http, Base64, ngTableParams, $filter) { - - var baseUrl = 'https://sat-perf-05.idm.lab.bos.redhat.com'; - - var defaultParams = function (data) { - var params = new ngTableParams({ - page: 1, // show first page - count: 10 // count per page - - }, { - total: data.length, // length of data - getData: function($defer, params) { - var filteredData = params.filter() ? - $filter('filter')(data, params.filter()) : - data; - var orderedData = params.sorting() ? - $filter('orderBy')(filteredData, params.orderBy()) : - data; - $defer.resolve(orderedData.slice((params.page() - 1) * params.count(), params.page() * params.count())); - } - }); - - return params; - }; - - $scope.errataLink = function (errata) { - return baseUrl + '/content_hosts/' + $scope.uuid + '/errata/' + errata.errata_id; - }; - - var payload = SatelliteErrata.get({id: $scope.uuid}); - payload.$promise.then( - function() { - $scope.errata = payload.results - $scope.errataParams = defaultParams($scope.errata); - }); - - - }], - template: - '\n' + - '\n' + - '\n' + - '\n' + - '\n' + - '\n' + - '\n'+ - '
\n' + - '{{e.title}}\n' + - '{{ e.type }}{{ e.errata_id }}{{ e.issued }}
\n', - link: function (scope, element, attrs, modelCtrl, transclude) { - scope.modelCtrl = modelCtrl; - scope.$transcludeFn = transclude; - } - }; - }] -}); - -angular.module('hz').controller({ - ErrataController: ['$scope', - function ($scope ) { - - }]}); diff --git a/tuskar_sat_ui/templates/infrastructure/nodes/_detail_overview_sat.html b/tuskar_sat_ui/templates/infrastructure/nodes/_detail_overview_sat.html new file mode 100644 index 0000000..e51042f --- /dev/null +++ b/tuskar_sat_ui/templates/infrastructure/nodes/_detail_overview_sat.html @@ -0,0 +1,17 @@ +{% extends "infrastructure/nodes/_detail_overview.html" %} +{% load i18n %} + +{% block additional_data %} + +{% if node.uuid %} +
+

{% trans "Errata" %}

+ {% if errata %} + {{ errata.render }} + {% else %} +

{% trans "No errata found" %}

+ {% endif %} +
+{% endif %} + +{% endblock %} diff --git a/tuskar_sat_ui/templates/infrastructure/nodes/sat_detail.html b/tuskar_sat_ui/templates/infrastructure/nodes/sat_detail.html deleted file mode 100644 index 71cfc53..0000000 --- a/tuskar_sat_ui/templates/infrastructure/nodes/sat_detail.html +++ /dev/null @@ -1,183 +0,0 @@ -{% extends 'infrastructure/base.html' %} -{% load i18n %} -{% block title %}{{ title }}{% endblock %} - -{% block page_header %} - {% include 'horizon/common/_page_header.html' with title=title %} -{% endblock page_header %} - -{% block js %} -{{ block.super }} - - -{% endblock %} - -{% block main %} -
-
-
- {% trans "Powered" %} {{ node.power_state|default:"—" }} -
-
- -
-

{% trans "Deployment" %}

-
-
{% trans "Deployment Role" %}
- {% if stack and role %} -
{{ role.name }}
- {% else %} -
- {% endif %} -
{% trans "Provisioning" %}
-
- {{ node.provisioning_status|default:"—" }} - {% if node.instance_uuid %} -
{{ node.instance.created }} - {% endif %} -
-
{% trans "Image" %}
-
{{ node.image_name|default:"—" }}
-
{% trans "Instance UUID" %}
-
{{ node.instance_uuid|default:"—" }}
-
-
- -
-

{% trans "Inventory" %}

-
-
{% trans "Node UUID" %}
-
{{ node.uuid|default:"—" }}
-
{% trans "Driver" %}
- -
{% trans "Network Cards" %}
- -
{% trans "Registered HW" %}
-
- {{ node.cpus|default:"—" }} {% trans "CPU" %}
- {{ node.memory_mb|default:"—" }} {% trans "RAM (MB)" %}
- {{ node.local_gb|default:"—" }} {% trans "HDD (GB)" %} -
-
-
- -
-

{% trans "Performance and Capacity" %}

- -{% if meter_conf %} -
-{% url 'horizon:infrastructure:nodes:performance' node.uuid as node_perf_url %} - -
-
-
- -
- -
-
-
- -
- -
-
-
- -
- -
-
-
-
- - - -
- {% for meter_label, url_part, y_max in meter_conf %} -
- {% include "infrastructure/_performance_chart.html" with label=meter_label y_max=y_max url=node_perf_url|add:"?"|add:url_part only %} -
- {% endfor %} - - {% else %} - {% trans 'Metering service is not enabled.' %} - {% endif %} -
-
- -{% if node.uuid %} -
-
-
-

{% trans "Errata" %}

-
-
-
-
-{% endif %} - -{% endblock %}