[CORD-2424] Adding Instances and Networks to the graph
Change-Id: Ib30081f4995930d979447af59124896f1308f54d
diff --git a/docs/developer/service_graph.md b/docs/developer/service_graph.md
index 29c54e2..caf076c 100644
--- a/docs/developer/service_graph.md
+++ b/docs/developer/service_graph.md
@@ -10,6 +10,8 @@
- `Shift + f` toggle fullscreen mode
- `Shift + s` to add `ServiceInstances` to the graph
+- `Shift + i` to add `Instances` to the graph (this require `ServiceInstances` to be shown)
+- `Shift + n` to add `Networks` to the graph (this require `Instances` to be shown)
## Define the position of the Services in the graph
diff --git a/src/app/core/services/helpers/config.helpers.spec.ts b/src/app/core/services/helpers/config.helpers.spec.ts
index f36c20a..db1c840 100644
--- a/src/app/core/services/helpers/config.helpers.spec.ts
+++ b/src/app/core/services/helpers/config.helpers.spec.ts
@@ -187,7 +187,7 @@
});
it('should return the state with params for usage in js', () => {
- expect(service.stateWithParamsForJs('Test', {id: 1})).toEqual({ name: 'xos.core.tests', params: Object({ id: 1 }) });
+ expect(service.stateWithParamsForJs('Test', 1)).toEqual({ name: 'xos.core.tests', params: Object({ id: 1 }) });
});
});
});
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 91db9f2..4baf9c5 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -196,7 +196,7 @@
};
if (f.name === 'id' || f.name === 'name') {
- col.link = item => this.stateWithParamsForJs(modelName, item);
+ col.link = item => this.stateWithParamsForJs(modelName, item.id);
}
// if the field identify a relation, create a link
@@ -206,7 +206,7 @@
this.populateRelated(item, item[f.name], f);
return item[f.name];
};
- col.link = item => this.relatedStateWithParams(f.relation.model, item[col.prop]);
+ col.link = item => this.stateWithParamsForJs(f.relation.model, item[col.prop]);
}
if (f.name === 'backend_status' || f.name === 'policy_status') {
@@ -257,9 +257,9 @@
return `${state}({id: ${id}})`;
}
- public stateWithParamsForJs(name: string, model: any): any {
+ public stateWithParamsForJs(name: string, id: number): any {
const state = this.stateFromCoreModel(name);
- return {name: state, params: {id: model.id}};
+ return {name: state, params: {id: id}};
}
public modelFieldToInputCfg(fields: IXosModelDefsField[]): IXosFormInput[] {
diff --git a/src/app/core/services/keyboard-shortcut.spec.ts b/src/app/core/services/keyboard-shortcut.spec.ts
index fa9bf41..d113161 100644
--- a/src/app/core/services/keyboard-shortcut.spec.ts
+++ b/src/app/core/services/keyboard-shortcut.spec.ts
@@ -195,4 +195,65 @@
expect(logSpy).toHaveBeenCalledWith('[XosKeyboardShortcut] A shortcut for key "a" has already been registered');
});
});
+
+ describe('the removeKeyBinding method', () => {
+
+ const globalBinding = {
+ key: 'g',
+ cb: 'callback',
+ modifiers: null
+ };
+
+ const viewBinding = {
+ key: 'v',
+ cb: 'callback',
+ modifiers: null
+ };
+
+ const missingBinding = {
+ key: 'b',
+ cb: 'callback',
+ modifiers: null
+ };
+
+ beforeEach(() => {
+ service['keyMapping'] = {
+ global: [
+ {
+ key: 'g',
+ cb: 'cb',
+ modifiers: null
+ }
+ ],
+ view: [
+ {
+ key: 'v',
+ cb: 'cb',
+ modifiers: null
+ }
+ ]
+ };
+ });
+
+ it(' should remove an existing global keybinding', () => {
+ service.removeKeyBinding(globalBinding);
+ expect(logSpy).not.toHaveBeenCalled();
+ expect(service['keyMapping']['global'].length).toBe(0);
+ expect(service['keyMapping']['view'].length).toBe(1);
+ });
+
+ it(' should remove an existing view keybinding', () => {
+ service.removeKeyBinding(viewBinding);
+ expect(logSpy).not.toHaveBeenCalled();
+ expect(service['keyMapping']['global'].length).toBe(1);
+ expect(service['keyMapping']['view'].length).toBe(0);
+ });
+
+ it('should not raise if removing a missing keybing', () => {
+ service.removeKeyBinding(missingBinding);
+ expect(service['keyMapping']['global'].length).toBe(1);
+ expect(service['keyMapping']['view'].length).toBe(1);
+ expect(logSpy).toHaveBeenCalledWith('[XosKeyboardShortcut] Trying to remove a shortcut for key "b" but it was not registered');
+ });
+ });
});
diff --git a/src/app/core/services/keyboard-shortcut.ts b/src/app/core/services/keyboard-shortcut.ts
index 9b98b45..e2a3a38 100644
--- a/src/app/core/services/keyboard-shortcut.ts
+++ b/src/app/core/services/keyboard-shortcut.ts
@@ -23,6 +23,7 @@
export interface IXosKeyboardShortcutService {
keyMapping: IXosKeyboardShortcutMap;
registerKeyBinding(binding: IXosKeyboardShortcutBinding, target?: string);
+ removeKeyBinding(binding: IXosKeyboardShortcutBinding);
setup(): void;
}
@@ -64,11 +65,17 @@
public baseBindings: IXosKeyboardShortcutBinding[] = [
{
key: 'slash',
- label: '/',
+ label: '/ or ?',
description: 'Toggle Shortcut Panel',
cb: this.toggleKeyBindingPanel,
},
{
+ // NOTE this is the ? code
+ key: 'slash',
+ modifiers: ['shift'],
+ cb: this.toggleKeyBindingPanel,
+ },
+ {
key: 'esc',
label: 'Esc',
cb: (event) => {
@@ -102,6 +109,7 @@
$('body').on('keydown', (e) => {
const pressedKey = this.whatKey(e.which);
+ this.$log.debug(`[XosKeyboardShortcut] Pressed key: ${pressedKey}`);
if (!pressedKey) {
return;
}
@@ -149,6 +157,18 @@
this.keyMapping[target].push(binding);
}
+ public removeKeyBinding(binding: IXosKeyboardShortcutBinding): void {
+ if (_.find(this.keyMapping.global, {key: binding.key, modifiers: binding.modifiers})) {
+ _.remove(this.keyMapping.global, {key: binding.key, modifiers: binding.modifiers});
+ }
+ else if (_.find(this.keyMapping.view, {key: binding.key, modifiers: binding.modifiers})) {
+ _.remove(this.keyMapping.view, {key: binding.key, modifiers: binding.modifiers});
+ }
+ else {
+ this.$log.warn(`[XosKeyboardShortcut] Trying to remove a shortcut for key "${binding.key}" but it was not registered`);
+ }
+ }
+
private addActiveModifierKey(key: string) {
if (this.activeModifiers.indexOf(key) === -1) {
this.activeModifiers.push(key);
diff --git a/src/app/service-graph/components/graph/graph.component.scss b/src/app/service-graph/components/graph/graph.component.scss
index 40b6f11..5d773a1 100644
--- a/src/app/service-graph/components/graph/graph.component.scss
+++ b/src/app/service-graph/components/graph/graph.component.scss
@@ -73,12 +73,12 @@
.node {
cursor: pointer;
+ fill: $background-color;
}
.node.service {
> rect {
stroke: $color-accent;
- fill: $background-color;
}
> .icon {
fill: $color-accent;
@@ -87,11 +87,28 @@
.node.serviceinstance {
> rect {
- stroke: green;
- fill: $background-color;
+ stroke: $serviceinstances-fg;
}
> .icon {
- fill: green;
+ fill: $serviceinstances-fg;
+ }
+ }
+
+ .node.instance {
+ > rect {
+ stroke: $instances-fg;
+ }
+ > .icon {
+ fill: $instances-fg;
+ }
+ }
+
+ .node.network {
+ > rect {
+ stroke: $networks-fg;
+ }
+ > .icon {
+ fill: $networks-fg;
}
}
@@ -114,12 +131,21 @@
}
line.ownership {
- stroke: green;
+ stroke: $serviceinstances-fg;
stroke-dasharray: 5;
}
line.serviceinstancelink {
- stroke: green;
+ stroke: $serviceinstances-fg;
+ }
+
+ line.instance_ownership {
+ stroke: $instances-fg;
+ stroke-dasharray: 5;
+ }
+
+ line.port {
+ stroke: $networks-fg;
}
}
.arrow-marker {
diff --git a/src/app/service-graph/components/graph/graph.component.ts b/src/app/service-graph/components/graph/graph.component.ts
index 5f84384..ed36654 100644
--- a/src/app/service-graph/components/graph/graph.component.ts
+++ b/src/app/service-graph/components/graph/graph.component.ts
@@ -26,7 +26,7 @@
import {IXosServiceGraphIcons} from '../../services/d3-helpers/graph-icons.service';
import {IXosNodePositioner} from '../../services/node-positioner.service';
import {IXosNodeRenderer} from '../../services/renderer/node.renderer';
-import {IXosSgNode} from '../../interfaces';
+import {IXosSgLink, IXosSgNode} from '../../interfaces';
import {IXosGraphConfig} from '../../services/graph.config';
class XosServiceGraphCtrl {
@@ -74,7 +74,6 @@
graph => {
this.graph = graph;
if (this.graph.nodes().length > 0) {
- this.loader = false;
this.renderGraph(this.graph);
}
},
@@ -154,6 +153,7 @@
this.XosNodePositioner.positionNodes(svgDim, nodes)
.then((nodes: IXosSgNode[]) => {
+ this.loader = false;
this.forceLayout
.nodes(nodes)
@@ -162,6 +162,15 @@
.linkDistance(config.force.linkDistance)
.charge(config.force.charge)
.gravity(config.force.gravity)
+ .linkStrength((link: IXosSgLink) => {
+ switch (link.type) {
+ case 'ownership':
+ case 'instance_ownership':
+ // NOTE make "ownsership" links stronger than other for positioning
+ return 1;
+ }
+ return 0.1;
+ })
.start();
// render nodes
diff --git a/src/app/service-graph/services/d3-helpers/graph-icons.service.ts b/src/app/service-graph/services/d3-helpers/graph-icons.service.ts
index 9baeb57..de03a62 100644
--- a/src/app/service-graph/services/d3-helpers/graph-icons.service.ts
+++ b/src/app/service-graph/services/d3-helpers/graph-icons.service.ts
@@ -34,6 +34,18 @@
'M13,0a1,1,0,0,1,.53.2l9.4,5.45c.54.32.54.45,0,.78C19.78,8.2,16.7,10,13.63,11.74a1.12,1.12,0,0,1-1.24,0C9.28,9.92,6.15,8.12,3,6.31c-.55-.31-.55-.46,0-.78L12.45.19A.86.86,0,0,1,13,0Z\n' +
'M24.73,14.16c0,1.81,0,3.61,0,5.42a.8.8,0,0,1-.46.79l-9.36,5.41c-.64.38-.79.29-.79-.47,0-3.53,0-7.07,0-10.6a.92.92,0,0,1,.52-.94q4.63-2.65,9.26-5.35l.24-.14c.43-.2.58-.11.58.36Z',
transform: ''
+ },
+ {
+ name: 'instance',
+ path: 'M11.87,19.94v5.47c0,.61-.18.72-.69.42l-9.6-5.55a.62.62,0,0,1-.32-.64c0-3.64,0-7.29,0-10.94,0-.7.16-.79.79-.42,2.89,1.67,5.77,3.39,8.69,5a1.81,1.81,0,0,1,1.14,2C11.8,16.81,11.87,18.38,11.87,19.94Z\n' +
+ 'M13,0a1,1,0,0,1,.53.2l9.4,5.45c.54.32.54.45,0,.78C19.78,8.2,16.7,10,13.63,11.74a1.12,1.12,0,0,1-1.24,0C9.28,9.92,6.15,8.12,3,6.31c-.55-.31-.55-.46,0-.78L12.45.19A.86.86,0,0,1,13,0Z\n' +
+ 'M24.73,14.16c0,1.81,0,3.61,0,5.42a.8.8,0,0,1-.46.79l-9.36,5.41c-.64.38-.79.29-.79-.47,0-3.53,0-7.07,0-10.6a.92.92,0,0,1,.52-.94q4.63-2.65,9.26-5.35l.24-.14c.43-.2.58-.11.58.36Z',
+ transform: ''
+ },
+ {
+ name: 'network',
+ path: 'M15.62,1.56a4.69,4.69,0,0,1,4.69,4.63,2.83,2.83,0,0,0,0,.29l0,1.15L21.33,8a3.11,3.11,0,0,1-1,6.06H4.69a3.13,3.13,0,0,1-.05-6.25l.22,0,1.19.07.39-1.12a3.12,3.12,0,0,1,2.94-2.1,3,3,0,0,1,.54.06l1.1.19.54-1a4.7,4.7,0,0,1,4.06-2.41m0-1.56A6.2,6.2,0,0,0,10.2,3.21a4.48,4.48,0,0,0-.82-.09A4.67,4.67,0,0,0,5,6.28l-.28,0a4.69,4.69,0,1,0,0,9.37H20.31a4.67,4.67,0,0,0,1.54-9.09c0-.1,0-.19,0-.28A6.25,6.25,0,0,0,15.62,0Z',
+ transform: ''
}
];
diff --git a/src/app/service-graph/services/graph.config.ts b/src/app/service-graph/services/graph.config.ts
index 7db2d97..e6ddfbd 100644
--- a/src/app/service-graph/services/graph.config.ts
+++ b/src/app/service-graph/services/graph.config.ts
@@ -34,6 +34,33 @@
'XosGraphStore'
];
+ private instanceEnabled = false;
+ private instanceBinding = {
+ key: 'i',
+ modifiers: ['shift'],
+ cb: () => {
+ // NOTE anytime the graph change the observable is updated,
+ // no need to manually retrigger here
+ this.XosGraphStore.toggleInstances();
+ this.toggleNetworkShortcuts();
+ },
+ label: 'i',
+ description: 'Toggle Instances'
+ };
+
+ private networkEnabled = false;
+ private networkBinding = {
+ key: 'n',
+ modifiers: ['shift'],
+ cb: () => {
+ // NOTE anytime the graph change the observable is updated,
+ // no need to manually retrigger here
+ this.XosGraphStore.toggleNetwork();
+ },
+ label: 'n',
+ description: 'Toggle Networks'
+ };
+
constructor (
private $log: ng.ILogService,
private $cookies: ng.cookies.ICookiesService,
@@ -64,13 +91,13 @@
key: 's',
modifiers: ['shift'],
cb: () => {
- // NOTE anytime the graph change the observable is updated,
- // no need to manually retrigger here
this.XosGraphStore.toggleServiceInstances();
+ this.toggleInstanceShortcuts();
},
label: 's',
description: 'Toggle ServiceInstances'
});
+
}
public toggleFullscreen() {
@@ -80,4 +107,26 @@
this.$rootScope.$broadcast('xos.sg.update');
}, 500);
}
+
+ private toggleInstanceShortcuts(): void {
+ if (!this.instanceEnabled) {
+ this.XosKeyboardShortcut.registerKeyBinding(this.instanceBinding);
+ }
+ else {
+ this.XosKeyboardShortcut.removeKeyBinding(this.instanceBinding);
+ this.XosKeyboardShortcut.removeKeyBinding(this.networkBinding);
+ }
+ this.instanceEnabled = !this.instanceEnabled;
+ }
+
+ private toggleNetworkShortcuts(): void {
+ if (!this.networkEnabled) {
+ this.XosKeyboardShortcut.registerKeyBinding(this.networkBinding);
+ }
+ else {
+ this.XosKeyboardShortcut.removeKeyBinding(this.networkBinding);
+ }
+
+ this.networkEnabled = !this.networkEnabled;
+ }
}
diff --git a/src/app/service-graph/services/graph.store.ts b/src/app/service-graph/services/graph.store.ts
index 4d46c88..52100e7 100644
--- a/src/app/service-graph/services/graph.store.ts
+++ b/src/app/service-graph/services/graph.store.ts
@@ -30,6 +30,8 @@
nodesFromGraph(graph: Graph): IXosSgNode[];
linksFromGraph(graph: Graph): IXosSgLink[];
toggleServiceInstances(): Graph;
+ toggleInstances(): Graph;
+ toggleNetwork(): Graph;
}
export class XosGraphStore implements IXosGraphStore {
@@ -41,16 +43,22 @@
// state
private serviceInstanceShown: boolean = false;
+ private instanceShown: boolean = false;
+ private networkShown: boolean = false;
// graphs
private serviceGraph: any;
private ServiceGraphSubject: BehaviorSubject<any>;
// datastore
+ private InstanceSubscription: Subscription;
+ private NetworkSubscription: Subscription;
+ private PortSubscription: Subscription;
private ServiceSubscription: Subscription;
private ServiceDependencySubscription: Subscription;
private ServiceInstanceSubscription: Subscription;
private ServiceInstanceLinkSubscription: Subscription;
+ private TenantWithContainerSubscription: Subscription;
// debounced
private efficientNext = this.XosDebouncer.debounce(this.callNext, 500, this, false);
@@ -76,6 +84,7 @@
public nodesFromGraph(graph: Graph): IXosSgNode[] {
return _.map(graph.nodes(), (n: string) => {
const nodeData = graph.node(n);
+
return {
id: n,
type: this.getModelType(nodeData),
@@ -87,16 +96,9 @@
public linksFromGraph(graph: Graph): IXosSgLink[] {
const nodes = this.nodesFromGraph(graph);
- // NOTE we'll need some intelligence here to differentiate between:
- // - ServiceDependency
- // - ServiceInstanceLinks
- // - Owners
-
return _.map(graph.edges(), l => {
const link = graph.edge(l);
const linkType = this.getModelType(link);
-
- // FIXME consider ownership links
let sourceId, targetId;
switch (linkType) {
@@ -105,17 +107,28 @@
targetId = this.getServiceId(link.provider_service_id);
break;
case 'serviceinstancelink':
+ // NOTE ServiceInstanceLink can actually also connect to a service and a network
sourceId = this.getServiceInstanceId(link.subscriber_service_instance_id);
targetId = this.getServiceInstanceId(link.provider_service_instance_id);
break;
case 'ownership':
sourceId = this.getServiceId(link.service);
targetId = this.getServiceInstanceId(link.service_instance);
+ break;
+ case 'instance_ownership':
+ sourceId = this.getServiceInstanceId(link.id);
+ targetId = this.getInstanceId(link.instance_id);
+ break;
+ case 'port':
+ sourceId = this.getInstanceId(link.instance_id);
+ targetId = this.getNetworkId(link.network_id);
+ break;
}
// NOTE help while debugging
if (!sourceId || !targetId) {
this.$log.warn(`Link ${l.v}-${l.w} has missing source or target:`, l, link);
+ // TODO return null and then filter out so that we don't break the rendering
}
return {
@@ -136,6 +149,18 @@
// remove nodes from the graph
this.removeElementsFromGraph('serviceinstance'); // NOTE links are automatically removed by the graph library
+
+ if (this.instanceShown) {
+ // NOTE if we remove ServiceInstances we also need to remove Instances
+ this.removeElementsFromGraph('instance');
+ this.instanceShown = false;
+ }
+
+ if (this.networkShown) {
+ // NOTE if we remove ServiceInstances we also need to remove Networks
+ this.removeElementsFromGraph('network');
+ this.networkShown = false;
+ }
}
else {
// NOTE subscribe to ServiceInstance and ServiceInstanceLink observables
@@ -146,6 +171,43 @@
return this.serviceGraph;
}
+ public toggleInstances(): Graph {
+ if (this.instanceShown) {
+
+ this.InstanceSubscription.unsubscribe();
+ this.TenantWithContainerSubscription.unsubscribe();
+
+ this.removeElementsFromGraph('instance'); // NOTE links are automatically removed by the graph library
+
+ if (this.networkShown) {
+ // NOTE if we remove Instances we also need to remove Networks
+ this.removeElementsFromGraph('network');
+ this.networkShown = false;
+ }
+ }
+ else {
+ this.loadInstances();
+ this.loadInstanceLinks();
+ }
+ this.instanceShown = !this.instanceShown;
+ return this.serviceGraph;
+ }
+
+ public toggleNetwork() {
+ if (this.networkShown) {
+ this.NetworkSubscription.unsubscribe();
+ this.PortSubscription.unsubscribe();
+ this.removeElementsFromGraph('network');
+ }
+ else {
+ this.loadNetworks();
+ this.loadPorts(); // Ports define the connection of an Instance to a Network
+ }
+
+ this.networkShown = !this.networkShown;
+ return this.serviceGraph;
+ }
+
public get(): Observable<Graph> {
return this.ServiceGraphSubject.asObservable();
}
@@ -194,6 +256,22 @@
this.serviceGraph.setEdge(sourceId, targetId, link);
}
+ private addInstanceOwner(tenantWithContainer: any) {
+ // NOTE some TenantWithContainer don't have an instance
+ if (tenantWithContainer.instance_id) {
+ const sourceId = this.getServiceInstanceId(tenantWithContainer.id);
+ const targetId = this.getInstanceId(tenantWithContainer.instance_id);
+ this.serviceGraph.setEdge(sourceId, targetId, angular.merge(tenantWithContainer, {type: 'instance_ownership'}));
+ }
+ }
+
+ private addNetworkLink(port: any) {
+ // ports are connected to 1 Instance and 1 Network
+ const sourceId = this.getInstanceId(port.instance_id);
+ const targetId = this.getNetworkId(port.network_id);
+ this.serviceGraph.setEdge(sourceId, targetId, angular.merge(port, {type: 'port'}));
+ }
+
private removeElementsFromGraph(type: string) {
_.forEach(this.serviceGraph.nodes(), (n: string) => {
const node = this.serviceGraph.node(n);
@@ -209,7 +287,7 @@
// helpers
private getModelType(node: IXosBaseModel): string {
if (node.type) {
- // NOTE we'll add "ownership" links
+ // NOTE handling "ownership" links
return node.type;
}
return node.class_names.split(',')[0].toLowerCase();
@@ -223,6 +301,14 @@
return `serviceinstance~${id}`;
}
+ private getInstanceId(id: number): string {
+ return `instance~${id}`;
+ }
+
+ private getNetworkId(id: number): string {
+ return `network~${id}`;
+ }
+
private getNodeId(node: IXosBaseModel): string {
const nodeType = this.getModelType(node);
@@ -231,6 +317,10 @@
return this.getServiceId(node.id);
case 'serviceinstance':
return this.getServiceInstanceId(node.id);
+ case 'instance':
+ return this.getInstanceId(node.id);
+ case 'network':
+ return this.getNetworkId(node.id);
}
}
@@ -303,6 +393,74 @@
);
}
+ private loadInstances() {
+ this.InstanceSubscription = this.XosModelStore.query('Instance', '/core/instances')
+ .subscribe(
+ (res) => {
+ if (res.length > 0) {
+ _.forEach(res, n => {
+ this.addNode(n);
+ });
+ this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+ }
+ },
+ (err) => {
+ this.$log.error(`[XosServiceGraphStore] Instance Observable: `, err);
+ }
+ );
+ }
+
+ private loadInstanceLinks() {
+ this.TenantWithContainerSubscription = this.XosModelStore.query('TnantWithContainer', '/core/tenantwithcontainers')
+ .subscribe(
+ (res) => {
+ if (res.length > 0) {
+ _.forEach(res, n => {
+ this.addInstanceOwner(n);
+ });
+ this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+ }
+ },
+ (err) => {
+ this.$log.error(`[XosServiceGraphStore] Instance Observable: `, err);
+ }
+ );
+ }
+
+ private loadNetworks() {
+ this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
+ .subscribe(
+ (res) => {
+ if (res.length > 0) {
+ _.forEach(res, n => {
+ this.addNode(n);
+ });
+ this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+ }
+ },
+ (err) => {
+ this.$log.error(`[XosServiceGraphStore] Network Observable: `, err);
+ }
+ );
+ }
+
+ private loadPorts() {
+ this.PortSubscription = this.XosModelStore.query('Port', '/core/ports')
+ .subscribe(
+ (res) => {
+ if (res.length > 0) {
+ _.forEach(res, n => {
+ this.addNetworkLink(n);
+ });
+ this.efficientNext(this.ServiceGraphSubject, this.serviceGraph);
+ }
+ },
+ (err) => {
+ this.$log.error(`[XosServiceGraphStore] Network Observable: `, err);
+ }
+ );
+ }
+
private callNext(subject: BehaviorSubject<any>, data: any) {
subject.next(data);
}
diff --git a/src/app/service-graph/services/renderer/node.renderer.ts b/src/app/service-graph/services/renderer/node.renderer.ts
index fb29046..18719f1 100644
--- a/src/app/service-graph/services/renderer/node.renderer.ts
+++ b/src/app/service-graph/services/renderer/node.renderer.ts
@@ -60,6 +60,8 @@
this.renderServiceNodes(entering.filter('.service'));
this.renderServiceInstanceNodes(entering.filter('.serviceinstance'));
+ this.renderInstanceNodes(entering.filter('.instance'));
+ this.renderNetworkNodes(entering.filter('.network'));
node.exit().remove();
}
@@ -106,6 +108,46 @@
this.handleLabels(nodes); // eventually improve, padding top is wrong
}
+ private renderInstanceNodes(nodes: d3.selection) {
+ nodes
+ .append('rect')
+ .attr({
+ rx: config.node.radius,
+ ry: config.node.radius
+ });
+
+ nodes
+ .append('path')
+ .attr({
+ d: this.XosServiceGraphIcons.get('instance').path,
+ transform: this.XosServiceGraphIcons.get('instance').transform,
+ class: 'icon'
+ });
+
+ this.positionServiceNodeGroup(nodes);
+ this.handleLabels(nodes);
+ }
+
+ private renderNetworkNodes(nodes: d3.selection) {
+ nodes
+ .append('rect')
+ .attr({
+ rx: config.node.radius,
+ ry: config.node.radius
+ });
+
+ nodes
+ .append('path')
+ .attr({
+ d: this.XosServiceGraphIcons.get('network').path,
+ transform: this.XosServiceGraphIcons.get('network').transform,
+ class: 'icon'
+ });
+
+ this.positionServiceNodeGroup(nodes);
+ this.handleLabels(nodes);
+ }
+
private positionServiceNodeGroup(nodes: d3.selection) {
const self = this;
nodes.each(function (d: IXosSgNode) {
@@ -231,6 +273,7 @@
}
private getNodeLabel(n: any): string {
+ // NOTE for 'instances' display instance_name instead of name?
return n.data.name ? n.data.name.toUpperCase() : n.data.id;
// return n.data.name ? n.data.name.toUpperCase() + ` - ${n.data.id}` : n.data.id;
}
diff --git a/src/app/style/vars.scss b/src/app/style/vars.scss
index 2f2f829..c95413a 100644
--- a/src/app/style/vars.scss
+++ b/src/app/style/vars.scss
@@ -41,6 +41,11 @@
$color-danger: #DB524B;
$color-success: #1bbf89;
+// SERVICE GRAPH COLORS
+$serviceinstances-fg: green;
+$instances-fg: #b0dff6;
+$networks-fg: #f696ad;
+
// SIZES
$padding: 10px;
$navigation-width: 200px;