Adding nemo engine.
[nemo.git] / nemo-impl / src / main / java / org / opendaylight / nemo / intent / computation / VNComputationUnit.java
1 /*\r
2  * Copyright (c) 2015 Huawei, Inc. and others. All rights reserved.\r
3  *\r
4  * This program and the accompanying materials are made available under the\r
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
6  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
7  */\r
8 \r
9 package org.opendaylight.nemo.intent.computation;\r
10 \r
11 import com.google.common.base.Optional;\r
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;\r
13 import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;\r
14 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;\r
15 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;\r
16 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;\r
17 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;\r
18 import org.opendaylight.nemo.intent.algorithm.Edge;\r
19 import org.opendaylight.nemo.intent.algorithm.RoutingAlgorithm;\r
20 import org.opendaylight.nemo.intent.algorithm.Vertex;\r
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.VirtualNetworks;\r
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.VirtualNetwork;\r
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.VirtualNetworkKey;\r
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.VirtualLinks;\r
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.VirtualNodes;\r
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.VirtualPaths;\r
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.VirtualRoutes;\r
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.links.VirtualLink;\r
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.nodes.VirtualNode;\r
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.paths.VirtualPath;\r
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.paths.VirtualPathBuilder;\r
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.paths.VirtualPathKey;\r
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.routes.VirtualRoute;\r
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.routes.VirtualRouteBuilder;\r
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.networks.virtual.network.virtual.routes.VirtualRouteKey;\r
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLinkBuilder;\r
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.common.rev151010.UserId;\r
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.VirtualLinkId;\r
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.VirtualNetworkId;\r
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.VirtualNodeId;\r
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.nemo.engine.common.rev151010.VirtualPathId;\r
42 import org.opendaylight.yangtools.concepts.ListenerRegistration;\r
43 import org.opendaylight.yangtools.yang.binding.DataObject;\r
44 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;\r
45 import org.slf4j.Logger;\r
46 import org.slf4j.LoggerFactory;\r
47 \r
48 import java.util.*;\r
49 import java.util.concurrent.ExecutionException;\r
50 \r
51 /**\r
52  * The virtual network computation unit implements the following functions:\r
53  * (1) Maintain a user's virtual network topology information generated in\r
54  *     terms of the user's intents through subscribing from the data store.\r
55  * (2) Automatically recompute all routes of the virtual network when it\r
56  *     changed, and store or update the routes into the data store.\r
57  * (3) Provide the path computation with SLA constraints to any other modules\r
58  *     that need it.\r
59  *\r
60  * @author Zhigang Ji\r
61  */\r
62 public class VNComputationUnit implements AutoCloseable {\r
63     private static final Logger LOG = LoggerFactory.getLogger(VNComputationUnit.class);\r
64 \r
65     private final DataBroker dataBroker;\r
66 \r
67     /**\r
68      * The user id for the virtual network maintained by\r
69      * this computation unit.\r
70      */\r
71     private UserId userId;\r
72 \r
73     /**\r
74      * The routing algorithm instance.\r
75      */\r
76     private RoutingAlgorithm routingAlgorithm;\r
77 \r
78     /**\r
79      * The virtual routers in the virtual network.\r
80      */\r
81     private Set<VirtualNodeId> virtualRouters;\r
82 \r
83     /**\r
84      * The registration for the virtual node change listener.\r
85      */\r
86     private ListenerRegistration<DataChangeListener> virtualNodeChangeListenerReg;\r
87 \r
88     /**\r
89      * The registration for the virtual link change listener.\r
90      */\r
91     private ListenerRegistration<DataChangeListener> virtualLinkChangeListenerReg;\r
92 \r
93     public VNComputationUnit(DataBroker dataBroker, UserId userId) {\r
94         super();\r
95 \r
96         this.dataBroker = dataBroker;\r
97         this.userId = userId;\r
98         routingAlgorithm = new RoutingAlgorithm();\r
99         virtualRouters = new HashSet<VirtualNodeId>();\r
100 \r
101         VirtualNetworkKey virtualNetworkKey = new VirtualNetworkKey(new VirtualNetworkId(userId.getValue()));\r
102         InstanceIdentifier<VirtualNode> virtualNodeIid = InstanceIdentifier\r
103                 .builder(VirtualNetworks.class)\r
104                 .child(VirtualNetwork.class, virtualNetworkKey)\r
105                 .child(VirtualNodes.class)\r
106                 .child(VirtualNode.class)\r
107                 .build();\r
108         InstanceIdentifier<VirtualLink> virtualLinkIid = InstanceIdentifier\r
109                 .builder(VirtualNetworks.class)\r
110                 .child(VirtualNetwork.class, virtualNetworkKey)\r
111                 .child(VirtualLinks.class)\r
112                 .child(VirtualLink.class)\r
113                 .build();\r
114 \r
115         virtualNodeChangeListenerReg = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,\r
116                 virtualNodeIid, new VirtualNodeChangeListener(), DataChangeScope.BASE);\r
117         virtualLinkChangeListenerReg = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,\r
118                 virtualLinkIid, new VirtualLinkChangeListener(), DataChangeScope.BASE);\r
119 \r
120         LOG.debug("Initialized the virtual network computation unit for the user {}.", userId.getValue());\r
121 \r
122         return;\r
123     }\r
124 \r
125     public VNComputationUnit(DataBroker dataBroker, VirtualNetwork virtualNetwork) {\r
126         super();\r
127 \r
128         this.dataBroker = dataBroker;\r
129         userId = virtualNetwork.getUserId();\r
130         routingAlgorithm = new RoutingAlgorithm();\r
131         virtualRouters = new HashSet<VirtualNodeId>();\r
132 \r
133         List<VirtualNode> virtualNodes = virtualNetwork.getVirtualNodes().getVirtualNode();\r
134         Vertex vertex;\r
135 \r
136         for ( VirtualNode virtualNode : virtualNodes ) {\r
137             vertex = new Vertex(virtualNode.getNodeId().getValue());\r
138             routingAlgorithm.addVertex(vertex);\r
139 \r
140             if ( VirtualNode.NodeType.Vrouter == virtualNode.getNodeType() ) {\r
141                 virtualRouters.add(virtualNode.getNodeId());\r
142             }\r
143         }\r
144 \r
145         List<VirtualLink> virtualLinks = virtualNetwork.getVirtualLinks().getVirtualLink();\r
146         Edge edge;\r
147 \r
148         for ( VirtualLink virtualLink : virtualLinks ) {\r
149             edge = new Edge(virtualLink.getLinkId().getValue(), virtualLink.getSrcNodeId().getValue(),\r
150                     virtualLink.getDestNodeId().getValue(), virtualLink.getMetric(),\r
151                     virtualLink.getBandwidth());\r
152             routingAlgorithm.addEdge(edge);\r
153         }\r
154 \r
155 //        computeRoute(virtualNetwork);\r
156 \r
157         return;\r
158     }\r
159 \r
160     /**\r
161      * Compute a shortest virtual path from the given source vertex to\r
162      * target one without any constraint.\r
163      *\r
164      * @param source The given source virtual node id.\r
165      * @param target The given target virtual node id.\r
166      * @return The virtual path if successful,or null otherwise.\r
167      */\r
168     public VirtualPath computePath(VirtualNodeId source, VirtualNodeId target) {\r
169         List<Edge> edges = routingAlgorithm.computePath(routingAlgorithm.getVertex(source.getValue()),\r
170                 routingAlgorithm.getVertex(target.getValue()));\r
171 \r
172         if ( null == edges || edges.isEmpty() ) {\r
173             return null;\r
174         }\r
175 \r
176         List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink> virtualLinks =\r
177                 new ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink>(edges.size());\r
178         org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink virtualLink;\r
179         long metric = 0;\r
180         long delay = 0;\r
181 \r
182         for ( Edge edge : edges ) {\r
183             virtualLink = new VirtualLinkBuilder()\r
184                     .setLinkId(new VirtualLinkId(edge.getId()))\r
185                     .setOrder((long) virtualLinks.size())\r
186                     .build();\r
187             virtualLinks.add(virtualLink);\r
188 \r
189             metric += edge.getMetric();\r
190 //            delay += edge.getDelay();\r
191         }\r
192 \r
193         VirtualPath virtualPath = new VirtualPathBuilder()\r
194                 .setPathId(new VirtualPathId(UUID.randomUUID().toString()))\r
195                 .setVirtualLink(virtualLinks)\r
196                 .setMetric(metric)\r
197                 .setBandwidth(0L)\r
198                 .setDelay(delay)\r
199                 .build();\r
200 \r
201         return virtualPath;\r
202     }\r
203 \r
204     /**\r
205      * Compute a shortest virtual path with the given bandwidth from the\r
206      * given source vertex to target one.\r
207      *\r
208      * @param source The given source virtual node id.\r
209      * @param target The given target virtual node id.\r
210      * @param bandwidth The given bandwidth for the virtual path.\r
211      * @return The virtual path if successful,or null otherwise.\r
212      */\r
213     public VirtualPath computePath(VirtualNodeId source, VirtualNodeId target, long bandwidth) {\r
214         List<Edge> edges = routingAlgorithm.computePath(routingAlgorithm.getVertex(source.getValue()),\r
215                 routingAlgorithm.getVertex(target.getValue()), bandwidth);\r
216 \r
217         if ( null == edges || edges.isEmpty() ) {\r
218             return null;\r
219         }\r
220 \r
221         List<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink> virtualLinks =\r
222                 new ArrayList<org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink>(edges.size());\r
223         org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.generic.virtual.network.rev151010.virtual.path.instance.VirtualLink virtualLink;\r
224         long metric = 0;\r
225         long delay = 0;\r
226 \r
227         for ( Edge edge : edges ) {\r
228             edge.setBandwidth(edge.getBandwidth() - bandwidth);\r
229             routingAlgorithm.updateEdge(edge);\r
230 \r
231             virtualLink = new VirtualLinkBuilder()\r
232                     .setLinkId(new VirtualLinkId(edge.getId()))\r
233                     .setOrder((long)virtualLinks.size())\r
234                     .build();\r
235             virtualLinks.add(virtualLink);\r
236 \r
237             metric += edge.getMetric();\r
238 //            delay += edge.getDelay();\r
239         }\r
240 \r
241         VirtualPath virtualPath = new VirtualPathBuilder()\r
242                 .setPathId(new VirtualPathId(UUID.randomUUID().toString()))\r
243                 .setVirtualLink(virtualLinks)\r
244                 .setMetric(metric)\r
245                 .setBandwidth(bandwidth)\r
246                 .setDelay(delay)\r
247                 .build();\r
248 \r
249         return virtualPath;\r
250     }\r
251 \r
252     @Override\r
253     public void close() throws Exception {\r
254         if ( null != virtualNodeChangeListenerReg ) {\r
255             virtualNodeChangeListenerReg.close();\r
256         }\r
257 \r
258         if ( null != virtualLinkChangeListenerReg ) {\r
259             virtualLinkChangeListenerReg.close();\r
260         }\r
261 \r
262         return;\r
263     }\r
264 \r
265     /**\r
266      * Compute the routes between all virtual routers in the virtual\r
267      * network, and store or update them into the data store.\r
268      */\r
269     private void computeRoute() {\r
270         Map<VirtualRouteKey, VirtualPath> routes = new HashMap<VirtualRouteKey, VirtualPath>();\r
271         VirtualPath virtualPath;\r
272 \r
273         for ( VirtualNodeId source : virtualRouters ) {\r
274             for ( VirtualNodeId target : virtualRouters ) {\r
275                 if ( !source.equals(target) ) {\r
276                     virtualPath = computePath(source, target);\r
277 \r
278                     if ( null == virtualPath ) {\r
279                         continue;\r
280                     }\r
281 \r
282                     routes.put(new VirtualRouteKey(source, target), virtualPath);\r
283                 }\r
284             }\r
285         }\r
286 \r
287         updateRoute(routes);\r
288 \r
289         return;\r
290     }\r
291 \r
292     /**\r
293      * Compute the routes between all virtual routers in the virtual\r
294      * network, and store them into the virtual network.\r
295      *\r
296      * @param virtualNetwork The virtual network to store the routes.\r
297      */\r
298     private void computeRoute(VirtualNetwork virtualNetwork) {\r
299         List<VirtualRoute> virtualRoutes = virtualNetwork.getVirtualRoutes().getVirtualRoute();\r
300         List<VirtualPath> virtualPaths = virtualNetwork.getVirtualPaths().getVirtualPath();\r
301         VirtualRoute virtualRoute;\r
302         VirtualPath virtualPath;\r
303 \r
304         for ( VirtualNodeId source : virtualRouters ) {\r
305             for ( VirtualNodeId target : virtualRouters ) {\r
306                 if ( !source.equals(target) ) {\r
307                     virtualPath = computePath(source, target);\r
308 \r
309                     if ( null == virtualPath ) {\r
310                         continue;\r
311                     }\r
312 \r
313                     virtualRoute = new VirtualRouteBuilder().setSrcNodeId(source)\r
314                             .setDestNodeId(target)\r
315                             .setPathId(virtualPath.getPathId())\r
316                             .build();\r
317 \r
318                     virtualPaths.add(virtualPath);\r
319                     virtualRoutes.add(virtualRoute);\r
320                 }\r
321             }\r
322         }\r
323 \r
324         return;\r
325     }\r
326 \r
327     /**\r
328      * Update the given routes into the data store. If the route\r
329      * already exists, remove it's old virtual path and store the\r
330      * given new one into the data store.\r
331      *\r
332      * @param routes The given routes to be updated.\r
333      */\r
334     private void updateRoute(Map<VirtualRouteKey, VirtualPath> routes) {\r
335         ReadWriteTransaction readWriteTransaction = dataBroker.newReadWriteTransaction();\r
336 \r
337         VirtualNetworkKey virtualNetworkKey = new VirtualNetworkKey(new VirtualNetworkId(userId.getValue()));\r
338         InstanceIdentifier<VirtualRoute> virtualRouteIid;\r
339         InstanceIdentifier<VirtualPath> virtualPathIid;\r
340         Optional<VirtualRoute> result;\r
341         VirtualRoute virtualRoute;\r
342 \r
343         for ( Map.Entry<VirtualRouteKey, VirtualPath> route : routes.entrySet() ) {\r
344             virtualRouteIid = InstanceIdentifier.builder(VirtualNetworks.class)\r
345                     .child(VirtualNetwork.class, virtualNetworkKey)\r
346                     .child(VirtualRoutes.class)\r
347                     .child(VirtualRoute.class, route.getKey())\r
348                     .build();\r
349 \r
350             try {\r
351                 result = readWriteTransaction.read(LogicalDatastoreType.CONFIGURATION, virtualRouteIid).get();\r
352             } catch ( InterruptedException | ExecutionException exception ) {\r
353                 LOG.error("Can not read the virtual route from the virtual node {} to {}.",\r
354                         route.getKey().getSrcNodeId().getValue(), route.getKey().getDestNodeId().getValue());\r
355 \r
356                 continue;\r
357             }\r
358 \r
359             if ( result.isPresent() ) {\r
360                 virtualRoute = result.get();\r
361                 virtualPathIid = InstanceIdentifier.builder(VirtualNetworks.class)\r
362                         .child(VirtualNetwork.class, virtualNetworkKey)\r
363                         .child(VirtualPaths.class)\r
364                         .child(VirtualPath.class, new VirtualPathKey(virtualRoute.getPathId()))\r
365                         .build();\r
366                 readWriteTransaction.delete(LogicalDatastoreType.CONFIGURATION, virtualPathIid);\r
367             }\r
368 \r
369             virtualPathIid = InstanceIdentifier.builder(VirtualNetworks.class)\r
370                     .child(VirtualNetwork.class, virtualNetworkKey)\r
371                     .child(VirtualPaths.class)\r
372                     .child(VirtualPath.class, route.getValue().getKey())\r
373                     .build();\r
374             readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, virtualPathIid, route.getValue(), true);\r
375 \r
376             virtualRoute = new VirtualRouteBuilder().setSrcNodeId(route.getKey().getSrcNodeId())\r
377                     .setDestNodeId(route.getKey().getDestNodeId())\r
378                     .setPathId(route.getValue().getPathId())\r
379                     .build();\r
380             readWriteTransaction.put(LogicalDatastoreType.CONFIGURATION, virtualRouteIid, virtualRoute, true);\r
381         }\r
382 \r
383         readWriteTransaction.submit();\r
384 \r
385         return;\r
386     }\r
387 \r
388     /**\r
389      * A listener to change events related to virtual nodes being\r
390      * added, removed or updated.\r
391      *\r
392      * @author Zhigang Ji\r
393      */\r
394     private class VirtualNodeChangeListener implements DataChangeListener {\r
395         @Override\r
396         public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {\r
397             if ( null == change ) {\r
398                 return;\r
399             }\r
400 \r
401             Map<InstanceIdentifier<?>, DataObject> createdData = change.getCreatedData();\r
402 \r
403             if ( null != createdData && !createdData.isEmpty() ) {\r
404                 VirtualNode virtualNode;\r
405                 Vertex vertex;\r
406 \r
407                 for ( DataObject dataObject : createdData.values() ) {\r
408                     if ( dataObject instanceof VirtualNode ) {\r
409                         virtualNode = (VirtualNode)dataObject;\r
410                         vertex = new Vertex(virtualNode.getNodeId().getValue());\r
411 \r
412                         routingAlgorithm.addVertex(vertex);\r
413 \r
414                         if ( VirtualNode.NodeType.Vrouter == virtualNode.getNodeType() ) {\r
415                             virtualRouters.add(virtualNode.getNodeId());\r
416                         }\r
417                     }\r
418                 }\r
419             }\r
420 \r
421             return;\r
422         }\r
423     }\r
424 \r
425     /**\r
426      * A listener to change events related to virtual links being\r
427      * added, removed or updated.\r
428      *\r
429      * @author Zhigang Ji\r
430      */\r
431     private class VirtualLinkChangeListener implements DataChangeListener {\r
432         @Override\r
433         public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {\r
434             if ( null == change ) {\r
435                 return;\r
436             }\r
437 \r
438             Map<InstanceIdentifier<?>, DataObject> createdData = change.getCreatedData();\r
439 \r
440             if ( null != createdData && !createdData.isEmpty() ) {\r
441                 VirtualLink virtualLink;\r
442                 Edge edge;\r
443                 boolean needRerouting = false;\r
444 \r
445                 for ( DataObject dataObject : createdData.values() ) {\r
446                     if ( dataObject instanceof VirtualLink ) {\r
447                         virtualLink = (VirtualLink)dataObject;\r
448                         edge = new Edge(virtualLink.getLinkId().getValue(), virtualLink.getSrcNodeId().getValue(),\r
449                                 virtualLink.getDestNodeId().getValue(), virtualLink.getMetric(),\r
450                                 virtualLink.getBandwidth());\r
451 \r
452                         routingAlgorithm.addEdge(edge);\r
453 \r
454                         if ( virtualRouters.contains(virtualLink.getSrcNodeId())\r
455                                 && virtualRouters.contains(virtualLink.getDestNodeId()) ) {\r
456                             needRerouting = true;\r
457                         }\r
458                     }\r
459                 }\r
460 \r
461                 if ( needRerouting ) {\r
462                     computeRoute();\r
463                 }\r
464             }\r
465 \r
466             return;\r
467         }\r
468     }\r
469 }\r