GUI2 added in details panel, updated docs
Change-Id: I49a874deeb4de1082f190ea5d0d985c986a978f8
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
index 2a654b7..6fe0cb1 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
@@ -1,5 +1,5 @@
<!--
-~ Copyright 2018-present Open Networking Foundation
+~ Copyright 2019-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -13,17 +13,42 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
+<!-- Template explaination - Add in the flash message component - and link it to
+the local variable - this is used to display messages when keyboard shortcuts are pressed
+-->
<onos-flash id="topoMsgFlash" message="{{ flashMsg }}" (closed)="flashMsg = ''"></onos-flash>
+<!-- Template explanation - Add in the Panel components for the Topology view
+ These are referenced inside the typescript by @ViewChild and their label
+-->
<onos-instance #instance [divTopPx]="80" (mastershipEvent)="force.onosInstMastership = $event"></onos-instance>
<onos-summary #summary></onos-summary>
-<onos-toolbar #toolbar></onos-toolbar>
+<onos-toolbar #toolbar (buttonEvent)="toolbarButtonClicked($event)"></onos-toolbar>
<onos-details #details></onos-details>
<div id="ov-topo2">
+ <!-- Template explanation -
+ Line 0) This is the root of the whole SVG canvas of the Topology View - all
+ components beneath it are SVG components only (no HTML)
+ line 1) the No Devices Connected banner is shown if the force component
+ (from line 4) does not contain any devices
+ line 2) Create an SVG Grouping and apply the onosZoomableOf directive to it,
+ passing in the whole SVG canvas (#svgZoom)
+ line 3) Add in the Background Svg Component (if showBackground is true - toggled
+ by toolbar and by keyboard shortcut 'B'
+ line 4) Add in the layer of the Force Svg Component. If any item is selected on it, pass
+ to the details view and deselect all others. This is node and line graph
+ whose contents are supplied through the Topology Service, and whose positions
+ are driven by the d3.force engine
+ -->
<svg:svg #svgZoom xmlns:svg="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" id="topo2">
- <svg:g *ngIf="force.regionData?.devices.length === 0" onos-nodeviceconnected />
+ <svg:desc>The main SVG canvas of the Topology View</svg:desc>
+ <svg:g *ngIf="force.regionData?.devices[0].length +
+ force.regionData?.devices[1].length +
+ force.regionData?.devices[2].length=== 0"
+ onos-nodeviceconnected />
<svg:g id="topo-zoomlayer" onosZoomableOf [zoomableOf]="svgZoom">
+ <svg:desc>A logical layer that allows the main SVG canvas to be zoomed and panned</svg:desc>
<svg:g *ngIf="showBackground" onos-backgroundsvg/>
<svg:g #force onos-forcesvg (selectedNodeEvent)="nodeSelected($event)"/>
</svg:g>
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
index a9c6194..23fb257 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
@@ -32,9 +32,11 @@
import {
FlashComponent,
FnService,
- LogService
+ LogService,
+ IconService, IconComponent
} from 'gui2-fw-lib';
import {ZoomableDirective} from '../layer/zoomable.directive';
+import {RouterTestingModule} from '@angular/router/testing';
class MockActivatedRoute extends ActivatedRoute {
@@ -72,6 +74,10 @@
destroy() {}
}
+class MockIconService {
+ loadIconDef() { }
+}
+
/**
* ONOS GUI -- Topology View -- Unit Tests
*/
@@ -101,7 +107,7 @@
fs = new FnService(ar, logSpy, windowMock);
TestBed.configureTestingModule({
- imports: [ BrowserAnimationsModule ],
+ imports: [ BrowserAnimationsModule, RouterTestingModule ],
declarations: [
TopologyComponent,
InstanceComponent,
@@ -109,7 +115,8 @@
ToolbarComponent,
DetailsComponent,
FlashComponent,
- ZoomableDirective
+ ZoomableDirective,
+ IconComponent
],
providers: [
{ provide: FnService, useValue: fs },
@@ -117,6 +124,7 @@
{ provide: 'Window', useValue: windowMock },
{ provide: HttpClient, useClass: MockHttpClient },
{ provide: TopologyService, useClass: MockTopologyService },
+ { provide: IconService, useClass: MockIconService },
]
}).compileComponents();
logServiceSpy = TestBed.get(LogService);
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
index 7365310..3e1e1a6 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-present Open Networking Foundation
+ * Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the 'License');
* you may not use this file except in compliance with the License.
@@ -21,9 +21,9 @@
} from '@angular/core';
import * as d3 from 'd3';
import {
- FnService,
+ FnService, IconService,
KeysService,
- KeysToken,
+ KeysToken, LionService,
LogService,
PrefsService,
SvgUtilService,
@@ -36,9 +36,14 @@
import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
import {TopologyService} from '../topology.service';
-import {HostLabelToggle, LabelToggle, Node} from '../layer/forcesvg/models';
+import {
+ HostLabelToggle,
+ LabelToggle,
+ UiElement
+} from '../layer/forcesvg/models';
import {ToolbarComponent} from '../panel/toolbar/toolbar.component';
import {TrafficService} from '../traffic.service';
+import {ZoomableDirective} from '../layer/zoomable.directive';
/**
* ONOS GUI Topology View
@@ -77,11 +82,13 @@
@ViewChild(ToolbarComponent) toolbar: ToolbarComponent;
@ViewChild(BackgroundSvgComponent) background: BackgroundSvgComponent;
@ViewChild(ForceSvgComponent) force: ForceSvgComponent;
+ @ViewChild(ZoomableDirective) zoomDirective: ZoomableDirective;
flashMsg: string = '';
prefsState = {};
hostLabelIdx: number = 1;
showBackground: boolean = false;
+ lionFn; // Function
constructor(
protected log: LogService,
@@ -92,29 +99,69 @@
protected wss: WebSocketService,
protected zs: ZoomService,
protected ts: TopologyService,
- protected trs: TrafficService
+ protected trs: TrafficService,
+ protected is: IconService,
+ private lion: LionService,
) {
+ if (this.lion.ubercache.length === 0) {
+ this.lionFn = this.dummyLion;
+ this.lion.loadCbs.set('topo-toolbar', () => this.doLion());
+ } else {
+ this.doLion();
+ }
+ this.is.loadIconDef('bird');
+ this.is.loadIconDef('active');
+ this.is.loadIconDef('uiAttached');
+ this.is.loadIconDef('m_switch');
+ this.is.loadIconDef('m_roadm');
+ this.is.loadIconDef('m_router');
+ this.is.loadIconDef('m_uiAttached');
+ this.is.loadIconDef('m_endstation');
+ this.is.loadIconDef('m_ports');
+ this.is.loadIconDef('m_summary');
+ this.is.loadIconDef('m_details');
+ this.is.loadIconDef('m_map');
+ this.is.loadIconDef('m_cycleLabels');
+ this.is.loadIconDef('m_resetZoom');
+ this.is.loadIconDef('m_eqMaster');
+ this.is.loadIconDef('m_unknown');
+ this.is.loadIconDef('m_allTraffic');
+ this.is.loadIconDef('deviceTable');
+ this.is.loadIconDef('flowTable');
+ this.is.loadIconDef('portTable');
+ this.is.loadIconDef('groupTable');
+ this.is.loadIconDef('meterTable');
+ this.is.loadIconDef('triangleUp');
this.log.debug('Topology component constructed');
}
+ /**
+ * Static functions must come before member variables
+ * @param index
+ */
private static deviceLabelFlashMessage(index: number): string {
switch (index) {
- case 0: return 'Hide device labels';
- case 1: return 'Show friendly device labels';
- case 2: return 'Show device ID labels';
+ case 0: return 'fl_device_labels_hide';
+ case 1: return 'fl_device_labels_show_friendly';
+ case 2: return 'fl_device_labels_show_id';
}
}
private static hostLabelFlashMessage(index: number): string {
switch (index) {
- case 0: return 'Hide host labels';
- case 1: return 'Show friendly host labels';
- case 2: return 'Show host IP labels';
- case 3: return 'Show host MAC Address labels';
+ case 0: return 'fl_host_labels_hide';
+ case 1: return 'fl_host_labels_show_friendly';
+ case 2: return 'fl_host_labels_show_ip';
+ case 3: return 'fl_host_labels_show_mac';
}
}
+ /**
+ * Pass the list of Key Commands to the KeyService, and initialize the Topology
+ * Service - which communicates with through the WebSocket to the ONOS server
+ * to get the nodes and links.
+ */
ngOnInit() {
this.bindCommands();
// The components from the template are handed over to TopologyService here
@@ -125,11 +172,72 @@
this.log.debug('Topology component initialized');
}
+ /**
+ * When this component is being stopped, disconnect the TopologyService from
+ * the WebSocket
+ */
ngOnDestroy() {
this.ts.destroy();
this.log.debug('Topology component destroyed');
}
+ /**
+ * When ever a toolbar button is clicked, an event is sent up from toolbar
+ * component which is caught and passed on to here.
+ * @param name The name of the button that was clicked
+ */
+ toolbarButtonClicked(name: string) {
+ switch (name) {
+ case 'instance-tog':
+ this.toggleInstancePanel();
+ break;
+ case 'summary-tog':
+ this.toggleSummary();
+ break;
+ case 'details-tog':
+ this.toggleDetails();
+ break;
+ case 'hosts-tog':
+ this.toggleHosts();
+ break;
+ case 'offline-tog':
+ this.toggleOfflineDevices();
+ break;
+ case 'ports-tog':
+ this.togglePorts();
+ break;
+ case 'bkgrnd-tog':
+ this.toggleBackground();
+ break;
+ case 'cycleLabels-btn':
+ this.cycleDeviceLabels();
+ break;
+ case 'cycleHostLabel-btn':
+ this.cycleHostLabels();
+ break;
+ case 'resetZoom-btn':
+ this.resetZoom();
+ break;
+ case 'eqMaster-btn':
+ this.equalizeMasters();
+ break;
+ case 'cancel-traffic':
+ this.cancelTraffic();
+ break;
+ case 'all-traffic':
+ this.monitorAllTraffic();
+ break;
+ default:
+ this.log.warn('Unhandled Toolbar action', name);
+ }
+ }
+
+ /**
+ * The list of key strokes that will be active in the Topology View.
+ *
+ * This action map is passed to the KeyService through the bindCommands()
+ * when this component is being initialized
+ */
actionMap() {
return {
A: [() => {this.monitorAllTraffic(); }, 'Monitor all traffic'],
@@ -226,7 +334,7 @@
const next = LabelToggle.next(old);
this.force.ngOnChanges({'deviceLabelToggle':
new SimpleChange(old, next, false)});
- this.flashMsg = TopologyComponent.deviceLabelFlashMessage(next);
+ this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
this.log.debug('Cycling device labels', old, next);
}
@@ -235,108 +343,112 @@
const next = HostLabelToggle.next(old);
this.force.ngOnChanges({'hostLabelToggle':
new SimpleChange(old, next, false)});
- this.flashMsg = TopologyComponent.hostLabelFlashMessage(next);
+ this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
this.log.debug('Cycling host labels', old, next);
}
- protected toggleBackground(token: KeysToken) {
- this.flashMsg = 'Toggling background';
+ protected toggleBackground(token?: KeysToken) {
this.showBackground = !this.showBackground;
+ this.flashMsg = this.lionFn(this.showBackground ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_background_map');
+ this.toolbar.backgroundVisible = this.showBackground;
this.log.debug('Toggling background', token);
- // TODO: Reinstate with components
- // t2bgs.toggle(x);
}
- protected toggleDetails(token: KeysToken) {
+ protected toggleDetails(token?: KeysToken) {
if (this.details.selectedNode) {
- this.flashMsg = 'Toggling details';
- this.details.togglePanel(() => {
+ const on: boolean = this.details.togglePanel(() => {
});
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_panel_details');
+ this.toolbar.detailsVisible = on;
+
this.log.debug('Toggling details', token);
}
}
- protected toggleInstancePanel(token: KeysToken) {
- this.flashMsg = 'Toggling instances';
- this.instance.togglePanel(() => {});
- this.log.debug('Toggling instances', token);
- // TODO: Reinstate with components
- // this.updatePrefsState('insts', t2is.toggle(x));
+ protected toggleInstancePanel(token?: KeysToken) {
+ const on: boolean = this.instance.togglePanel(() => {});
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_panel_instances');
+ this.toolbar.instancesVisible = on;
+ this.log.debug('Toggling instances', token, on);
}
protected toggleSummary() {
- this.flashMsg = 'Toggling summary';
- this.summary.togglePanel(() => {});
+ const on: boolean = this.summary.togglePanel(() => {});
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_panel_summary');
+ this.toolbar.summaryVisible = on;
}
protected resetZoom() {
- // this.zoomer.reset();
- this.log.debug('resetting zoom');
- // TODO: Reinstate with components
- // t2bgs.resetZoom();
- // flash.flash('Pan and zoom reset');
+ this.zoomDirective.resetZoom();
+ this.flashMsg = this.lionFn('fl_pan_zoom_reset');
}
- protected togglePorts(token: KeysToken) {
- this.log.debug('Toggling ports');
- // TODO: Reinstate with components
- // this.updatePrefsState('porthl', t2vs.togglePortHighlights(x));
- // t2fs.updateLinks();
+ protected togglePorts(token?: KeysToken) {
+ const old: boolean = this.force.highlightPorts;
+ const current: boolean = !this.force.highlightPorts;
+ this.force.ngOnChanges({'highlightPorts': new SimpleChange(old, current, false)});
+ this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
+ ' ' + this.lionFn('fl_port_highlighting');
+ this.toolbar.portsVisible = current;
+ this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
}
protected equalizeMasters() {
this.wss.sendEvent('equalizeMasters', null);
-
+ this.flashMsg = this.lionFn('fl_eq_masters');
this.log.debug('equalizing masters');
- // TODO: Reinstate with components
- // flash.flash('Equalizing master roles');
}
protected resetNodeLocation() {
+ // TODO: Implement reset locations
+ this.flashMsg = this.lionFn('fl_reset_node_locations');
this.log.debug('resetting node location');
- // TODO: Reinstate with components
- // t2fs.resetNodeLocation();
- // flash.flash('Reset node locations');
}
protected unpinNode() {
+ // TODO: Implement this
this.log.debug('unpinning node');
- // TODO: Reinstate with components
- // t2fs.unpin();
- // flash.flash('Unpin node');
}
protected toggleToolbar() {
this.log.debug('toggling toolbar');
- this.flashMsg = ('Toggle toolbar');
this.toolbar.on = !this.toolbar.on;
}
- protected actionedFlashed(action, message) {
- this.log.debug('action flashed');
- // TODO: Reinstate with components
- // this.flash.flash(action + ' ' + message);
- }
-
protected toggleHosts() {
const old: boolean = this.force.showHosts;
const current = !this.force.showHosts;
this.force.ngOnChanges({'showHosts': new SimpleChange(old, current, false)});
- this.flashMsg = (this.force.showHosts ? 'Show' : 'Hide') + ' Hosts';
+ this.flashMsg = this.lionFn('hosts') + ' ' +
+ this.lionFn(this.force.showHosts ? 'visible' : 'hidden');
+ this.toolbar.hostsVisible = current;
this.log.debug('toggling hosts: ', this.force.showHosts ? 'Show' : 'Hide');
}
protected toggleOfflineDevices() {
+ // TODO: Implement toggle offline visibility
+ const on: boolean = true;
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_offline_devices');
this.log.debug('toggling offline devices');
- // TODO: Reinstate with components
- // let on = t2rs.toggleOfflineDevices();
- // this.actionedFlashed(on ? 'Show': 'Hide', 'offline devices');
}
+ /**
+ * Check to see if this is needed anymore
+ * @param what
+ */
protected notValid(what) {
this.log.warn('topo.js getActionEntry(): Not a valid ' + what);
}
+ /**
+ * Check to see if this is needed anymore
+ * @param what
+ */
getActionEntry(key) {
let entry;
@@ -354,15 +466,23 @@
return this.fs.isA(entry) || [entry, ''];
}
- nodeSelected(node: Node) {
- this.details.selectedNode = node;
- this.details.on = Boolean(node);
+ /**
+ * An event handler that updates the details panel as items are
+ * selected in the forcesvg layer
+ * @param nodeOrLink the item to display details of
+ */
+ nodeSelected(nodeOrLink: UiElement) {
+ this.details.ngOnChanges({'selectedNode':
+ new SimpleChange(undefined, nodeOrLink, true)});
+ this.details.on = Boolean(nodeOrLink);
}
/**
* Enable traffic monitoring
*/
monitorAllTraffic() {
+ // TODO: Implement support for toggling between bits, packets and octets
+ this.flashMsg = this.lionFn('tr_fl_pstats_bits');
this.trs.init(this.force);
}
@@ -370,6 +490,22 @@
* Cancel traffic monitoring
*/
cancelTraffic() {
+ this.flashMsg = this.lionFn('fl_monitoring_canceled');
this.trs.destroy();
}
+
+ /**
+ * Read the LION bundle for Toolbar and set up the lionFn
+ */
+ doLion() {
+ this.lionFn = this.lion.bundle('core.view.Topo');
+ }
+
+ /**
+ * A dummy implementation of the lionFn until the response is received and the LION
+ * bundle is received from the WebSocket
+ */
+ dummyLion(key: string): string {
+ return '%' + key + '%';
+ }
}