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