blob: 73653106b2c3c2e6199ac27c3ae1128d3bc945ee [file] [log] [blame]
Sean Condonf4f54a12018-10-10 23:25:46 +01001/*
2 * Copyright 2018-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 */
Sean Condon0c577f62018-11-18 22:40:05 +000016import {
17 Component,
18 OnDestroy,
Sean Condon021f0fa2018-12-06 23:31:11 -080019 OnInit, SimpleChange,
Sean Condon0c577f62018-11-18 22:40:05 +000020 ViewChild
21} from '@angular/core';
Sean Condon55c30532018-10-29 12:26:57 +000022import * as d3 from 'd3';
Sean Condonf4f54a12018-10-10 23:25:46 +010023import {
24 FnService,
Sean Condon0c577f62018-11-18 22:40:05 +000025 KeysService,
26 KeysToken,
27 LogService,
28 PrefsService,
29 SvgUtilService,
30 WebSocketService,
Sean Condon0c577f62018-11-18 22:40:05 +000031 ZoomService
Sean Condonf4f54a12018-10-10 23:25:46 +010032} from 'gui2-fw-lib';
Sean Condon0c577f62018-11-18 22:40:05 +000033import {InstanceComponent} from '../panel/instance/instance.component';
34import {SummaryComponent} from '../panel/summary/summary.component';
35import {DetailsComponent} from '../panel/details/details.component';
36import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
37import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
38import {TopologyService} from '../topology.service';
Sean Condon021f0fa2018-12-06 23:31:11 -080039import {HostLabelToggle, LabelToggle, Node} from '../layer/forcesvg/models';
Sean Condon50855cf2018-12-23 15:37:42 +000040import {ToolbarComponent} from '../panel/toolbar/toolbar.component';
41import {TrafficService} from '../traffic.service';
Sean Condonf4f54a12018-10-10 23:25:46 +010042
43/**
44 * ONOS GUI Topology View
45 *
46 * This Topology View component is the top level component in a hierarchy that
47 * comprises the whole Topology View
48 *
49 * There are three main parts (panels, graphical and breadcrumbs)
50 * The panel hierarchy
51 * |-- Instances Panel (shows ONOS instances)
52 * |-- Summary Panel (summary of ONOS)
53 * |-- Toolbar Panel (the toolbar)
54 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
55 *
56 * The graphical hierarchy contains
57 * Topology (this)
58 * |-- No Devices Connected (only of there are no nodes to show)
59 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
60 * |-- Background (container for any backgrounds - can be toggled on and off)
61 * |-- Map
62 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
63 *
64 * The breadcrumbs
65 * |-- Breadcrumb (in region view a way of navigating back up through regions)
66 */
67@Component({
68 selector: 'onos-topology',
69 templateUrl: './topology.component.html',
70 styleUrls: ['./topology.component.css']
71})
Sean Condonaa4366d2018-11-02 14:29:01 +000072export class TopologyComponent implements OnInit, OnDestroy {
73 // These are references to the components inserted in the template
Sean Condonf4f54a12018-10-10 23:25:46 +010074 @ViewChild(InstanceComponent) instance: InstanceComponent;
75 @ViewChild(SummaryComponent) summary: SummaryComponent;
76 @ViewChild(DetailsComponent) details: DetailsComponent;
Sean Condon50855cf2018-12-23 15:37:42 +000077 @ViewChild(ToolbarComponent) toolbar: ToolbarComponent;
Sean Condonaa4366d2018-11-02 14:29:01 +000078 @ViewChild(BackgroundSvgComponent) background: BackgroundSvgComponent;
79 @ViewChild(ForceSvgComponent) force: ForceSvgComponent;
Sean Condonf4f54a12018-10-10 23:25:46 +010080
81 flashMsg: string = '';
82 prefsState = {};
Sean Condonf4f54a12018-10-10 23:25:46 +010083 hostLabelIdx: number = 1;
Sean Condon50855cf2018-12-23 15:37:42 +000084 showBackground: boolean = false;
Sean Condon55c30532018-10-29 12:26:57 +000085
Sean Condonf4f54a12018-10-10 23:25:46 +010086 constructor(
87 protected log: LogService,
88 protected fs: FnService,
89 protected ks: KeysService,
90 protected sus: SvgUtilService,
91 protected ps: PrefsService,
Sean Condon55c30532018-10-29 12:26:57 +000092 protected wss: WebSocketService,
Sean Condonaa4366d2018-11-02 14:29:01 +000093 protected zs: ZoomService,
94 protected ts: TopologyService,
Sean Condon50855cf2018-12-23 15:37:42 +000095 protected trs: TrafficService
Sean Condonf4f54a12018-10-10 23:25:46 +010096 ) {
97
98 this.log.debug('Topology component constructed');
99 }
100
Sean Condon021f0fa2018-12-06 23:31:11 -0800101 private static deviceLabelFlashMessage(index: number): string {
102 switch (index) {
103 case 0: return 'Hide device labels';
104 case 1: return 'Show friendly device labels';
105 case 2: return 'Show device ID labels';
106 }
107 }
108
109 private static hostLabelFlashMessage(index: number): string {
110 switch (index) {
111 case 0: return 'Hide host labels';
112 case 1: return 'Show friendly host labels';
113 case 2: return 'Show host IP labels';
114 case 3: return 'Show host MAC Address labels';
115 }
116 }
117
Sean Condonf4f54a12018-10-10 23:25:46 +0100118 ngOnInit() {
119 this.bindCommands();
Sean Condonaa4366d2018-11-02 14:29:01 +0000120 // The components from the template are handed over to TopologyService here
121 // so that WebSocket responses can be passed back in to them
122 // The handling of the WebSocket call is delegated out to the Topology
123 // Service just to compartmentalize things a bit
124 this.ts.init(this.instance, this.background, this.force);
Sean Condonf4f54a12018-10-10 23:25:46 +0100125 this.log.debug('Topology component initialized');
126 }
127
Sean Condonaa4366d2018-11-02 14:29:01 +0000128 ngOnDestroy() {
129 this.ts.destroy();
130 this.log.debug('Topology component destroyed');
131 }
132
Sean Condonf4f54a12018-10-10 23:25:46 +0100133 actionMap() {
134 return {
Sean Condon50855cf2018-12-23 15:37:42 +0000135 A: [() => {this.monitorAllTraffic(); }, 'Monitor all traffic'],
Sean Condonf4f54a12018-10-10 23:25:46 +0100136 L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
137 B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
138 D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
139 I: [(token) => {this.toggleInstancePanel(token); }, 'Toggle ONOS Instance Panel'],
140 O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
141 R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
142 P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
143 E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
144 X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
145 U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
146 H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
147 M: [() => {this.toggleOfflineDevices(); }, 'Toggle offline visibility'],
148 dot: [() => {this.toggleToolbar(); }, 'Toggle Toolbar'],
Sean Condon50855cf2018-12-23 15:37:42 +0000149 0: [() => {this.cancelTraffic(); }, 'Cancel traffic monitoring'],
Sean Condonf4f54a12018-10-10 23:25:46 +0100150 'shift-L': [() => {this.cycleHostLabels(); }, 'Cycle host labels'],
151
152 // -- instance color palette debug
Sean Condon55c30532018-10-29 12:26:57 +0000153 9: () => {
154 this.sus.cat7().testCard(d3.select('svg#topo2'));
Sean Condonf4f54a12018-10-10 23:25:46 +0100155 },
156
157 esc: this.handleEscape,
158
159 // TODO update after adding in Background Service
160 // topology overlay selections
161 // F1: function () { t2tbs.fnKey(0); },
162 // F2: function () { t2tbs.fnKey(1); },
163 // F3: function () { t2tbs.fnKey(2); },
164 // F4: function () { t2tbs.fnKey(3); },
165 // F5: function () { t2tbs.fnKey(4); },
166 //
167 // _keyListener: t2tbs.keyListener.bind(t2tbs),
168
169 _helpFormat: [
170 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
171 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
172 [], // this column reserved for overlay actions
173 ],
174 };
175 }
176
177
178 bindCommands(additional?: any) {
179
180 const am = this.actionMap();
181 const add = this.fs.isO(additional);
182
Sean Condonf4f54a12018-10-10 23:25:46 +0100183 this.ks.keyBindings(am);
184
185 this.ks.gestureNotes([
186 ['click', 'Select the item and show details'],
187 ['shift-click', 'Toggle selection state'],
188 ['drag', 'Reposition (and pin) device / host'],
189 ['cmd-scroll', 'Zoom in / out'],
190 ['cmd-drag', 'Pan'],
191 ]);
192 }
193
194 handleEscape() {
195
196 if (false) {
197 // TODO: Cancel show mastership
198 // TODO: Cancel Active overlay
199 // TODO: Reinstate with components
200 } else {
201 this.log.debug('Handling escape');
202 // } else if (t2rs.deselectAllNodes()) {
203 // // else if we have node selections, deselect them all
204 // // (work already done)
205 // } else if (t2rs.deselectLink()) {
206 // // else if we have a link selection, deselect it
207 // // (work already done)
208 // } else if (t2is.isVisible()) {
209 // // If the instance panel is visible, close it
210 // t2is.toggle();
211 // } else if (t2sp.isVisible()) {
212 // // If the summary panel is visible, close it
213 // t2sp.toggle();
214 }
215 }
216
217
218
219 updatePrefsState(what, b) {
220 this.prefsState[what] = b ? 1 : 0;
221 this.ps.setPrefs('topo2_prefs', this.prefsState);
222 }
223
Sean Condonf4f54a12018-10-10 23:25:46 +0100224 protected cycleDeviceLabels() {
Sean Condon021f0fa2018-12-06 23:31:11 -0800225 const old: LabelToggle = this.force.deviceLabelToggle;
226 const next = LabelToggle.next(old);
227 this.force.ngOnChanges({'deviceLabelToggle':
228 new SimpleChange(old, next, false)});
229 this.flashMsg = TopologyComponent.deviceLabelFlashMessage(next);
230 this.log.debug('Cycling device labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100231 }
232
233 protected cycleHostLabels() {
Sean Condon021f0fa2018-12-06 23:31:11 -0800234 const old: HostLabelToggle = this.force.hostLabelToggle;
235 const next = HostLabelToggle.next(old);
236 this.force.ngOnChanges({'hostLabelToggle':
237 new SimpleChange(old, next, false)});
238 this.flashMsg = TopologyComponent.hostLabelFlashMessage(next);
239 this.log.debug('Cycling host labels', old, next);
Sean Condonf4f54a12018-10-10 23:25:46 +0100240 }
241
242 protected toggleBackground(token: KeysToken) {
243 this.flashMsg = 'Toggling background';
Sean Condon50855cf2018-12-23 15:37:42 +0000244 this.showBackground = !this.showBackground;
Sean Condonf4f54a12018-10-10 23:25:46 +0100245 this.log.debug('Toggling background', token);
246 // TODO: Reinstate with components
247 // t2bgs.toggle(x);
248 }
249
250 protected toggleDetails(token: KeysToken) {
Sean Condon0c577f62018-11-18 22:40:05 +0000251 if (this.details.selectedNode) {
252 this.flashMsg = 'Toggling details';
253 this.details.togglePanel(() => {
254 });
255 this.log.debug('Toggling details', token);
256 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100257 }
258
259 protected toggleInstancePanel(token: KeysToken) {
260 this.flashMsg = 'Toggling instances';
261 this.instance.togglePanel(() => {});
262 this.log.debug('Toggling instances', token);
263 // TODO: Reinstate with components
264 // this.updatePrefsState('insts', t2is.toggle(x));
265 }
266
267 protected toggleSummary() {
268 this.flashMsg = 'Toggling summary';
269 this.summary.togglePanel(() => {});
270 }
271
272 protected resetZoom() {
Sean Condon50855cf2018-12-23 15:37:42 +0000273 // this.zoomer.reset();
Sean Condonf4f54a12018-10-10 23:25:46 +0100274 this.log.debug('resetting zoom');
275 // TODO: Reinstate with components
276 // t2bgs.resetZoom();
277 // flash.flash('Pan and zoom reset');
278 }
279
280 protected togglePorts(token: KeysToken) {
281 this.log.debug('Toggling ports');
282 // TODO: Reinstate with components
283 // this.updatePrefsState('porthl', t2vs.togglePortHighlights(x));
284 // t2fs.updateLinks();
285 }
286
287 protected equalizeMasters() {
288 this.wss.sendEvent('equalizeMasters', null);
289
290 this.log.debug('equalizing masters');
291 // TODO: Reinstate with components
292 // flash.flash('Equalizing master roles');
293 }
294
295 protected resetNodeLocation() {
296 this.log.debug('resetting node location');
297 // TODO: Reinstate with components
298 // t2fs.resetNodeLocation();
299 // flash.flash('Reset node locations');
300 }
301
302 protected unpinNode() {
303 this.log.debug('unpinning node');
304 // TODO: Reinstate with components
305 // t2fs.unpin();
306 // flash.flash('Unpin node');
307 }
308
309 protected toggleToolbar() {
310 this.log.debug('toggling toolbar');
Sean Condon50855cf2018-12-23 15:37:42 +0000311 this.flashMsg = ('Toggle toolbar');
312 this.toolbar.on = !this.toolbar.on;
Sean Condonf4f54a12018-10-10 23:25:46 +0100313 }
314
315 protected actionedFlashed(action, message) {
316 this.log.debug('action flashed');
317 // TODO: Reinstate with components
318 // this.flash.flash(action + ' ' + message);
319 }
320
321 protected toggleHosts() {
Sean Condon021f0fa2018-12-06 23:31:11 -0800322 const old: boolean = this.force.showHosts;
323 const current = !this.force.showHosts;
324 this.force.ngOnChanges({'showHosts': new SimpleChange(old, current, false)});
325 this.flashMsg = (this.force.showHosts ? 'Show' : 'Hide') + ' Hosts';
326 this.log.debug('toggling hosts: ', this.force.showHosts ? 'Show' : 'Hide');
Sean Condonf4f54a12018-10-10 23:25:46 +0100327 }
328
329 protected toggleOfflineDevices() {
330 this.log.debug('toggling offline devices');
331 // TODO: Reinstate with components
332 // let on = t2rs.toggleOfflineDevices();
333 // this.actionedFlashed(on ? 'Show': 'Hide', 'offline devices');
334 }
335
336 protected notValid(what) {
337 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
338 }
339
340 getActionEntry(key) {
341 let entry;
342
343 if (!key) {
344 this.notValid('key');
345 return null;
346 }
347
348 entry = this.actionMap()[key];
349
350 if (!entry) {
351 this.notValid('actionMap (' + key + ') entry');
352 return null;
353 }
354 return this.fs.isA(entry) || [entry, ''];
355 }
356
Sean Condon0c577f62018-11-18 22:40:05 +0000357 nodeSelected(node: Node) {
358 this.details.selectedNode = node;
359 this.details.on = Boolean(node);
360 }
361
Sean Condon50855cf2018-12-23 15:37:42 +0000362 /**
363 * Enable traffic monitoring
364 */
365 monitorAllTraffic() {
366 this.trs.init(this.force);
367 }
368
369 /**
370 * Cancel traffic monitoring
371 */
372 cancelTraffic() {
373 this.trs.destroy();
374 }
Sean Condonf4f54a12018-10-10 23:25:46 +0100375}