blob: 0ed096075b7bcdd1772fcf0d8a380a451b69c6e3 [file] [log] [blame]
Matteo Scandolo968e7f22017-03-03 11:49:18 -08001import * as _ from 'lodash';
Matteo Scandoloa160eef2017-03-06 17:21:26 -08002import {Observable, BehaviorSubject, Subscription} from 'rxjs';
Matteo Scandoloa62adbc2017-03-02 15:37:34 -08003import {IXosModelStoreService} from '../../datasources/stores/model.store';
Matteo Scandolo968e7f22017-03-03 11:49:18 -08004import {
5 IXosServiceGraph, IXosServiceModel, IXosTenantModel, IXosCoarseGraphData,
Matteo Scandolo75171782017-03-08 14:17:01 -08006 IXosServiceGraphNode, IXosServiceGraphLink, IXosFineGrainedGraphData
Matteo Scandolo968e7f22017-03-03 11:49:18 -08007} from '../interfaces';
Matteo Scandoloa62adbc2017-03-02 15:37:34 -08008import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
9export interface IXosServiceGraphStore {
10 get(): Observable<IXosServiceGraph>;
Matteo Scandolo968e7f22017-03-03 11:49:18 -080011 getCoarse(): Observable<IXosServiceGraph>;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080012}
13
14export class XosServiceGraphStore implements IXosServiceGraphStore {
15 static $inject = [
16 '$log',
17 'XosModelStore',
18 'XosDebouncer'
19 ];
Matteo Scandolo968e7f22017-03-03 11:49:18 -080020
21 // graph data store
Matteo Scandolo75171782017-03-08 14:17:01 -080022 private graphData: BehaviorSubject<IXosFineGrainedGraphData> = new BehaviorSubject({
Matteo Scandolo968e7f22017-03-03 11:49:18 -080023 services: [],
Matteo Scandolo75171782017-03-08 14:17:01 -080024 tenants: [],
25 networks: [],
26 subscribers: []
Matteo Scandolo968e7f22017-03-03 11:49:18 -080027 });
28
Matteo Scandolo75171782017-03-08 14:17:01 -080029 // representation of the graph as D3 requires
Matteo Scandolo968e7f22017-03-03 11:49:18 -080030 private d3CoarseGraph = new BehaviorSubject({});
31 private d3FineGrainedGraph = new BehaviorSubject({});
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080032
33 // storing locally reference to the data model
34 private services;
35 private tenants;
Matteo Scandolo75171782017-03-08 14:17:01 -080036 private subscribers;
37 private networks;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080038
39 // debounced functions
40 private handleData;
41
42 // datastore
Matteo Scandoloa160eef2017-03-06 17:21:26 -080043 private ServiceSubscription: Subscription;
44 private TenantSubscription: Subscription;
Matteo Scandolo75171782017-03-08 14:17:01 -080045 private SubscriberSubscription: Subscription;
46 private NetworkSubscription: Subscription;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080047
48 constructor (
49 private $log: ng.ILogService,
50 private XosModelStore: IXosModelStoreService,
51 private XosDebouncer: IXosDebouncer
52 ) {
53
Matteo Scandolo968e7f22017-03-03 11:49:18 -080054 this.$log.info(`[XosServiceGraphStore] Setup`);
55
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080056 // we want to have a quiet period of 500ms from the last event before doing anything
Matteo Scandolo968e7f22017-03-03 11:49:18 -080057 this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080058
Matteo Scandolo968e7f22017-03-03 11:49:18 -080059 // observe models and populate graphData
Matteo Scandolo75171782017-03-08 14:17:01 -080060 // TODO get Nodes (model that represent compute nodes in a pod)
61 // TODO get Instances (model that represent deployed VMs)
Matteo Scandoloa160eef2017-03-06 17:21:26 -080062 this.ServiceSubscription = this.XosModelStore.query('Service', '/core/services')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080063 .subscribe(
64 (res) => {
65 this.combineData(res, 'services');
66 },
67 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080068 this.$log.error(`[XosServiceGraphStore] Service Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080069 }
70 );
71
Matteo Scandoloa160eef2017-03-06 17:21:26 -080072 this.TenantSubscription = this.XosModelStore.query('Tenant', '/core/tenants')
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080073 .subscribe(
74 (res) => {
75 this.combineData(res, 'tenants');
76 },
77 (err) => {
Matteo Scandolo75171782017-03-08 14:17:01 -080078 this.$log.error(`[XosServiceGraphStore] Tenant Observable: `, err);
Matteo Scandoloa62adbc2017-03-02 15:37:34 -080079 }
80 );
Matteo Scandolo968e7f22017-03-03 11:49:18 -080081
Matteo Scandolo75171782017-03-08 14:17:01 -080082 this.SubscriberSubscription = this.XosModelStore.query('Subscriber', '/core/subscribers')
Matteo Scandolo968e7f22017-03-03 11:49:18 -080083 .subscribe(
Matteo Scandolo75171782017-03-08 14:17:01 -080084 (res) => {
85 this.combineData(res, 'subscribers');
86 },
87 (err) => {
88 this.$log.error(`[XosServiceGraphStore] Subscriber Observable: `, err);
89 }
90 );
91
92 this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
93 .subscribe(
94 (res) => {
95 this.combineData(res, 'networks');
Matteo Scandolo968e7f22017-03-03 11:49:18 -080096 },
97 (err) => {
98 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
99 }
100 );
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800101
Matteo Scandolo75171782017-03-08 14:17:01 -0800102 // observe graphData and build Coarse and FineGrained graphs
103 this.graphData
104 .subscribe(
105 (res: IXosFineGrainedGraphData) => {
106 this.graphDataToCoarseGraph(res);
107 this.graphDataToFineGrainedGraph(res);
108 },
109 (err) => {
110 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
111 }
112 );
Matteo Scandoloa160eef2017-03-06 17:21:26 -0800113 }
114
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800115 public get() {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800116 return this.d3FineGrainedGraph.asObservable();
117 }
118
119 public getCoarse() {
120 return this.d3CoarseGraph.asObservable();
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800121 }
122
Matteo Scandolo75171782017-03-08 14:17:01 -0800123 private combineData(data: any, type: 'services'|'tenants'|'subscribers'|'networks') {
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800124 switch (type) {
125 case 'services':
126 this.services = data;
127 break;
128 case 'tenants':
129 this.tenants = data;
130 break;
Matteo Scandolo75171782017-03-08 14:17:01 -0800131 case 'subscribers':
132 this.subscribers = data;
133 break;
134 case 'networks':
135 this.networks = data;
136 break;
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800137 }
138 this.handleData(this.services, this.tenants);
139 }
140
141 private _handleData(services: IXosServiceModel[], tenants: IXosTenantModel[]) {
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800142 this.graphData.next({
143 services: this.services,
Matteo Scandolo75171782017-03-08 14:17:01 -0800144 tenants: this.tenants,
145 subscribers: this.subscribers,
146 networks: this.networks
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800147 });
148 }
149
Matteo Scandolo75171782017-03-08 14:17:01 -0800150 private getNodeIndexById(id: number | string, nodes: IXosServiceModel[]) {
Matteo Scandolo0c61c9b2017-03-03 11:49:18 -0800151 return _.findIndex(nodes, {id: id});
152 }
153
Matteo Scandolo75171782017-03-08 14:17:01 -0800154 private d3Id(type: string, id: number) {
155 return `${type.toLowerCase()}~${id}`;
156 }
157
158 private getTargetId(tenant: IXosTenantModel) {
159
160 let targetId;
161 if (tenant.subscriber_service_id) {
162 targetId = this.d3Id('service', tenant.subscriber_service_id);
163 }
164 else if (tenant.subscriber_tenant_id) {
165 targetId = this.d3Id('tenant', tenant.subscriber_tenant_id);
166 }
167 else if (tenant.subscriber_network_id) {
168 targetId = this.d3Id('network', tenant.subscriber_network_id);
169 }
170 else if (tenant.subscriber_root_id) {
171 targetId = this.d3Id('subscriber', tenant.subscriber_root_id);
172 }
173 return targetId;
174 }
175
176 private getSourceId(tenant: IXosTenantModel) {
177 return this.d3Id('service', tenant.provider_service_id);
178 }
179
180 private getNodeType(n: any) {
181 return n.class_names.split(',')[0].toLowerCase();
182 }
183
184 private getNodeLabel(n: any) {
185 if (this.getNodeType(n) === 'tenant') {
186 return n.id;
187 }
188 return n.humanReadableName ? n.humanReadableName : n.name;
189 }
190
191 private removeUnwantedFineGrainedData(data: IXosFineGrainedGraphData): IXosFineGrainedGraphData {
192 data.tenants = _.filter(data.tenants, t => t.kind !== 'coarse');
193 data.networks = _.filter(data.networks, n => {
194 const subscriber = _.findIndex(data.tenants, {subscriber_network_id: n.id});
195 return subscriber > -1;
196 });
197 return data;
198 }
199
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800200 private graphDataToCoarseGraph(data: IXosCoarseGraphData) {
Matteo Scandolo75171782017-03-08 14:17:01 -0800201
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800202 const links: IXosServiceGraphLink[] = _.chain(data.tenants)
203 .filter((t: IXosTenantModel) => t.kind === 'coarse')
204 .map((t: IXosTenantModel) => {
205 return {
206 id: t.id,
Matteo Scandolo75171782017-03-08 14:17:01 -0800207 source: this.getNodeIndexById(t.provider_service_id, data.services),
208 target: this.getNodeIndexById(t.subscriber_service_id, data.services),
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800209 model: t
210 };
211 })
212 .value();
213
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800214 const nodes: IXosServiceGraphNode[] = _.map(data.services, (s: IXosServiceModel) => {
215 return {
216 id: s.id,
217 label: s.name,
218 model: s
219 };
220 });
221
Matteo Scandolo968e7f22017-03-03 11:49:18 -0800222 this.d3CoarseGraph.next({
223 nodes: nodes,
224 links: links
225 });
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800226 }
227
Matteo Scandolo75171782017-03-08 14:17:01 -0800228 private graphDataToFineGrainedGraph(data: IXosFineGrainedGraphData) {
229
230 data = this.removeUnwantedFineGrainedData(data);
231
232 let nodes = _.reduce(Object.keys(data), (list: any[], k: string) => {
233 return list.concat(data[k]);
234 }, []);
235
236 nodes = _.chain(nodes)
237 .map(n => {
238 n.d3Id = this.d3Id(this.getNodeType(n), n.id);
239 return n;
240 })
241 .map(n => {
242 let node: IXosServiceGraphNode = {
243 id: n.d3Id,
244 label: this.getNodeLabel(n),
245 model: n,
246 type: this.getNodeType(n)
247 };
248 return node;
249 })
250 .value();
251
252 const links = _.reduce(data.tenants, (links: IXosServiceGraphLink[], tenant: IXosTenantModel) => {
253 const sourceId = this.getSourceId(tenant);
254 const targetId = this.getTargetId(tenant);
255
256 const tenantToProvider = {
257 id: `${sourceId}_${tenant.d3Id}`,
258 source: this.getNodeIndexById(sourceId, nodes),
259 target: this.getNodeIndexById(tenant.d3Id, nodes),
260 model: tenant
261 };
262
263 const tenantToSubscriber = {
264 id: `${tenant.d3Id}_${targetId}`,
265 source: this.getNodeIndexById(tenant.d3Id, nodes),
266 target: this.getNodeIndexById(targetId, nodes),
267 model: tenant
268 };
269
270 links.push(tenantToProvider);
271 links.push(tenantToSubscriber);
272 return links;
273 }, []);
274
275 if (nodes.length === 0 || links.length === 0) {
276 return;
277 }
278
279 this.d3FineGrainedGraph.next({
280 nodes: nodes,
281 links: links
282 });
283 }
284
Matteo Scandoloa62adbc2017-03-02 15:37:34 -0800285}