GUI2 Handle node additions and removals in Topology view
Change-Id: Ic16fc1325fe338e2136f1cc70febc621342be4f2
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
index 36375eb..5ca86e3 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiHost.java
@@ -73,6 +73,16 @@
}
/**
+ * Returns the identifier of the region to which this device belongs.
+ * This will be null if the device does not belong to any region.
+ *
+ * @return region ID
+ */
+ public RegionId regionId() {
+ return regionId;
+ }
+
+ /**
* Sets the ID of the region to which this device belongs.
*
* @param regionId region identifier
diff --git a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
index 74c8677..36629f2 100644
--- a/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
+++ b/core/api/src/main/java/org/onosproject/ui/model/topo/UiRegion.java
@@ -40,6 +40,7 @@
private static final String NULL_NAME = "(root)";
private static final String NO_NAME = "???";
+ private static final String MEMO_ADDED = "added";
/**
* The identifier for the null-region. That is, a container for devices,
@@ -327,10 +328,24 @@
return true;
case REGION_ADDED_OR_UPDATED:
+ if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
+ regionId.toString().equalsIgnoreCase(
+ ((UiRegion) event.subject()).backingRegion().toString())) {
+ return true;
+ } else {
+ return isDeviceRelevant(((UiDevice) event.subject()).id());
+ }
case REGION_REMOVED:
return isRegionRelevant(((UiRegion) event.subject()).id());
case DEVICE_ADDED_OR_UPDATED:
+ if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
+ regionId.toString().equalsIgnoreCase(
+ ((UiDevice) event.subject()).regionId().toString())) {
+ return true;
+ } else {
+ return isDeviceRelevant(((UiDevice) event.subject()).id());
+ }
case DEVICE_REMOVED:
return isDeviceRelevant(((UiDevice) event.subject()).id());
@@ -339,6 +354,13 @@
return isLinkRelevant((UiLink) event.subject());
case HOST_ADDED_OR_UPDATED:
+ if (MEMO_ADDED.equalsIgnoreCase(event.memo()) &&
+ regionId.toString().equalsIgnoreCase(
+ ((UiHost) event.subject()).regionId().toString())) {
+ return true;
+ } else {
+ return isDeviceRelevant(((UiDevice) event.subject()).id());
+ }
case HOST_MOVED:
case HOST_REMOVED:
return isDeviceRelevant(((UiHost) event.subject()).locationDevice());
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.html b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.html
index b673b24..58606e9 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/forcesvg.component.html
@@ -89,7 +89,7 @@
<svg:g onos-hostnodesvg [host]="host"
*ngFor="let host of regionData.hosts[visibleLayerIdx()]"
onosDraggableNode [draggableNode]="host" [draggableInGraph]="graph"
- (newLocation)="nodeMoved('host', device.id, $event)"
+ (newLocation)="nodeMoved('host', host.id, $event)"
(selectedEvent)="updateSelected($event)"
[labelToggle]="hostLabelToggle"
[scale]="scale">
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 ebc70aa..48d973b 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
@@ -57,10 +57,7 @@
HostNodeSvgComponent,
LinkSvgComponent
} from './visuals';
-import {
- BackgroundSvgComponent,
- LocationType
-} from '../backgroundsvg/backgroundsvg.component';
+import {LocationType} from '../backgroundsvg/backgroundsvg.component';
interface UpdateMeta {
id: string;
@@ -130,6 +127,28 @@
}
}
+ /**
+ * Recursive method to compare 2 objects attribute by attribute and update
+ * the first where a change is detected
+ * @param existingNode 1st object
+ * @param updatedNode 2nd object
+ */
+ private static updateObject(existingNode: Object, updatedNode: Object): number {
+ let changed: number = 0;
+ for (const key of Object.keys(updatedNode)) {
+ const o = updatedNode[key];
+ if (key === 'id') {
+ continue;
+ } else if (o && typeof o === 'object' && o.constructor === Object) {
+ changed += ForceSvgComponent.updateObject(existingNode[key], updatedNode[key]);
+ } else if (existingNode[key] !== updatedNode[key]) {
+ changed++;
+ existingNode[key] = updatedNode[key];
+ }
+ }
+ return changed;
+ }
+
@HostListener('window:resize', ['$event'])
onResize(event) {
this.graph.initSimulation(this.options);
@@ -310,25 +329,32 @@
case ModelEventType.DEVICE_ADDED_OR_UPDATED:
if (memo === ModelEventMemo.ADDED) {
this.regionData.devices[this.visibleLayerIdx()].push(<Device>data);
+ this.graph.nodes.push(<Device>data);
+ this.log.debug('Device added', (<Device>data).id);
} else if (memo === ModelEventMemo.UPDATED) {
const oldDevice: Device =
this.regionData.devices[this.visibleLayerIdx()]
.find((d) => d.id === subject);
- this.compareDevice(oldDevice, <Device>data);
+ const changes = ForceSvgComponent.updateObject(oldDevice, <Device>data);
+ if (changes > 0) {
+ this.log.debug('Device ', oldDevice.id, memo, ' - ', changes, 'changes');
+ }
} else {
this.log.warn('Device ', memo, ' - not yet implemented', data);
}
- this.log.warn('Device ', memo, ' - not yet implemented', data);
break;
case ModelEventType.HOST_ADDED_OR_UPDATED:
if (memo === ModelEventMemo.ADDED) {
this.regionData.hosts[this.visibleLayerIdx()].push(<Host>data);
- this.log.warn('Host added - not yet implemented', data);
+ this.graph.nodes.push(<Host>data);
+ this.log.debug('Host added', (<Host>data).id);
} else if (memo === ModelEventMemo.UPDATED) {
const oldHost: Host = this.regionData.hosts[this.visibleLayerIdx()]
.find((h) => h.id === subject);
- this.compareHost(oldHost, <Host>data);
- this.log.warn('Host updated - not yet implemented', data);
+ const changes = ForceSvgComponent.updateObject(oldHost, <Host>data);
+ if (changes > 0) {
+ this.log.debug('Host ', oldHost.id, memo, ' - ', changes, 'changes');
+ }
} else {
this.log.warn('Host change', memo, ' - unexpected');
}
@@ -338,9 +364,9 @@
const removeIdx: number =
this.regionData.devices[this.visibleLayerIdx()]
.findIndex((d) => d.id === subject);
- const removeCmpt: DeviceNodeSvgComponent =
- this.devices.find((dc) => dc.device.id === subject);
- this.log.warn('Device ', subject, 'removed - not yet implemented', removeIdx, removeCmpt.device.id);
+ this.regionData.devices[this.visibleLayerIdx()].splice(removeIdx, 1);
+ this.removeRelatedLinks(subject);
+ this.log.debug('Device ', subject, 'removed. Links', this.regionData.links);
} else {
this.log.warn('Device removed - unexpected memo', memo);
}
@@ -350,33 +376,53 @@
const removeIdx: number =
this.regionData.hosts[this.visibleLayerIdx()]
.findIndex((h) => h.id === subject);
- const removeCmpt: HostNodeSvgComponent =
- this.hosts.find((hc) => hc.host.id === subject);
- this.log.warn('Host ', subject, 'removed - not yet implemented', removeIdx, removeCmpt.host.id);
+ this.regionData.hosts[this.visibleLayerIdx()].splice(removeIdx, 1);
+ this.removeRelatedLinks(subject);
+ this.log.warn('Host ', subject, 'removed');
} else {
this.log.warn('Host removed - unexpected memo', memo);
}
break;
case ModelEventType.LINK_ADDED_OR_UPDATED:
- this.log.warn('link added or updated - not yet implemented', subject);
+ if (memo === ModelEventMemo.ADDED &&
+ this.regionData.links.findIndex((l) => l.id === subject) === -1) {
+ const listLen = this.regionData.links.push(<RegionLink>data);
+ const epA = ForceSvgComponent.extractNodeName(
+ this.regionData.links[listLen - 1].epA);
+ this.regionData.links[listLen - 1].source =
+ this.graph.nodes.find((node) =>
+ node.id === epA);
+ const epB = ForceSvgComponent.extractNodeName(
+ this.regionData.links[listLen - 1].epB);
+ this.regionData.links[listLen - 1].target =
+ this.graph.nodes.find((node) =>
+ node.id === epB);
+ this.log.debug('Link added', subject);
+ } else if (memo === ModelEventMemo.UPDATED) {
+ const oldLink = this.regionData.links.find((l) => l.id === subject);
+ const changes = ForceSvgComponent.updateObject(oldLink, <RegionLink>data);
+ this.log.debug('Link ', subject, '. Updated', changes, 'items');
+ } else {
+ this.log.warn('Link added or updated - unexpected memo', memo);
+ }
break;
default:
this.log.error('Unexpected model event', type, 'for', subject);
}
+ this.ref.markForCheck();
+ this.graph.initSimulation(this.options);
}
- private compareDevice(oldDevice: Device, updatedDevice: Device) {
- if (oldDevice.master !== updatedDevice.master) {
- this.log.debug('Mastership has changed for', updatedDevice.id, 'to', updatedDevice.master);
- }
- if (oldDevice.online !== updatedDevice.online) {
- this.log.debug('Status has changed for', updatedDevice.id, 'to', updatedDevice.online);
- }
- }
-
- private compareHost(oldHost: Host, updatedHost: Host) {
- if (oldHost.configured !== updatedHost.configured) {
- this.log.debug('Configured has changed for', updatedHost.id, 'to', updatedHost.configured);
+ private removeRelatedLinks(subject: string) {
+ const len = this.regionData.links.length;
+ for (let i = 0; i < len; i++) {
+ const linkIdx = this.regionData.links.findIndex((l) =>
+ (ForceSvgComponent.extractNodeName(l.epA) === subject ||
+ ForceSvgComponent.extractNodeName(l.epB) === subject));
+ if (linkIdx >= 0) {
+ this.regionData.links.splice(linkIdx, 1);
+ this.log.debug('Link ', linkIdx, 'removed on attempt', i);
+ }
}
}
diff --git a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
index ce5441f..af280a4 100644
--- a/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/layer/forcesvg/models/node.ts
@@ -111,6 +111,9 @@
name: string;
locType: LocationType;
uiType: string;
+ channelId: string;
+ managementAddress: string;
+ protocol: string;
}
export interface HostProps {
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 8644ac1..8f3378f 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
@@ -56,7 +56,7 @@
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"
- preserveAspectRatio="xMaxYMax none">
+ preserveAspectRatio="xMaxYMax meet">
<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 +
diff --git a/web/gui2/src/main/webapp/login.html b/web/gui2/src/main/webapp/login.html
index d05260f..40676fb 100644
--- a/web/gui2/src/main/webapp/login.html
+++ b/web/gui2/src/main/webapp/login.html
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<title>ONOS Login</title>
-
+ <link rel="shortcut icon" href="data/img/onos-logo.png">
<style type="text/css">
img {
margin: 24px;