blob: c796962f74b3ca56218b5f0cc48d42c149b6d636 [file] [log] [blame]
Priyanka H Mfa5b77a2018-07-27 12:43:44 +05301/*
2 * Copyright 2015-present Open Networking Foundation
3 *
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
75@Directive({
76 selector: '[onosClusterDetails]',
77})
78
79export class ClusterDetailsDirective extends DetailsPanelBaseImpl implements OnInit, OnDestroy, OnChanges {
80 @Input() id: string;
81 @Output() closeEvent = new EventEmitter<string>();
82
83 lionFn; // Function
84
85 constructor(protected fs: FnService,
86 protected ls: LoadingService,
87 protected is: IconService,
88 protected lion: LionService,
89 protected wss: WebSocketService,
90 protected log: LogService,
91 protected mast: MastService,
92 protected ps: PanelService,
93 protected el: ElementRef,
94 @Inject('Window') private w: Window) {
95 super(fs, ls, log, wss, 'cluster');
96
97 if (this.lion.ubercache.length === 0) {
98 this.lionFn = this.dummyLion;
99 this.lion.loadCbs.set('clusterdetails', () => this.doLion());
100 } else {
101 this.doLion();
102 }
103 this.log.debug('ClusterDetailsDirective constructed');
104 }
105
106 ngOnInit() {
107 this.init();
108 this.initPanel();
109 this.log.debug('Cluster Details Component initialized');
110 }
111
112 /**
113 * Stop listening to clusterDetailsResponse on WebSocket
114 */
115 ngOnDestroy() {
116 this.lion.loadCbs.delete('clusterdetails');
117 this.destroy();
118 this.ps.destroyPanel(pName);
119 this.log.debug('Cluster Details Component destroyed');
120 }
121
122 @HostListener('window:resize', ['event'])
123 onResize(event: any) {
124 this.heightCalc();
125 this.populateDetails(this.detailsData);
126 return {
127 h: this.w.innerHeight,
128 w: this.w.innerWidth
129 };
130 }
131
132 @HostListener('document:click', ['$event'])
133 onClick(event) {
134 if (event.path !== undefined) {
135 for (let i = 0; i < event.path.length; i++) {
136 if (event.path[i].className === 'close-btn') {
137 this.close();
138 break;
139 }
140 }
141 } else if (event.target.href === undefined) {
142 if (event.target.parentNode.className === 'close-btn') {
143 this.close();
144 }
145 } else if (event.target.href.baseVal === '#xClose') {
146 this.close();
147 }
148 }
149
150 /**
151 * Details Panel Data Request on row selection changes
152 * Should be called whenever id changes
153 * If id is empty, no request is made
154 */
155 ngOnChanges() {
156 if (this.id === '') {
157 if (detailsPanel) {
158 detailsPanel.hide();
159 }
160 return '';
161 } else {
162 const query = {
163 'id': this.id
164 };
165 this.requestDetailsPanelData(query);
166 this.heightCalc();
167
168 /*
169 * Details data takes around 2ms to come up on web-socket
170 * putting a timeout interval of 5ms
171 */
172 setTimeout(() => {
173 this.populateDetails(this.detailsData);
174 detailsPanel.show();
175 }, 500);
176
177
178 }
179 }
180
181 doLion() {
182 this.lionFn = this.lion.bundle('core.view.Cluster');
183 }
184
185 heightCalc() {
186 pStartY = this.fs.noPxStyle(d3.select('.tabular-header'), 'height')
187 + this.mast.mastHeight + topPdg;
188 wSize = this.fs.windowSize(this.fs.noPxStyle(d3.select('.tabular-header'), 'height'));
189 pHeight = wSize.height;
190 }
191
192 createDetailsPane() {
193 detailsPanel = this.ps.createPanel(pName, {
194 width: wSize.width,
195 margin: 0,
196 hideMargin: 0,
197 });
198 detailsPanel.el().style('top', pStartY + 'px');
199 detailsPanel.el().style('position', 'absolute');
200 this.hidePanel = function () {
201 detailsPanel.hide();
202 };
203 detailsPanel.hide();
204 }
205
206 initPanel() {
207 this.heightCalc();
208 this.createDetailsPane();
209 }
210
211 populateDetails(details) {
212 this.setUpPanel();
213 this.populateTop(details);
214 this.populateBottom(details.devices);
215 detailsPanel.height(pHeight);
216 }
217
218 setUpPanel() {
219 let container, closeBtn;
220 detailsPanel.empty();
221
222 container = detailsPanel.append('div').classed('container', true);
223
224 top = container.append('div').classed('top', true);
225 closeBtn = top.append('div').classed('close-btn', true);
226 this.addCloseBtn(closeBtn);
227 iconDiv = top.append('div').classed('dev-icon', true);
228 top.append('h2');
229 topTable = top.append('div').classed('top-content', true)
230 .append('table');
231 top.append('hr');
232
233 bottom = container.append('div').classed('bottom', true);
234 bottom.append('h2').classed('devices-title', true).text('Devices');
235 bottom.append('table');
236 }
237
238 addCloseBtn(div) {
239 this.is.loadEmbeddedIcon(div, 'close', 20);
240 div.on('click', this.closePanel);
241 }
242
243 closePanel(): boolean {
244 if (detailsPanel.isVisible()) {
245 detailsPanel.hide();
246 return true;
247 }
248 return false;
249 }
250
251 populateTop(details) {
252 const propLabels = this.getLionProps();
253
254 this.is.loadEmbeddedIcon(iconDiv, 'node', 40);
255 top.select('h2').text(details.id);
256
257 const tbody = topTable.append('tbody');
258
259 propOrder.forEach(function (prop, i) {
260 addProp(tbody, propLabels[i], details[prop]);
261 });
262 }
263
264 getLionDeviceCols() {
265 return [
266 this.lionFn('uri'),
267 this.lionFn('type'),
268 this.lionFn('chassis_id'),
269 this.lionFn('vendor'),
270 this.lionFn('hw_version'),
271 this.lionFn('sw_version'),
272 this.lionFn('protocol'),
273 this.lionFn('serial_number'),
274 ];
275 }
276
277 populateBottom(devices) {
278 const table = bottom.select('table'),
279 theader = table.append('thead').append('tr'),
280 tbody = table.append('tbody');
281
282 let tbWidth, tbHeight;
283
284 this.getLionDeviceCols().forEach(function (col) {
285 theader.append('th').text(col);
286 });
287 if (devices !== undefined) {
288 devices.forEach(function (device) {
289 addDeviceRow(tbody, device);
290 });
291 }
292
293 tbWidth = this.fs.noPxStyle(tbody, 'width') + scrollSize;
294 tbHeight = pHeight
295 - (this.fs.noPxStyle(detailsPanel.el()
296 .select('.top'), 'height')
297 + this.fs.noPxStyle(detailsPanel.el()
298 .select('.devices-title'), 'height')
299 + portsTblPdg);
300
301 table.style('zIndex', '0');
302 table.style('height', tbHeight + 'px');
303 table.style('width', tbWidth + 'px');
304 table.style('overflow', 'auto');
305 table.style('display', 'block');
306
307 detailsPanel.width(tbWidth + ctnrPdg);
308 }
309
310 getLionProps() {
311 return [
312 this.lionFn('node_id'),
313 this.lionFn('ip_address'),
314 ];
315 }
316}