blob: db0b1e129daff3fc749e435fe36722327eec3ef9 [file] [log] [blame]
Priyanka H Mfa5b77a2018-07-27 12:43:44 +05301/*
Sean Condonf4f54a12018-10-10 23:25:46 +01002 * Copyright 2018-present Open Networking Foundation
Priyanka H Mfa5b77a2018-07-27 12:43:44 +05303 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16import {Directive, ElementRef, EventEmitter, Inject, Input, OnChanges, OnDestroy, OnInit, Output} from '@angular/core';
Sean Condon5ca00262018-09-06 17:55:25 +010017import {
18 FnService,
19 LogService,
20 MastService,
21 DetailsPanelBaseImpl,
Sean Condon5ca00262018-09-06 17:55:25 +010022 IconService,
23 LionService,
24 PanelService,
25 WebSocketService
Sean Condon3dd062f2020-04-14 09:25:00 +010026} from 'org_onosproject_onos/web/gui2-fw-lib/public_api';
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053027import * as d3 from 'd3';
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053028import {HostListener} from '@angular/core';
29
30// internal state
31let detailsPanel,
32 pStartY,
33 pHeight,
34 top,
35 topTable,
36 bottom,
37 iconDiv,
38 wSize;
39
40
41// constants
42const topPdg = 28,
43 ctnrPdg = 24,
44 scrollSize = 17,
45 portsTblPdg = 100,
46 pName = 'details-panel',
47 propOrder = [
48 'id', 'ip',
49 ],
50 deviceCols = [
51 'id', 'type', 'chassisid', 'mfr',
52 'hw', 'sw', 'protocol', 'serial',
53 ];
54
55function addProp(tbody, label, value) {
56 const tr = tbody.append('tr');
57
58 function addCell(cls, txt) {
59 tr.append('td').attr('class', cls).text(txt);
60 }
61
62 addCell('label', label + ' :');
63 addCell('value', value);
64}
65
66function addDeviceRow(tbody, device) {
67 const tr = tbody.append('tr');
68
69 deviceCols.forEach(function (col) {
70 tr.append('td').text(device[col]);
71 });
72}
73
Sean Condonf4f54a12018-10-10 23:25:46 +010074
75/**
76 * This should not be a directive - this should be a component, like all of the other details views
77 * Change it when time allows
78 */
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053079@Directive({
80 selector: '[onosClusterDetails]',
81})
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053082export class ClusterDetailsDirective extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
83 @Input() id: string;
84 @Output() closeEvent = new EventEmitter<string>();
85
86 lionFn; // Function
87
88 constructor(protected fs: FnService,
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053089 protected is: IconService,
90 protected lion: LionService,
91 protected wss: WebSocketService,
92 protected log: LogService,
93 protected mast: MastService,
94 protected ps: PanelService,
95 protected el: ElementRef,
96 @Inject('Window') private w: Window) {
Sean Condon95fb5742019-04-02 12:16:55 +010097 super(fs, log, wss, 'cluster');
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053098
99 if (this.lion.ubercache.length === 0) {
100 this.lionFn = this.dummyLion;
101 this.lion.loadCbs.set('clusterdetails', () => this.doLion());
102 } else {
103 this.doLion();
104 }
105 this.log.debug('ClusterDetailsDirective constructed');
106 }
107
108 ngOnInit() {
109 this.init();
110 this.initPanel();
111 this.log.debug('Cluster Details Component initialized');
112 }
113
114 /**
115 * Stop listening to clusterDetailsResponse on WebSocket
116 */
117 ngOnDestroy() {
118 this.lion.loadCbs.delete('clusterdetails');
119 this.destroy();
120 this.ps.destroyPanel(pName);
121 this.log.debug('Cluster Details Component destroyed');
122 }
123
124 @HostListener('window:resize', ['event'])
125 onResize(event: any) {
126 this.heightCalc();
127 this.populateDetails(this.detailsData);
128 return {
129 h: this.w.innerHeight,
130 w: this.w.innerWidth
131 };
132 }
133
134 @HostListener('document:click', ['$event'])
135 onClick(event) {
136 if (event.path !== undefined) {
137 for (let i = 0; i < event.path.length; i++) {
138 if (event.path[i].className === 'close-btn') {
139 this.close();
140 break;
141 }
142 }
143 } else if (event.target.href === undefined) {
144 if (event.target.parentNode.className === 'close-btn') {
145 this.close();
146 }
147 } else if (event.target.href.baseVal === '#xClose') {
148 this.close();
149 }
150 }
151
152 /**
153 * Details Panel Data Request on row selection changes
154 * Should be called whenever id changes
155 * If id is empty, no request is made
156 */
157 ngOnChanges() {
158 if (this.id === '') {
159 if (detailsPanel) {
160 detailsPanel.hide();
161 }
162 return '';
163 } else {
164 const query = {
165 'id': this.id
166 };
167 this.requestDetailsPanelData(query);
168 this.heightCalc();
169
170 /*
171 * Details data takes around 2ms to come up on web-socket
172 * putting a timeout interval of 5ms
173 */
174 setTimeout(() => {
175 this.populateDetails(this.detailsData);
176 detailsPanel.show();
177 }, 500);
178
179
180 }
181 }
182
183 doLion() {
184 this.lionFn = this.lion.bundle('core.view.Cluster');
185 }
186
187 heightCalc() {
188 pStartY = this.fs.noPxStyle(d3.select('.tabular-header'), 'height')
189 + this.mast.mastHeight + topPdg;
190 wSize = this.fs.windowSize(this.fs.noPxStyle(d3.select('.tabular-header'), 'height'));
191 pHeight = wSize.height;
192 }
193
194 createDetailsPane() {
195 detailsPanel = this.ps.createPanel(pName, {
196 width: wSize.width,
197 margin: 0,
198 hideMargin: 0,
199 });
200 detailsPanel.el().style('top', pStartY + 'px');
201 detailsPanel.el().style('position', 'absolute');
202 this.hidePanel = function () {
203 detailsPanel.hide();
204 };
205 detailsPanel.hide();
206 }
207
208 initPanel() {
209 this.heightCalc();
210 this.createDetailsPane();
211 }
212
213 populateDetails(details) {
214 this.setUpPanel();
215 this.populateTop(details);
216 this.populateBottom(details.devices);
217 detailsPanel.height(pHeight);
218 }
219
220 setUpPanel() {
221 let container, closeBtn;
222 detailsPanel.empty();
223
224 container = detailsPanel.append('div').classed('container', true);
225
226 top = container.append('div').classed('top', true);
227 closeBtn = top.append('div').classed('close-btn', true);
228 this.addCloseBtn(closeBtn);
229 iconDiv = top.append('div').classed('dev-icon', true);
230 top.append('h2');
231 topTable = top.append('div').classed('top-content', true)
232 .append('table');
233 top.append('hr');
234
235 bottom = container.append('div').classed('bottom', true);
236 bottom.append('h2').classed('devices-title', true).text('Devices');
237 bottom.append('table');
238 }
239
240 addCloseBtn(div) {
Sean Condonc687ccb2019-10-25 12:27:54 +0100241 // This whole cluster app needs to be changed over to the Angular 7 style
242 // It is the only one remaining that uses the d3 structure
243 // this.is.loadEmbeddedIcon(div, 'close', 20);
Priyanka H Mfa5b77a2018-07-27 12:43:44 +0530244 div.on('click', this.closePanel);
245 }
246
247 closePanel(): boolean {
248 if (detailsPanel.isVisible()) {
249 detailsPanel.hide();
250 return true;
251 }
252 return false;
253 }
254
255 populateTop(details) {
256 const propLabels = this.getLionProps();
257
Sean Condonc687ccb2019-10-25 12:27:54 +0100258 // This whole cluster app needs to be changed over to the Angular 7 style
259 // It is the only one remaining that uses the d3 structure
260 // this.is.loadEmbeddedIcon(iconDiv, 'node', 40);
Priyanka H Mfa5b77a2018-07-27 12:43:44 +0530261 top.select('h2').text(details.id);
262
263 const tbody = topTable.append('tbody');
264
265 propOrder.forEach(function (prop, i) {
266 addProp(tbody, propLabels[i], details[prop]);
267 });
268 }
269
270 getLionDeviceCols() {
271 return [
272 this.lionFn('uri'),
273 this.lionFn('type'),
274 this.lionFn('chassis_id'),
275 this.lionFn('vendor'),
276 this.lionFn('hw_version'),
277 this.lionFn('sw_version'),
278 this.lionFn('protocol'),
279 this.lionFn('serial_number'),
280 ];
281 }
282
283 populateBottom(devices) {
284 const table = bottom.select('table'),
285 theader = table.append('thead').append('tr'),
286 tbody = table.append('tbody');
287
288 let tbWidth, tbHeight;
289
290 this.getLionDeviceCols().forEach(function (col) {
291 theader.append('th').text(col);
292 });
293 if (devices !== undefined) {
294 devices.forEach(function (device) {
295 addDeviceRow(tbody, device);
296 });
297 }
298
299 tbWidth = this.fs.noPxStyle(tbody, 'width') + scrollSize;
300 tbHeight = pHeight
301 - (this.fs.noPxStyle(detailsPanel.el()
302 .select('.top'), 'height')
303 + this.fs.noPxStyle(detailsPanel.el()
304 .select('.devices-title'), 'height')
305 + portsTblPdg);
306
307 table.style('zIndex', '0');
308 table.style('height', tbHeight + 'px');
309 table.style('width', tbWidth + 'px');
310 table.style('overflow', 'auto');
311 table.style('display', 'block');
312
313 detailsPanel.width(tbWidth + ctnrPdg);
314 }
315
316 getLionProps() {
317 return [
318 this.lionFn('node_id'),
319 this.lionFn('ip_address'),
320 ];
321 }
322}