blob: 5ed744b00e75739b3fe5eda0077ede38d9146812 [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 */
16import { Component, OnInit, ViewChild } from '@angular/core';
17import {
18 FnService,
19 KeysService, KeysToken,
20 LogService, PrefsService,
21 SvgUtilService, WebSocketService
22} from 'gui2-fw-lib';
23import {InstanceComponent} from '../panel/instance/instance.component';
24import {SummaryComponent} from '../panel/summary/summary.component';
25import {DetailsComponent} from '../panel/details/details.component';
26
27/**
28 * ONOS GUI Topology View
29 *
30 * This Topology View component is the top level component in a hierarchy that
31 * comprises the whole Topology View
32 *
33 * There are three main parts (panels, graphical and breadcrumbs)
34 * The panel hierarchy
35 * |-- Instances Panel (shows ONOS instances)
36 * |-- Summary Panel (summary of ONOS)
37 * |-- Toolbar Panel (the toolbar)
38 * |-- Details Panel (when a node is selected in the Force graphical view (see below))
39 *
40 * The graphical hierarchy contains
41 * Topology (this)
42 * |-- No Devices Connected (only of there are no nodes to show)
43 * |-- Zoom Layer (everything beneath this can be zoomed and panned)
44 * |-- Background (container for any backgrounds - can be toggled on and off)
45 * |-- Map
46 * |-- Forces (all of the nodes and links laid out by a d3.force simulation)
47 *
48 * The breadcrumbs
49 * |-- Breadcrumb (in region view a way of navigating back up through regions)
50 */
51@Component({
52 selector: 'onos-topology',
53 templateUrl: './topology.component.html',
54 styleUrls: ['./topology.component.css']
55})
56export class TopologyComponent implements OnInit {
57 @ViewChild(InstanceComponent) instance: InstanceComponent;
58 @ViewChild(SummaryComponent) summary: SummaryComponent;
59 @ViewChild(DetailsComponent) details: DetailsComponent;
60
61 flashMsg: string = '';
62 prefsState = {};
63 svg: any;
64 hostLabelIdx: number = 1;
65
66 constructor(
67 protected log: LogService,
68 protected fs: FnService,
69 protected ks: KeysService,
70 protected sus: SvgUtilService,
71 protected ps: PrefsService,
72 protected wss: WebSocketService
73 ) {
74
75 this.log.debug('Topology component constructed');
76 }
77
78 ngOnInit() {
79 this.bindCommands();
80 this.log.debug('Topology component initialized');
81 }
82
83 actionMap() {
84 return {
85 L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],
86 B: [(token) => {this.toggleBackground(token); }, 'Toggle background'],
87 D: [(token) => {this.toggleDetails(token); }, 'Toggle details panel'],
88 I: [(token) => {this.toggleInstancePanel(token); }, 'Toggle ONOS Instance Panel'],
89 O: [() => {this.toggleSummary(); }, 'Toggle the Summary Panel'],
90 R: [() => {this.resetZoom(); }, 'Reset pan / zoom'],
91 P: [(token) => {this.togglePorts(token); }, 'Toggle Port Highlighting'],
92 E: [() => {this.equalizeMasters(); }, 'Equalize mastership roles'],
93 X: [() => {this.resetNodeLocation(); }, 'Reset Node Location'],
94 U: [() => {this.unpinNode(); }, 'Unpin node (mouse over)'],
95 H: [() => {this.toggleHosts(); }, 'Toggle host visibility'],
96 M: [() => {this.toggleOfflineDevices(); }, 'Toggle offline visibility'],
97 dot: [() => {this.toggleToolbar(); }, 'Toggle Toolbar'],
98 'shift-L': [() => {this.cycleHostLabels(); }, 'Cycle host labels'],
99
100 // -- instance color palette debug
101 9: function () {
102 this.sus.cat7().testCard(this.svg);
103 },
104
105 esc: this.handleEscape,
106
107 // TODO update after adding in Background Service
108 // topology overlay selections
109 // F1: function () { t2tbs.fnKey(0); },
110 // F2: function () { t2tbs.fnKey(1); },
111 // F3: function () { t2tbs.fnKey(2); },
112 // F4: function () { t2tbs.fnKey(3); },
113 // F5: function () { t2tbs.fnKey(4); },
114 //
115 // _keyListener: t2tbs.keyListener.bind(t2tbs),
116
117 _helpFormat: [
118 ['I', 'O', 'D', 'H', 'M', 'P', 'dash', 'B'],
119 ['X', 'Z', 'N', 'L', 'shift-L', 'U', 'R', 'E', 'dot'],
120 [], // this column reserved for overlay actions
121 ],
122 };
123 }
124
125
126 bindCommands(additional?: any) {
127
128 const am = this.actionMap();
129 const add = this.fs.isO(additional);
130
131 // TODO: Reimplement when we have a use case
132 // if (add) {
133 // _.each(add, function (value, key) {
134 // // filter out meta properties (e.g. _keyOrder)
135 // if (!(_.startsWith(key, '_'))) {
136 // // don't allow re-definition of existing key bindings
137 // if (am[key]) {
138 // this.log.warn('keybind: ' + key + ' already exists');
139 // } else {
140 // am[key] = [value.cb, value.tt];
141 // }
142 // }
143 // });
144 // }
145
146 this.ks.keyBindings(am);
147
148 this.ks.gestureNotes([
149 ['click', 'Select the item and show details'],
150 ['shift-click', 'Toggle selection state'],
151 ['drag', 'Reposition (and pin) device / host'],
152 ['cmd-scroll', 'Zoom in / out'],
153 ['cmd-drag', 'Pan'],
154 ]);
155 }
156
157 handleEscape() {
158
159 if (false) {
160 // TODO: Cancel show mastership
161 // TODO: Cancel Active overlay
162 // TODO: Reinstate with components
163 } else {
164 this.log.debug('Handling escape');
165 // } else if (t2rs.deselectAllNodes()) {
166 // // else if we have node selections, deselect them all
167 // // (work already done)
168 // } else if (t2rs.deselectLink()) {
169 // // else if we have a link selection, deselect it
170 // // (work already done)
171 // } else if (t2is.isVisible()) {
172 // // If the instance panel is visible, close it
173 // t2is.toggle();
174 // } else if (t2sp.isVisible()) {
175 // // If the summary panel is visible, close it
176 // t2sp.toggle();
177 }
178 }
179
180
181
182 updatePrefsState(what, b) {
183 this.prefsState[what] = b ? 1 : 0;
184 this.ps.setPrefs('topo2_prefs', this.prefsState);
185 }
186
187 deviceLabelFlashMessage(index) {
188 switch (index) {
189 case 0: return 'Hide device labels';
190 case 1: return 'Show friendly device labels';
191 case 2: return 'Show device ID labels';
192 }
193 }
194
195 hostLabelFlashMessage(index) {
196 switch (index) {
197 case 0: return 'Hide host labels';
198 case 1: return 'Show friendly host labels';
199 case 2: return 'Show host IP labels';
200 case 3: return 'Show host MAC Address labels';
201 }
202 }
203
204 protected cycleDeviceLabels() {
205 this.log.debug('Cycling device labels');
206 // TODO: Reinstate with components
207 // let deviceLabelIndex = t2ps.get('dlbls') + 1;
208 // let newDeviceLabelIndex = deviceLabelIndex % 3;
209 //
210 // t2ps.set('dlbls', newDeviceLabelIndex);
211 // t2fs.updateNodes();
212 // flash.flash(deviceLabelFlashMessage(newDeviceLabelIndex));
213 }
214
215 protected cycleHostLabels() {
216 const hostLabelIndex = this.hostLabelIdx + 1;
217 this.hostLabelIdx = hostLabelIndex % 4;
218 this.flashMsg = this.hostLabelFlashMessage(this.hostLabelIdx);
219 this.log.debug('Cycling host labels');
220 // TODO: Reinstate with components
221 // t2ps.set('hlbls', newHostLabelIndex);
222 // t2fs.updateNodes();
223 }
224
225 protected toggleBackground(token: KeysToken) {
226 this.flashMsg = 'Toggling background';
227 this.log.debug('Toggling background', token);
228 // TODO: Reinstate with components
229 // t2bgs.toggle(x);
230 }
231
232 protected toggleDetails(token: KeysToken) {
233 this.flashMsg = 'Toggling details';
234 this.details.togglePanel(() => {});
235 this.log.debug('Toggling details', token);
236 }
237
238 protected toggleInstancePanel(token: KeysToken) {
239 this.flashMsg = 'Toggling instances';
240 this.instance.togglePanel(() => {});
241 this.log.debug('Toggling instances', token);
242 // TODO: Reinstate with components
243 // this.updatePrefsState('insts', t2is.toggle(x));
244 }
245
246 protected toggleSummary() {
247 this.flashMsg = 'Toggling summary';
248 this.summary.togglePanel(() => {});
249 }
250
251 protected resetZoom() {
252 this.log.debug('resetting zoom');
253 // TODO: Reinstate with components
254 // t2bgs.resetZoom();
255 // flash.flash('Pan and zoom reset');
256 }
257
258 protected togglePorts(token: KeysToken) {
259 this.log.debug('Toggling ports');
260 // TODO: Reinstate with components
261 // this.updatePrefsState('porthl', t2vs.togglePortHighlights(x));
262 // t2fs.updateLinks();
263 }
264
265 protected equalizeMasters() {
266 this.wss.sendEvent('equalizeMasters', null);
267
268 this.log.debug('equalizing masters');
269 // TODO: Reinstate with components
270 // flash.flash('Equalizing master roles');
271 }
272
273 protected resetNodeLocation() {
274 this.log.debug('resetting node location');
275 // TODO: Reinstate with components
276 // t2fs.resetNodeLocation();
277 // flash.flash('Reset node locations');
278 }
279
280 protected unpinNode() {
281 this.log.debug('unpinning node');
282 // TODO: Reinstate with components
283 // t2fs.unpin();
284 // flash.flash('Unpin node');
285 }
286
287 protected toggleToolbar() {
288 this.log.debug('toggling toolbar');
289 // TODO: Reinstate with components
290 // t2tbs.toggle();
291 }
292
293 protected actionedFlashed(action, message) {
294 this.log.debug('action flashed');
295 // TODO: Reinstate with components
296 // this.flash.flash(action + ' ' + message);
297 }
298
299 protected toggleHosts() {
300 // this.flashMsg = on ? 'Show': 'Hide', 'Hosts';
301 this.log.debug('toggling hosts');
302 // TODO: Reinstate with components
303 // let on = t2rs.toggleHosts();
304 // this.actionedFlashed(on ? 'Show': 'Hide', 'Hosts');
305 }
306
307 protected toggleOfflineDevices() {
308 this.log.debug('toggling offline devices');
309 // TODO: Reinstate with components
310 // let on = t2rs.toggleOfflineDevices();
311 // this.actionedFlashed(on ? 'Show': 'Hide', 'offline devices');
312 }
313
314 protected notValid(what) {
315 this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
316 }
317
318 getActionEntry(key) {
319 let entry;
320
321 if (!key) {
322 this.notValid('key');
323 return null;
324 }
325
326 entry = this.actionMap()[key];
327
328 if (!entry) {
329 this.notValid('actionMap (' + key + ') entry');
330 return null;
331 }
332 return this.fs.isA(entry) || [entry, ''];
333 }
334
335}