blob: b8220b965dc075a0dea980972c3e949f6cb3665b [file] [log] [blame]
Matteo Scandolofb46ae62017-08-08 09:10:50 -07001
2/*
3 * Copyright 2017-present Open Networking Foundation
4
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8
9 * http://www.apache.org/licenses/LICENSE-2.0
10
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18
Matteo Scandolo72181592017-07-25 14:49:40 -070019import * as _ from 'lodash';
20import {Observable, BehaviorSubject, Subscription} from 'rxjs';
21import {
22 IXosServiceGraph, IXosServiceInstanceGraphData, IXosServiceGraphNode
23} from '../interfaces';
24import {IXosDebouncer} from '../../core/services/helpers/debounce.helper';
25import {IXosModelStoreService} from '../../datasources/stores/model.store';
26import {IXosServiceGraphStore} from './service-graph.store';
27
28export interface IXosServiceInstanceGraphStore {
29 get(): Observable<IXosServiceGraph>;
30}
31
32export class XosServiceInstanceGraphStore implements IXosServiceInstanceGraphStore {
33 static $inject = [
34 '$log',
35 'XosServiceGraphStore',
36 'XosModelStore',
37 'XosDebouncer'
38 ];
39
40 private CoarseGraphSubscription: Subscription;
41 private ServiceInstanceSubscription: Subscription;
42 private ServiceInstanceLinkSubscription: Subscription;
43 private NetworkSubscription: Subscription;
44
45 // debounced functions
46 private handleData;
47
48
49 // FIXME this is declared also in ServiceGraphStore
50 private emptyGraph: IXosServiceGraph = {
51 nodes: [],
52 links: []
53 };
54
55 // graph data store
56 private graphData: BehaviorSubject<IXosServiceInstanceGraphData> = new BehaviorSubject({
57 serviceGraph: this.emptyGraph,
58 serviceInstances: [],
59 serviceInstanceLinks: [],
60 networks: []
61 });
62
63 private d3ServiceInstanceGraph = new BehaviorSubject(this.emptyGraph);
64
65 private serviceGraph: IXosServiceGraph = this.emptyGraph;
66 private serviceInstances: any[] = [];
67 private serviceInstanceLinks: any[] = [];
68 private networks: any[] = [];
69
70 constructor (
71 private $log: ng.ILogService,
72 private XosServiceGraphStore: IXosServiceGraphStore,
73 private XosModelStore: IXosModelStoreService,
74 private XosDebouncer: IXosDebouncer
75 ) {
76 this.$log.info(`[XosServiceInstanceGraphStore] Setup`);
77
78 // we want to have a quiet period of 500ms from the last event before doing anything
79 this.handleData = this.XosDebouncer.debounce(this._handleData, 500, this, false);
80
81 this.CoarseGraphSubscription = this.XosServiceGraphStore.getCoarse()
82 .subscribe(
83 (graph: IXosServiceGraph) => {
84 this.combineData(graph, 'serviceGraph');
85 }
86 );
87
88 this.ServiceInstanceSubscription = this.XosModelStore.query('ServiceInstance', '/core/serviceinstances')
89 .subscribe(
90 (res) => {
91 this.combineData(res, 'serviceInstance');
92 },
93 (err) => {
94 this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
95 }
96 );
97
98 this.ServiceInstanceLinkSubscription = this.XosModelStore.query('ServiceInstanceLink', '/core/serviceinstancelinks')
99 .subscribe(
100 (res) => {
101 this.combineData(res, 'serviceInstanceLink');
102 },
103 (err) => {
104 this.$log.error(`[XosServiceInstanceGraphStore] Service Observable: `, err);
105 }
106 );
107
108 this.NetworkSubscription = this.XosModelStore.query('Network', '/core/networks')
109 .subscribe(
110 (res) => {
111 this.combineData(res, 'networks');
112 },
113 (err) => {
114 this.$log.error(`[XosServiceGraphStore] graphData Observable: `, err);
115 }
116 );
117
118 // observe graphData and build ServiceInstance graph
119 this.graphData
120 .subscribe(
121 (res: IXosServiceInstanceGraphData) => {
122 this.$log.debug(`[XosServiceInstanceGraphStore] New graph data received`, res);
123
124 this.graphDataToD3(res);
125 },
126 (err) => {
127 this.$log.error(`[XosServiceInstanceGraphStore] graphData Observable: `, err);
128 }
129 );
130 }
131
132 public get(): Observable<IXosServiceGraph> {
133 return this.d3ServiceInstanceGraph;
134 }
135
136 // called by all the observables, combine the data in a globla graph observable
137 private combineData(data: any, type: 'serviceGraph' | 'serviceInstance' | 'serviceInstanceLink' | 'serviceInterface' | 'networks') {
138 switch (type) {
139 case 'serviceGraph':
140 this.serviceGraph = angular.copy(data);
141 break;
142 case 'serviceInstance':
143 this.serviceInstances = data;
144 break;
145 case 'serviceInstanceLink':
146 this.serviceInstanceLinks = data;
147 break;
148 case 'networks':
149 this.networks = data;
150 break;
151 }
152 this.handleData();
153 }
154
155 private _handleData() {
156 this.graphData.next({
157 serviceGraph: this.serviceGraph,
158 serviceInstances: this.serviceInstances,
159 serviceInstanceLinks: this.serviceInstanceLinks,
160 networks: this.networks
161 });
162 }
163
164 private getNodeType(n: any) {
165 return n.class_names.split(',')[0].toLowerCase();
166 }
167
168 private getNodeLabel(n: any) {
169 if (this.getNodeType(n) === 'serviceinstance') {
170 return n.name ? n.name : n.id;
171 }
172 return n.humanReadableName ? n.humanReadableName : n.name;
173 }
174
175 private d3Id(type: string, id: number) {
176 return `${type.toLowerCase()}~${id}`;
177 }
178
179 private toD3Node(n: any): IXosServiceGraphNode {
180 return {
181 id: this.d3Id(this.getNodeType(n), n.id),
182 label: this.getNodeLabel(n),
183 model: n,
184 type: this.getNodeType(n)
185 };
186 }
187
188 private getServiceInstanceIndexById(l: any, nodes: any[], where: 'source' | 'target'): string {
189 if (where === 'source') {
190 return _.find(nodes, {id: `serviceinstance~${l.provider_service_instance_id}`});
191 }
192 else {
193 if (l.subscriber_service_id) {
194 return _.find(nodes, {id: `service~${l.subscriber_service_id}`});
195 }
196 else if (l.subscriber_network_id) {
197 return _.find(nodes, {id: `network~${l.subscriber_network_id}`});
198 }
199 else if (l.subscriber_service_instance_id) {
200 return _.find(nodes, {id: `serviceinstance~${l.subscriber_service_instance_id}`});
201 }
202 }
203 }
204
205 private getOwnerById(id: number, nodes: any[]): any {
206 return _.find(nodes, {id: `service~${id}`});
207 }
208
209 private graphDataToD3(data: IXosServiceInstanceGraphData) {
210 try {
211 // get all the nodes
212 let nodes = _.chain(data.serviceGraph.nodes)
213 .map(n => {
214 // HACK we are receiving node as d3 models
215 return n.model;
216 })
217 .map(n => {
218 return this.toD3Node(n);
219 })
220 .value();
221
222 data.serviceInstances = _.chain(data.serviceInstances)
223 .map(n => {
224 return this.toD3Node(n);
225 })
226 .value();
227 nodes = nodes.concat(data.serviceInstances);
228
229 data.networks = _.chain(data.networks)
230 .filter(n => {
231 const subscriber = _.findIndex(data.serviceInstanceLinks, {subscriber_network_id: n.id});
232 return subscriber > -1;
233 })
234 .map(n => {
235 return this.toD3Node(n);
236 })
237 .value();
238 nodes = nodes.concat(data.networks);
239
240 let links = data.serviceGraph.links;
241
242 // create the links starting from the coarse ones
243 links = _.reduce(data.serviceInstanceLinks, (links, l) => {
244 let link = {
245 id: `service_instance_link~${l.id}`,
246 source: this.getServiceInstanceIndexById(l, nodes, 'source'),
247 target: this.getServiceInstanceIndexById(l, nodes, 'target'),
248 model: l,
249 d3Class: 'service-instance'
250 };
251 links.push(link);
252 return links;
253 }, data.serviceGraph.links);
254
255 const linksToService = _.reduce(data.serviceInstances, (links, n) => {
256 if (angular.isDefined(n.model.owner_id)) {
257 let link = {
258 id: `owner~${n.id}`,
259 source: n,
260 target: this.getOwnerById(n.model.owner_id, nodes),
261 model: n,
262 d3Class: 'owner'
263 };
264 links.push(link);
265 }
266 return links;
267 }, []);
268
269 links = links.concat(linksToService);
270
271 let graph: IXosServiceGraph = {
272 nodes,
273 links
274 };
275
276 this.d3ServiceInstanceGraph.next(graph);
277 } catch (e) {
278 this.d3ServiceInstanceGraph.error(e);
279 }
280 }
281}