blob: 17e5842b31f3ad7fa4f69c7da2dc333afe5cd34b [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,
22 LoadingService,
23 IconService,
24 LionService,
25 PanelService,
26 WebSocketService
27} from 'gui2-fw-lib';
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053028import * as d3 from 'd3';
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053029import {HostListener} from '@angular/core';
30
31// internal state
32let detailsPanel,
33 pStartY,
34 pHeight,
35 top,
36 topTable,
37 bottom,
38 iconDiv,
39 wSize;
40
41
42// constants
43const topPdg = 28,
44 ctnrPdg = 24,
45 scrollSize = 17,
46 portsTblPdg = 100,
47 pName = 'details-panel',
48 propOrder = [
49 'id', 'ip',
50 ],
51 deviceCols = [
52 'id', 'type', 'chassisid', 'mfr',
53 'hw', 'sw', 'protocol', 'serial',
54 ];
55
56function addProp(tbody, label, value) {
57 const tr = tbody.append('tr');
58
59 function addCell(cls, txt) {
60 tr.append('td').attr('class', cls).text(txt);
61 }
62
63 addCell('label', label + ' :');
64 addCell('value', value);
65}
66
67function addDeviceRow(tbody, device) {
68 const tr = tbody.append('tr');
69
70 deviceCols.forEach(function (col) {
71 tr.append('td').text(device[col]);
72 });
73}
74
Sean Condonf4f54a12018-10-10 23:25:46 +010075
76/**
77 * This should not be a directive - this should be a component, like all of the other details views
78 * Change it when time allows
79 */
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053080@Directive({
81 selector: '[onosClusterDetails]',
82})
Priyanka H Mfa5b77a2018-07-27 12:43:44 +053083export class ClusterDetailsDirective extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
84 @Input() id: string;
85 @Output() closeEvent = new EventEmitter<string>();
86
87 lionFn; // Function
88
89 constructor(protected fs: FnService,
90 protected ls: LoadingService,
91 protected is: IconService,
92 protected lion: LionService,
93 protected wss: WebSocketService,
94 protected log: LogService,
95 protected mast: MastService,
96 protected ps: PanelService,
97 protected el: ElementRef,
98 @Inject('Window') private w: Window) {
99 super(fs, ls, log, wss, 'cluster');
100
101 if (this.lion.ubercache.length === 0) {
102 this.lionFn = this.dummyLion;
103 this.lion.loadCbs.set('clusterdetails', () => this.doLion());
104 } else {
105 this.doLion();
106 }
107 this.log.debug('ClusterDetailsDirective constructed');
108 }
109
110 ngOnInit() {
111 this.init();
112 this.initPanel();
113 this.log.debug('Cluster Details Component initialized');
114 }
115
116 /**
117 * Stop listening to clusterDetailsResponse on WebSocket
118 */
119 ngOnDestroy() {
120 this.lion.loadCbs.delete('clusterdetails');
121 this.destroy();
122 this.ps.destroyPanel(pName);
123 this.log.debug('Cluster Details Component destroyed');
124 }
125
126 @HostListener('window:resize', ['event'])
127 onResize(event: any) {
128 this.heightCalc();
129 this.populateDetails(this.detailsData);
130 return {
131 h: this.w.innerHeight,
132 w: this.w.innerWidth
133 };
134 }
135
136 @HostListener('document:click', ['$event'])
137 onClick(event) {
138 if (event.path !== undefined) {
139 for (let i = 0; i < event.path.length; i++) {
140 if (event.path[i].className === 'close-btn') {
141 this.close();
142 break;
143 }
144 }
145 } else if (event.target.href === undefined) {
146 if (event.target.parentNode.className === 'close-btn') {
147 this.close();
148 }
149 } else if (event.target.href.baseVal === '#xClose') {
150 this.close();
151 }
152 }
153
154 /**
155 * Details Panel Data Request on row selection changes
156 * Should be called whenever id changes
157 * If id is empty, no request is made
158 */
159 ngOnChanges() {
160 if (this.id === '') {
161 if (detailsPanel) {
162 detailsPanel.hide();
163 }
164 return '';
165 } else {
166 const query = {
167 'id': this.id
168 };
169 this.requestDetailsPanelData(query);
170 this.heightCalc();
171
172 /*
173 * Details data takes around 2ms to come up on web-socket
174 * putting a timeout interval of 5ms
175 */
176 setTimeout(() => {
177 this.populateDetails(this.detailsData);
178 detailsPanel.show();
179 }, 500);
180
181
182 }
183 }
184
185 doLion() {
186 this.lionFn = this.lion.bundle('core.view.Cluster');
187 }
188
189 heightCalc() {
190 pStartY = this.fs.noPxStyle(d3.select('.tabular-header'), 'height')
191 + this.mast.mastHeight + topPdg;
192 wSize = this.fs.windowSize(this.fs.noPxStyle(d3.select('.tabular-header'), 'height'));
193 pHeight = wSize.height;
194 }
195
196 createDetailsPane() {
197 detailsPanel = this.ps.createPanel(pName, {
198 width: wSize.width,
199 margin: 0,
200 hideMargin: 0,
201 });
202 detailsPanel.el().style('top', pStartY + 'px');
203 detailsPanel.el().style('position', 'absolute');
204 this.hidePanel = function () {
205 detailsPanel.hide();
206 };
207 detailsPanel.hide();
208 }
209
210 initPanel() {
211 this.heightCalc();
212 this.createDetailsPane();
213 }
214
215 populateDetails(details) {
216 this.setUpPanel();
217 this.populateTop(details);
218 this.populateBottom(details.devices);
219 detailsPanel.height(pHeight);
220 }
221
222 setUpPanel() {
223 let container, closeBtn;
224 detailsPanel.empty();
225
226 container = detailsPanel.append('div').classed('container', true);
227
228 top = container.append('div').classed('top', true);
229 closeBtn = top.append('div').classed('close-btn', true);
230 this.addCloseBtn(closeBtn);
231 iconDiv = top.append('div').classed('dev-icon', true);
232 top.append('h2');
233 topTable = top.append('div').classed('top-content', true)
234 .append('table');
235 top.append('hr');
236
237 bottom = container.append('div').classed('bottom', true);
238 bottom.append('h2').classed('devices-title', true).text('Devices');
239 bottom.append('table');
240 }
241
242 addCloseBtn(div) {
243 this.is.loadEmbeddedIcon(div, 'close', 20);
244 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
258 this.is.loadEmbeddedIcon(iconDiv, 'node', 40);
259 top.select('h2').text(details.id);
260
261 const tbody = topTable.append('tbody');
262
263 propOrder.forEach(function (prop, i) {
264 addProp(tbody, propLabels[i], details[prop]);
265 });
266 }
267
268 getLionDeviceCols() {
269 return [
270 this.lionFn('uri'),
271 this.lionFn('type'),
272 this.lionFn('chassis_id'),
273 this.lionFn('vendor'),
274 this.lionFn('hw_version'),
275 this.lionFn('sw_version'),
276 this.lionFn('protocol'),
277 this.lionFn('serial_number'),
278 ];
279 }
280
281 populateBottom(devices) {
282 const table = bottom.select('table'),
283 theader = table.append('thead').append('tr'),
284 tbody = table.append('tbody');
285
286 let tbWidth, tbHeight;
287
288 this.getLionDeviceCols().forEach(function (col) {
289 theader.append('th').text(col);
290 });
291 if (devices !== undefined) {
292 devices.forEach(function (device) {
293 addDeviceRow(tbody, device);
294 });
295 }
296
297 tbWidth = this.fs.noPxStyle(tbody, 'width') + scrollSize;
298 tbHeight = pHeight
299 - (this.fs.noPxStyle(detailsPanel.el()
300 .select('.top'), 'height')
301 + this.fs.noPxStyle(detailsPanel.el()
302 .select('.devices-title'), 'height')
303 + portsTblPdg);
304
305 table.style('zIndex', '0');
306 table.style('height', tbHeight + 'px');
307 table.style('width', tbWidth + 'px');
308 table.style('overflow', 'auto');
309 table.style('display', 'block');
310
311 detailsPanel.width(tbWidth + ctnrPdg);
312 }
313
314 getLionProps() {
315 return [
316 this.lionFn('node_id'),
317 this.lionFn('ip_address'),
318 ];
319 }
320}