Implemented Instance View of Topo in GUI2
Change-Id: If603481e729ebc19a6f91db2739f1b422cc762d0
diff --git a/web/gui2/src/main/webapp/app/onos-routing.module.ts b/web/gui2/src/main/webapp/app/onos-routing.module.ts
index 60ec9d7..962f5a3 100644
--- a/web/gui2/src/main/webapp/app/onos-routing.module.ts
+++ b/web/gui2/src/main/webapp/app/onos-routing.module.ts
@@ -79,7 +79,7 @@
loadChildren: 'app/view/meter/meter.module#MeterModule'
},
{
- path: 'topo',
+ path: 'topo2',
loadChildren: 'app/view/topology/topology.module#TopologyModule'
},
/* Comment out below section for running locally with 'ng serve' when developing */
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.html b/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.html
index 3f78ebb..389ec9c 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.html
@@ -14,3 +14,9 @@
~ limitations under the License.
-->
<svg:g onos-mapsvg />
+<svg:g>
+ <svg:text>Layout: {{ layoutData.id }} {{ layoutData.bgDesc }}</svg:text>
+ <svg:text>Region: {{ layoutData.region }} {{ layoutData.regionName }}</svg:text>
+ <svg:text>Parent {{ layoutData.parent }}</svg:text>
+ <svg:text *ngFor="let crumb of layoutData.crumbs">{{ crumb.id }} {{ crumb.name }}</svg:text>
+</svg:g>
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.ts b/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.ts
index 6a010d1..ff6f99a 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/backgroundsvg/backgroundsvg.component.ts
@@ -16,6 +16,57 @@
import { Component, OnInit } from '@angular/core';
/**
+ * model of the topo2CurrentLayout attrs from BgZoom below
+ */
+export interface BgZoomAttrs {
+ offsetX: number;
+ offsetY: number;
+ scale: number;
+}
+
+/**
+ * model of the topo2CurrentLayout background zoom attrs from Layout below
+ */
+export interface BgZoom {
+ cfg: BgZoomAttrs;
+ usr?: BgZoomAttrs;
+}
+
+/**
+ * model of the topo2CurrentLayout breadcrumb from Layout below
+ */
+export interface LayoutCrumb {
+ id: string;
+ name: string;
+}
+
+/**
+ * Enum of the topo2CurrentRegion location type from Location below
+ */
+export enum LocationType {
+ GEO = 'geo',
+ GRID = 'grid'
+}
+
+/**
+ * model of the topo2CurrentLayout WebSocket response
+ */
+export interface Layout {
+ id: string;
+ bgDefaultScale: number;
+ bgDesc: string;
+ bgFilePath: string;
+ bgId: string;
+ bgType: LocationType;
+ bgWarn: string;
+ bgZoom: BgZoom;
+ crumbs: LayoutCrumb[];
+ parent: string;
+ region: string;
+ regionName: string;
+}
+
+/**
* ONOS GUI -- Topology Background Layer View.
*/
@Component({
@@ -25,9 +76,13 @@
})
export class BackgroundSvgComponent implements OnInit {
+ layoutData: Layout = <Layout>{};
+
constructor() { }
ngOnInit() {
}
+
+
}
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
index 45fa000..d159f06 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.ts
@@ -13,7 +13,127 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit } from '@angular/core';
+import {Component, Input, OnInit} from '@angular/core';
+import { LocationType } from '../backgroundsvg/backgroundsvg.component';
+
+/**
+ * Enum of the topo2CurrentRegion node type from SubRegion below
+ */
+export enum NodeType {
+ REGION = 'region',
+ DEVICE = 'device'
+}
+
+/**
+ * Enum of the topo2CurrentRegion layerOrder from Region below
+ */
+export enum LayerOrder {
+ LAYER_OPTICAL = 'opt',
+ LAYER_PACKET = 'pkt',
+ LAYER_DEFAULT = 'def'
+}
+
+/**
+ * model of the topo2CurrentRegion location from SubRegion below
+ */
+export interface Location {
+ locType: LocationType;
+ latOrY: number;
+ longOrX: number;
+}
+
+/**
+ * model of the topo2CurrentRegion props from SubRegion below
+ */
+export interface RegionProps {
+ latitude: number;
+ longitude: number;
+ name: string;
+ peerLocations: string;
+}
+
+/**
+ * model of the topo2CurrentRegion subregion from Region below
+ */
+export interface SubRegion {
+ id: string;
+ location: Location;
+ nDevs: number;
+ nHosts: number;
+ name: string;
+ nodeType: NodeType;
+ props: RegionProps;
+}
+
+export enum LinkType {
+ UiRegionLink,
+ UiDeviceLink
+}
+
+/**
+ * model of the topo2CurrentRegion region rollup from Region below
+ */
+export interface RegionRollup {
+ id: string;
+ epA: string;
+ epB: string;
+ portA: string;
+ portB: string;
+ type: LinkType;
+}
+
+/**
+ * model of the topo2CurrentRegion region link from Region below
+ */
+export interface RegionLink {
+ id: string;
+ epA: string;
+ epB: string;
+ rollup: RegionRollup[];
+ type: LinkType;
+}
+
+/**
+ * model of the topo2CurrentRegion device props from Device below
+ */
+export interface DeviceProps {
+ latitude: number;
+ longitude: number;
+ name: string;
+ locType: LocationType;
+}
+
+export interface Device {
+ id: string;
+ layer: LayerOrder;
+ location: LocationType;
+ master: string;
+ nodeType: NodeType;
+ online: boolean;
+ props: DeviceProps;
+ type: string;
+}
+
+/**
+ * model of the topo2CurrentRegion WebSocket response
+ */
+export interface Region {
+ note?: string;
+ id: string;
+ devices: Device[][];
+ hosts: Object[];
+ links: RegionLink[];
+ layerOrder: LayerOrder[];
+ peerLocations?: Location[];
+ subregions: SubRegion[];
+}
+
+/**
+ * model of the topo2PeerRegions WebSocket response
+ */
+export interface Peer {
+ peers: SubRegion[];
+}
/**
* ONOS GUI -- Topology Forces Graph Layer View.
@@ -24,6 +144,8 @@
styleUrls: ['./forcesvg.component.css']
})
export class ForceSvgComponent implements OnInit {
+ @Input() onosInstMastership: string = '';
+ regionData: Region;
constructor() { }
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/layout/layout.component.ts b/web/gui2/src/main/webapp/app/view/topology/layer/layout/layout.component.ts
index 477dd15..122c14b 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/layout/layout.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/layout/layout.component.ts
@@ -16,15 +16,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
- selector: 'onos-layout',
- templateUrl: './layout.component.html',
- styleUrls: ['./layout.component.css']
+ selector: 'onos-layout',
+ templateUrl: './layout.component.html',
+ styleUrls: ['./layout.component.css']
})
export class LayoutComponent implements OnInit {
- constructor() { }
+ constructor() { }
- ngOnInit() {
- }
+ ngOnInit() {
+ }
}
diff --git a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.css b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.css
index cb78e8d..f335726 100644
--- a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.css
+++ b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.css
@@ -15,11 +15,6 @@
*/
/* --- Topo Instance Panel --- */
-#topo-p-instance {
- height: 85px;
- padding: 10px;
-}
-
#topo-p-instance div.onosInst {
display: inline-block;
width: 170px;
diff --git a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.html b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.html
index 6a1aba9..f856213 100644
--- a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.html
@@ -13,17 +13,28 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<div id="topo-p-instance" class="floatpanel" style="left: 20px; width: 170px; height: 85px;" [@instancePanelState]="!on">
- <div class="onosInst online ready mastership affinity">
+<div id="topo-p-instance" class="floatpanel" [ngStyle]="{'left': '20px', 'top':divTopPx+'px', 'width': (onosInstances.length * 170)+'px', 'height': '85px'}" [@instancePanelState]="!on">
+ <div *ngFor="let inst of onosInstances | keyvalue ; let i=index"
+ [ngClass]="['onosInst', inst.value.online?'online':'', inst.value.ready? 'ready': '', mastership?'mastership':'', 'affinity']"
+ (click)="chooseMastership(inst.value.id)">
<svg width="170" height="85" viewBox="0 0 170 85">
- <rect x="5" y="5" width="160" height="30" style="fill: rgb(91, 153, 210);"></rect>
+ <!-- The following blue-glow effect is applied (through CSS) when mastership style is activated on a rectangle -->
+ <filter x="-50%" y="-50%" width="200%" height="200%" id="blue-glow">
+ <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0 0 0 1 0 "></feColorMatrix>
+ <feGaussianBlur stdDeviation="3" result="coloredBlur"></feGaussianBlur>
+ <feMerge>
+ <feMergeNode in="coloredBlur"></feMergeNode>
+ <feMergeNode in="SourceGraphic"></feMergeNode>
+ </feMerge>
+ </filter>
+ <rect x="5" y="5" width="160" height="30" [ngStyle]="{ 'fill': panelColour(i)}"></rect>
+ <text class="instTitle" x="48" y="27">{{ inst.value.id }}</text>
<rect x="5" y="35" width="160" height="45"></rect>
+ <text class="instLabel ip" x="48" y="55">{{ inst.value.ip }}</text>
<use width="20" height="20" class="glyph badgeIcon bird" xlink:href="#bird" transform="translate(15,10)"></use>
- <use width="16" height="16" class="glyph overlay badgeIcon readyBadge" xlink:href="#checkMark" transform="translate(18,40)"></use>
- <text class="instTitle" x="48" y="27">127.0.0.1</text>
- <text class="instLabel ip" x="48" y="55">127.0.0.1</text>
- <text class="instLabel ns" x="48" y="73">Devices: 0</text>
- <use width="24" height="24" class="glyph overlay badgeIcon uiBadge" xlink:href="#uiAttached" transform="translate(14,54)"></use>
+ <use *ngIf="inst.value.ready" width="16" height="16" class="glyph overlay badgeIcon readyBadge" xlink:href="#checkMark" transform="translate(18,40)"></use>
+ <text class="instLabel ns" x="48" y="73">Devices: {{ inst.value.switches }}</text>
+ <use *ngIf="inst.value.uiAttached" width="24" height="24" class="glyph overlay badgeIcon uiBadge" xlink:href="#uiAttached" transform="translate(14,54)"></use>
</svg>
</div>
</div>
\ No newline at end of file
diff --git a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.ts b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.ts
index 66b2a05..05e8768 100644
--- a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.component.ts
@@ -13,18 +13,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit, Input } from '@angular/core';
+import {
+ Component,
+ Input,
+ Output,
+ EventEmitter
+} from '@angular/core';
import { animate, state, style, transition, trigger } from '@angular/animations';
import {
LogService,
LoadingService,
FnService,
- PanelBaseImpl
+ PanelBaseImpl,
+ IconService,
+ SvgUtilService
} from 'gui2-fw-lib';
-/*
- ONOS GUI -- Topology Instances Panel.
- Displays ONOS instances.
+/**
+ * A model of instance information that drives each panel
+ */
+export interface Instance {
+ id: string;
+ ip: string;
+ online: boolean;
+ ready: boolean;
+ switches: number;
+ uiAttached: boolean;
+}
+
+/**
+ * ONOS GUI -- Topology Instances Panel.
+ * Displays ONOS instances. The onosInstances Array gets updated by topology.service
+ * whenever a topo2AllInstances update arrives back on the WebSocket
+ *
+ * This emits a mastership event when the user clicks on an instance, to
+ * see the devices that it has mastership of.
*/
@Component({
selector: 'onos-instance',
@@ -38,29 +61,57 @@
trigger('instancePanelState', [
state('true', style({
transform: 'translateX(0%)',
- opacity: '100'
+ opacity: '1.0'
})),
state('false', style({
transform: 'translateX(-100%)',
- opacity: '0'
+ opacity: '0.0'
})),
transition('0 => 1', animate('100ms ease-in')),
transition('1 => 0', animate('100ms ease-out'))
])
]
})
-export class InstanceComponent extends PanelBaseImpl implements OnInit {
+export class InstanceComponent extends PanelBaseImpl {
+ @Input() divTopPx: number = 100;
+ @Output() mastershipEvent = new EventEmitter<string>();
+ public onosInstances: Array<Instance>;
+ protected mastership: string;
constructor(
protected fs: FnService,
protected log: LogService,
protected ls: LoadingService,
+ protected is: IconService,
+ protected sus: SvgUtilService
) {
super(fs, ls, log);
+ this.onosInstances = <Array<Instance>>[];
+ this.is.loadIconDef('active');
+ this.is.loadIconDef('uiAttached');
this.log.debug('InstanceComponent constructed');
}
- ngOnInit() {
+ /**
+ * Get a colour for the banner of the nth panel
+ * @param idx The index of the panel (0-6)
+ */
+ panelColour(idx: number): string {
+ return this.sus.cat7().getColor(idx, false, '');
}
+ /**
+ * Toggle the display of mastership
+ * If the same instance is clicked a second time then cancel display of mastership
+ * @param instId The instance to display mastership for
+ */
+ chooseMastership(instId: string): void {
+ if (this.mastership === instId) {
+ this.mastership = '';
+ } else {
+ this.mastership = instId;
+ }
+ this.mastershipEvent.emit(this.mastership);
+ this.log.debug('Instance', this.mastership, 'chosen on GUI');
+ }
}
diff --git a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.theme.css b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.theme.css
index a20711d..3be7bdd 100644
--- a/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.theme.css
+++ b/web/gui2/src/main/webapp/app/view/topology/panel/instance/instance.theme.css
@@ -34,4 +34,119 @@
}
.dark #topo-p-instance .online svg .glyph.overlay {
fill: #fff;
+}
+
+/* offline */
+#topo-p-instance svg .badgeIcon {
+ opacity: 0.4;
+ fill: #939598;
+}
+
+/* online */
+#topo-p-instance .online svg .badgeIcon {
+ opacity: 1.0;
+ fill: #939598;
+}
+#topo-p-instance .online svg .badgeIcon.bird {
+ fill: #ffffff;
+}
+
+#topo-p-instance svg .readyBadge {
+ visibility: hidden;
+}
+#topo-p-instance .ready svg .readyBadge {
+ visibility: visible;
+}
+
+#topo-p-instance svg text {
+ text-anchor: start;
+ opacity: 0.5;
+ fill: #3c3a3a;
+}
+
+#topo-p-instance .online svg text {
+ opacity: 1.0;
+ fill: #3c3a3a;
+}
+
+#topo-p-instance .onosInst.mastership {
+ opacity: 0.3;
+}
+#topo-p-instance .onosInst.mastership.affinity {
+ opacity: 1.0;
+}
+#topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.dark #topo-p-instance {
+ background-color: #2f313c;
+ color: #c2c2b7;
+ border: 1px solid #364144;
+
+}
+
+.dark #topo-p-instance svg rect {
+ stroke-width: 0;
+ fill: #525660;
+}
+
+/* body of an instance */
+.dark #topo-p-instance .online svg rect {
+ opacity: 1;
+ fill: #838992;
+}
+
+.dark #topo-p-instance svg .glyph {
+ fill: #ddd;
+}
+.dark #topo-p-instance .online svg .glyph {
+ fill: #fff;
+}
+.dark #topo-p-instance .online svg .glyph.overlay {
+ fill: #c7c7c7;
+}
+
+/* offline */
+.dark #topo-p-instance svg .badgeIcon {
+ opacity: 0.4;
+ fill: #939598;
+}
+
+/* online */
+.dark #topo-p-instance .online svg .badgeIcon {
+ opacity: 1.0;
+ fill: #939598;
+}
+.dark #topo-p-instance .online svg .badgeIcon.bird {
+ fill: #ffffff;
+}
+
+.dark #topo-p-instance svg text {
+ text-anchor: start;
+ opacity: 0.5;
+ fill: #aaa;
+}
+
+.dark #topo-p-instance .online svg text {
+ opacity: 1.0;
+ fill: #fff;
+}
+
+.dark #topo-p-instance .onosInst.mastership {
+ opacity: 0.3;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity {
+ opacity: 1.0;
+}
+.dark #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
+}
+
+.dark.firefox #topo-p-instance .onosInst.mastership.affinity svg rect {
+ filter: url(#blue-glow);
}
\ No newline at end of file
diff --git a/web/gui2/src/main/webapp/app/view/topology/panel/summary/summary.component.ts b/web/gui2/src/main/webapp/app/view/topology/panel/summary/summary.component.ts
index 7de5e89..c1c41f8 100644
--- a/web/gui2/src/main/webapp/app/view/topology/panel/summary/summary.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/panel/summary/summary.component.ts
@@ -94,7 +94,6 @@
handleSummaryData(data: SummaryResponse) {
this.summaryData = data;
this.render();
- this.log.debug('Summary', data);
}
private render() {
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology.module.ts b/web/gui2/src/main/webapp/app/view/topology/topology.module.ts
index a26f3ec..a954827 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology.module.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology.module.ts
@@ -27,6 +27,7 @@
import { BackgroundSvgComponent } from './layer/backgroundsvg/backgroundsvg.component';
import { ForceSvgComponent } from './layer/forcesvg/forcesvg.component';
import { MapSvgComponent } from './layer/mapsvg/mapsvg.component';
+import { TopologyService } from './topology.service';
/**
* ONOS GUI -- Topology View Module
@@ -51,6 +52,9 @@
BackgroundSvgComponent,
ForceSvgComponent,
MapSvgComponent
+ ],
+ providers: [
+ TopologyService
]
})
export class TopologyModule { }
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology.service.spec.ts b/web/gui2/src/main/webapp/app/view/topology/topology.service.spec.ts
index ca8711a..29d456f 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology.service.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology.service.spec.ts
@@ -14,23 +14,61 @@
* limitations under the License.
*/
import { TestBed, inject } from '@angular/core/testing';
-import { LogService, ConsoleLoggerService } from 'gui2-fw-lib';
+import { ActivatedRoute, Params } from '@angular/router';
+import {of} from 'rxjs';
+
import { TopologyService } from './topology.service';
+import {
+ LogService,
+ FnService
+} from 'gui2-fw-lib';
+
+class MockActivatedRoute extends ActivatedRoute {
+ constructor(params: Params) {
+ super();
+ this.queryParams = of(params);
+ }
+}
/**
* ONOS GUI -- Topology Service - Unit Tests
*/
describe('TopologyService', () => {
- let log: LogService;
+ let logServiceSpy: jasmine.SpyObj<LogService>;
+ let ar: ActivatedRoute;
+ let fs: FnService;
+ let mockWindow: Window;
beforeEach(() => {
- log = new ConsoleLoggerService();
+ const logSpy = jasmine.createSpyObj('LogService', ['debug', 'warn', 'info']);
+ ar = new MockActivatedRoute({'debug': 'TestService'});
+ mockWindow = <any>{
+ innerWidth: 400,
+ innerHeight: 200,
+ navigator: {
+ userAgent: 'defaultUA'
+ },
+ location: <any>{
+ hostname: 'foo',
+ host: 'foo',
+ port: '80',
+ protocol: 'http',
+ search: { debug: 'true' },
+ href: 'ws://foo:123/onos/ui/websock/path',
+ absUrl: 'ws://foo:123/onos/ui/websock/path'
+ }
+ };
+ fs = new FnService(ar, logSpy, mockWindow);
TestBed.configureTestingModule({
providers: [TopologyService,
- { provide: LogService, useValue: log },
+ { provide: FnService, useValue: fs},
+ { provide: LogService, useValue: logSpy },
+ { provide: ActivatedRoute, useValue: ar },
+ { provide: 'Window', useFactory: (() => mockWindow ) }
]
});
+ logServiceSpy = TestBed.get(LogService);
});
it('should be created', inject([TopologyService], (service: TopologyService) => {
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology.service.ts b/web/gui2/src/main/webapp/app/view/topology/topology.service.ts
index b75ed07..1d85a08 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology.service.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology.service.ts
@@ -15,9 +15,11 @@
*/
import { Injectable } from '@angular/core';
import {
- LogService,
+ LogService, WebSocketService,
} from 'gui2-fw-lib';
-
+import { InstanceComponent } from './panel/instance/instance.component';
+import { BackgroundSvgComponent } from './layer/backgroundsvg/backgroundsvg.component';
+import { ForceSvgComponent } from './layer/forcesvg/forcesvg.component';
/**
* ONOS GUI -- Topology Service Module.
@@ -25,19 +27,72 @@
@Injectable()
export class TopologyService {
+ private handlers: string[] = [];
+ private openListener: any;
+
constructor(
protected log: LogService,
+ protected wss: WebSocketService
) {
- this.log.debug('Initialized TopologyService');
+ this.log.debug('TopologyService constructed');
}
- init() {
+ /**
+ * bind our event handlers to the web socket service, so that our
+ * callbacks get invoked for incoming events
+ */
+ init(instance: InstanceComponent, background: BackgroundSvgComponent, force: ForceSvgComponent) {
+ this.wss.bindHandlers(new Map<string, (data) => void>([
+ ['topo2AllInstances', (data) => {
+ this.log.warn('Add fn for topo2AllInstances callback', data);
+ instance.onosInstances = data.members;
+ }
+ ],
+ ['topo2CurrentLayout', (data) => {
+ this.log.warn('Add fn for topo2CurrentLayout callback', data);
+ background.layoutData = data;
+ }
+ ],
+ ['topo2CurrentRegion', (data) => {
+ this.log.warn('Add fn for topo2CurrentRegion callback', data);
+ force.regionData = data;
+ }
+ ],
+ ['topo2PeerRegions', (data) => { this.log.warn('Add fn for topo2PeerRegions callback', data); } ],
+ ['topo2UiModelEvent', (data) => { this.log.warn('Add fn for topo2UiModelEvent callback', data); } ],
+ ['topo2Highlights', (data) => { this.log.warn('Add fn for topo2Highlights callback', data); } ],
+ ]));
+ this.handlers.push('topo2AllInstances');
+ this.handlers.push('topo2CurrentLayout');
+ this.handlers.push('topo2CurrentRegion');
+ this.handlers.push('topo2PeerRegions');
+ this.handlers.push('topo2UiModelEvent');
+ this.handlers.push('topo2Highlights');
+ // in case we fail over to a new server,
+ // listen for wsock-open events
+ this.openListener = this.wss.addOpenListener(() => this.wsOpen);
+ // tell the server we are ready to receive topology events
+ this.wss.sendEvent('topo2Start', {});
+ this.log.debug('TopologyService initialized');
}
+ /**
+ * tell the server we no longer wish to receive topology events
+ */
destroy() {
+ this.wss.sendEvent('topo2Stop', {});
+ this.wss.unbindHandlers(this.handlers);
+ this.wss.removeOpenListener(this.openListener);
+ this.openListener = null;
+ this.log.debug('TopologyService destroyed');
+ }
+ wsOpen(host: string, url: string) {
+ this.log.debug('topo2Event: WSopen - cluster node:', host, 'URL:', url);
+ // tell the server we are ready to receive topo events
+ this.wss.sendEvent('topo2Start', {});
}
}
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 0db2c82..d74991c 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
@@ -15,7 +15,7 @@
-->
<onos-flash id="topoMsgFlash" message="{{ flashMsg }}" (closed)="flashMsg = ''"></onos-flash>
-<onos-instance #instance></onos-instance>
+<onos-instance #instance [divTopPx]="80" (mastershipEvent)="force.onosInstMastership = $event"></onos-instance>
<onos-summary #summary></onos-summary>
<onos-toolbar #toolbar></onos-toolbar>
<onos-details #details></onos-details>
@@ -24,8 +24,8 @@
<svg viewBox="0 0 1000 1000" id="topo2">
<svg:g onos-nodeviceconnected />
<svg:g id="topo-zoomlayer">
- <svg:g onos-backgroundsvg/>
- <svg:g onos-forcesvg/>
+ <svg:g #background onos-backgroundsvg/>
+ <svg:g #force onos-forcesvg/>
</svg:g>
</svg>
</div>
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 5933d37..d98a55c 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
@@ -20,10 +20,14 @@
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TopologyComponent } from './topology.component';
-import { InstanceComponent } from '../panel/instance/instance.component';
+import {
+ Instance,
+ InstanceComponent
+} from '../panel/instance/instance.component';
import { SummaryComponent } from '../panel/summary/summary.component';
import { ToolbarComponent } from '../panel/toolbar/toolbar.component';
import { DetailsComponent } from '../panel/details/details.component';
+import { TopologyService } from '../topology.service';
import {
FlashComponent,
@@ -41,6 +45,32 @@
class MockHttpClient {}
+class MockTopologyService {
+ init(instance: InstanceComponent) {
+ instance.onosInstances = [
+ <Instance>{
+ 'id': 'inst1',
+ 'ip': '127.0.0.1',
+ 'reachable': true,
+ 'online': true,
+ 'ready': true,
+ 'switches': 4,
+ 'uiAttached': true
+ },
+ <Instance>{
+ 'id': 'inst1',
+ 'ip': '127.0.0.2',
+ 'reachable': true,
+ 'online': true,
+ 'ready': true,
+ 'switches': 3,
+ 'uiAttached': false
+ }
+ ];
+ }
+ destroy() {}
+}
+
/**
* ONOS GUI -- Topology View -- Unit Tests
*/
@@ -84,6 +114,7 @@
{ provide: LogService, useValue: logSpy },
{ provide: 'Window', useValue: windowMock },
{ provide: HttpClient, useClass: MockHttpClient },
+ { provide: TopologyService, useClass: MockTopologyService }
]
}).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 bf46637..728347d 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
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Component, OnInit, ViewChild } from '@angular/core';
+import {Component, OnDestroy, OnInit, ViewChild} from '@angular/core';
import * as d3 from 'd3';
import {
FnService,
@@ -21,9 +21,12 @@
LogService, PrefsService,
SvgUtilService, WebSocketService, Zoomer, ZoomOpts, ZoomService
} from 'gui2-fw-lib';
-import {InstanceComponent} from '../panel/instance/instance.component';
-import {SummaryComponent} from '../panel/summary/summary.component';
-import {DetailsComponent} from '../panel/details/details.component';
+import { InstanceComponent } from '../panel/instance/instance.component';
+import { SummaryComponent } from '../panel/summary/summary.component';
+import { DetailsComponent } from '../panel/details/details.component';
+import { BackgroundSvgComponent } from '../layer/backgroundsvg/backgroundsvg.component';
+import { ForceSvgComponent } from '../layer/forcesvg/forcesvg.component';
+import { TopologyService } from '../topology.service';
/**
* ONOS GUI Topology View
@@ -54,10 +57,13 @@
templateUrl: './topology.component.html',
styleUrls: ['./topology.component.css']
})
-export class TopologyComponent implements OnInit {
+export class TopologyComponent implements OnInit, OnDestroy {
+ // These are references to the components inserted in the template
@ViewChild(InstanceComponent) instance: InstanceComponent;
@ViewChild(SummaryComponent) summary: SummaryComponent;
@ViewChild(DetailsComponent) details: DetailsComponent;
+ @ViewChild(BackgroundSvgComponent) background: BackgroundSvgComponent;
+ @ViewChild(ForceSvgComponent) force: ForceSvgComponent;
flashMsg: string = '';
prefsState = {};
@@ -73,7 +79,8 @@
protected sus: SvgUtilService,
protected ps: PrefsService,
protected wss: WebSocketService,
- protected zs: ZoomService
+ protected zs: ZoomService,
+ protected ts: TopologyService,
) {
this.log.debug('Topology component constructed');
@@ -90,9 +97,19 @@
zoomCallback: (() => { return; })
});
this.zoomEventListeners = [];
+ // The components from the template are handed over to TopologyService here
+ // so that WebSocket responses can be passed back in to them
+ // The handling of the WebSocket call is delegated out to the Topology
+ // Service just to compartmentalize things a bit
+ this.ts.init(this.instance, this.background, this.force);
this.log.debug('Topology component initialized');
}
+ ngOnDestroy() {
+ this.ts.destroy();
+ this.log.debug('Topology component destroyed');
+ }
+
actionMap() {
return {
L: [() => {this.cycleDeviceLabels(); }, 'Cycle device labels'],