Bump to odlparent 3.1.0 and yangtools 2.0.3
[alto.git] / alto-core / standard-northbound-routes / costmap / impl / src / main / java / org / opendaylight / alto / core / northbound / route / costmap / impl / AltoNorthboundRouteCostmap.java
1 /*
2  * Copyright (c) 2015 Yale University and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.alto.core.northbound.route.costmap.impl;
10
11
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13
14 import com.fasterxml.jackson.core.JsonProcessingException;
15 import com.fasterxml.jackson.databind.JsonNode;
16 import com.fasterxml.jackson.databind.ObjectMapper;
17 import com.fasterxml.jackson.databind.node.ArrayNode;
18 import com.google.common.base.Optional;
19 import org.opendaylight.alto.core.northbound.api.AltoNorthboundRoute;
20 import org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter;
21 import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostMap;
22 import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
23 import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285VersionTag;
24
25 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
26 import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
29 import org.opendaylight.yang.gen.v1.urn.alto.northbound.route.costmap.rev151021.Records;
30 import org.opendaylight.yang.gen.v1.urn.alto.northbound.route.costmap.rev151021.records.Record;
31 import org.opendaylight.yang.gen.v1.urn.alto.northbound.route.costmap.rev151021.records.RecordKey;
32 import org.opendaylight.yang.gen.v1.urn.alto.resourcepool.rev150921.context.Resource;
33 import org.opendaylight.yang.gen.v1.urn.alto.resourcepool.rev150921.context.resource.ContextTag;
34 import org.opendaylight.yang.gen.v1.urn.alto.resourcepool.rev150921.context.resource.ContextTagKey;
35 import org.opendaylight.yang.gen.v1.urn.alto.types.rev150921.CostMetric;
36 import org.opendaylight.yang.gen.v1.urn.alto.types.rev150921.PidName;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.AltoModelCostmapService;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.QueryInput;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.QueryInputBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.QueryOutput;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.ResourceTypeCostmap;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.alto.request.costmap.request.CostmapRequestBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.alto.response.costmap.response.CostmapResponse;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.cost.type.data.CostTypeBuilder;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.costmap.request.data.CostmapParamsBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.costmap.response.data.CostmapResponseData;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.costmap.response.data.costmap.response.data.CostmapSource;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.costmap.response.data.costmap.response.data.costmap.source.CostmapDestination;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rev151021.costmap.response.data.costmap.response.data.costmap.source.costmap.destination.Cost;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rfc7285.rev151021.costmap.filter.data.CostmapFilterBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.costmap.rfc7285.rev151021.query.input.request.costmap.request.costmap.params.filter.CostmapFilterDataBuilder;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.common.RpcResult;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import javax.ws.rs.Consumes;
58 import javax.ws.rs.GET;
59 import javax.ws.rs.POST;
60 import javax.ws.rs.Path;
61 import javax.ws.rs.PathParam;
62 import javax.ws.rs.Produces;
63 import javax.ws.rs.core.Response;
64 import java.util.HashMap;
65 import java.util.HashSet;
66 import java.util.Iterator;
67 import java.util.LinkedHashMap;
68 import java.util.LinkedList;
69 import java.util.List;
70 import java.util.Map;
71 import java.util.concurrent.Future;
72
73 public class AltoNorthboundRouteCostmap implements AltoNorthboundRoute {
74     public static final String COSTMAP_ROUTE = "costmap";
75
76     public static final String ALTO_COSTMAP_FILTER = "application/alto-costmapfilter+json";
77     public static final String ALTO_COSTMAP = "application/alto-costmap+json";
78
79     public static final String FIELD_PIDS = "pids";
80     public static final String FIELD_COST_TYPE = "cost-type";
81     public static final String FIELD_COST_MODE = "cost-mode";
82     public static final String FIELD_COST_METRIC = "cost-metric";
83     public static final String FIELD_PID_SOURCE = "srcs";
84     public static final String FIELD_PID_DESTINSTION = "dsts";
85
86     private ObjectMapper mapper = new ObjectMapper();
87     private static final Logger LOG = LoggerFactory.getLogger(AltoNorthboundRouteCostmap.class);
88
89     private DataBroker dataBroker = null;
90
91     private AltoNorthboundRouter router = null;
92
93     private static AltoModelCostmapService mapService = null;
94
95     public void setDataBroker(DataBroker dataBroker) {
96         this.dataBroker = dataBroker;
97     }
98
99     public void setMapService(final AltoModelCostmapService mapService) {
100         this.mapService = mapService;
101     }
102
103     public void init() {
104
105         if (dataBroker == null) {
106             LOG.error("Failed to init: data broker is null");
107         }
108
109
110         LOG.info("AltoNorthboundRouteCostmap initiated");
111
112     }
113
114     public void register(AltoNorthboundRouter router) {
115         this.router = router;
116         this.router.addRoute(COSTMAP_ROUTE, new AltoNorthboundRouteCostmap());
117     }
118
119     public void close() {
120         router.removeRoute(COSTMAP_ROUTE);
121     }
122
123     @Path("{path}")
124     @GET
125     @Produces({ALTO_COSTMAP, ALTO_ERROR})
126     public Response getFullMap(@PathParam("path") String path) throws JsonProcessingException{
127         QueryInput input = prepareDefaultInput(path, "numerical", "hopcount");
128         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
129         QueryOutput output = null;
130         try {
131             output = outputFuture.get().getResult();
132         } catch (Exception e) {
133             LOG.warn("get output failed:" , e);
134         }
135         Response response = buildOutput(input, output);
136         if(response != null)
137             return response;
138         else
139             return Response.status(404).build();
140     }
141
142     @Path("{path}/{mode}")
143     @GET
144     @Produces({ALTO_COSTMAP, ALTO_ERROR})
145     public Response getFullMap(@PathParam("path") String path,
146                                @PathParam("mode") String mode) throws JsonProcessingException{
147         QueryInput input = prepareDefaultInput(path, mode, "hopcount");
148         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
149         QueryOutput output = null;
150         try {
151             output = outputFuture.get().getResult();
152         } catch (Exception e) {
153             LOG.warn("get output failed:" , e);
154         }
155         Response response = buildOutput(input, output);
156         if(response != null)
157             return response;
158         else
159             return Response.status(404).build();
160     }
161
162     @Path("{path}/{mode}/{metric}")
163     @GET
164     @Produces({ALTO_COSTMAP, ALTO_ERROR})
165     public Response getFullMap(@PathParam("path") String path,
166                                @PathParam("mode") String mode,
167                                @PathParam("metric") String metric) throws JsonProcessingException{
168         QueryInput input = prepareDefaultInput(path, mode, metric);
169         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
170         QueryOutput output = null;
171         try {
172             output = outputFuture.get().getResult();
173         } catch (Exception e) {
174             LOG.warn("get output failed:" , e);
175         }
176         Response response = buildOutput(input, output);
177         if(response != null)
178             return response;
179         else
180             return Response.status(404).build();
181     }
182
183     @Path("{path}")
184     @POST
185     @Consumes({ALTO_COSTMAP_FILTER})
186     @Produces({ALTO_COSTMAP, ALTO_ERROR})
187     public Response getFilteredMap(@PathParam("path") String path, String filter) throws JsonProcessingException {
188         Response error;
189
190         String cost_mode = null;
191         String cost_metric = null;
192         List<String> pid_source = null;
193         List<String> pid_destination = null;
194
195
196         try {
197             JsonNode filterNode = mapper.readTree(filter);
198
199             JsonNode _pids = filterNode.get(FIELD_PIDS);
200
201             error = CostmapRouteChecker.checkMissing(_pids, FIELD_PIDS, filter);
202             if (error != null)
203                 return error;
204
205             error = CostmapRouteChecker.checkList(_pids, FIELD_PIDS, filter);
206             if (error != null)
207                 return error;
208
209             //need check pid_source
210             JsonNode _pid_source = _pids.get(FIELD_PID_SOURCE);
211             error = CostmapRouteChecker.checkList(_pid_source, FIELD_PID_SOURCE, filter);
212             if (error != null){
213                 return error;
214             }
215             pid_source = arrayNode2List(FIELD_PID_SOURCE, (ArrayNode)_pid_source);
216
217             //need check pid_destination
218             JsonNode _pid_destination = _pids.get(FIELD_PID_DESTINSTION);
219             error = CostmapRouteChecker.checkList(_pid_destination, FIELD_PID_DESTINSTION, filter);
220             if (error != null){
221                 return error;
222             }
223             pid_destination = arrayNode2List(FIELD_PID_DESTINSTION, (ArrayNode) _pid_destination);
224
225             JsonNode _cost_type = filterNode.get(FIELD_COST_TYPE);
226             if(_cost_type == null){
227                 error = null;
228                 return error;
229             }else {
230                 cost_mode = _cost_type.get(FIELD_COST_MODE).asText();
231                 cost_metric = _cost_type.get(FIELD_COST_METRIC).asText();
232             }
233
234
235         } catch (JsonProcessingException e) {
236             throw e;
237         } catch (Exception e) {
238             return Response.status(500).build();
239         }
240
241         //TODO
242         QueryInput input = prepareInput(path, cost_mode, cost_metric, pid_source, pid_destination);
243         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
244         QueryOutput output = null;
245         try {
246             output = outputFuture.get().getResult();
247         } catch (Exception e) {
248             LOG.warn("get output failed:" , e);
249         }
250         Response response = buildOutput(input, output);
251         if(response != null)
252             return response;
253         else
254             return Response.status(404).build();
255     }
256
257     protected List<String> arrayNode2List(String field, ArrayNode node) {
258         HashSet<String> retval = new HashSet<String>();
259
260         for (Iterator<JsonNode> itr = node.elements(); itr.hasNext(); ) {
261             JsonNode data = itr.next();
262
263             retval.add(data.asText());
264         }
265         return new LinkedList<String>(retval);
266     }
267
268     protected QueryInput prepareDefaultInput(String rid, String cost_mode, String cost_metric) {
269         /*
270          * Set source and destination pids as empty so all PID pairs should be returned.
271          *
272          * See https://tools.ietf.org/html/rfc7285#section-11.3.2.3
273          *
274          * */
275         return prepareInput(rid, cost_mode, cost_metric, new LinkedList<String>(), new LinkedList<String>());
276     }
277
278     protected QueryInput prepareInput(String path, String cost_mode, String cost_metric, List<String> pid_source, List<String> pid_destination) {
279         //TODO
280         QueryInputBuilder queryInputBuilder = new QueryInputBuilder();
281
282         CostmapRequestBuilder costmapRequestBuilder = new CostmapRequestBuilder();
283         CostmapParamsBuilder costmapParamsBuilder = new CostmapParamsBuilder();
284
285         CostTypeBuilder costTypeBuilder = new CostTypeBuilder();
286         costTypeBuilder.setCostMetric(new CostMetric(cost_metric));
287         costTypeBuilder.setCostMode(cost_mode);
288
289         CostmapFilterBuilder costmapFilterBuilder = new CostmapFilterBuilder();
290
291         List<PidName> pidNames1 = new LinkedList<PidName>();
292         for (String pid:pid_source){
293             PidName p = new PidName(pid);
294             pidNames1.add(p);
295         }
296         costmapFilterBuilder.setPidSource(pidNames1);
297
298         List<PidName> pidNames2 = new LinkedList<PidName>();
299         for (String pid:pid_destination){
300             PidName p = new PidName(pid);
301             pidNames2.add(p);
302         }
303         costmapFilterBuilder.setPidDestination(pidNames2);
304
305         CostmapFilterDataBuilder filterdata = new CostmapFilterDataBuilder();
306         filterdata.setCostmapFilter(costmapFilterBuilder.build());
307
308         costmapParamsBuilder.setFilter(filterdata.build());
309         costmapParamsBuilder.setCostType(costTypeBuilder.build());
310
311         costmapRequestBuilder.setCostmapParams(costmapParamsBuilder.build());
312
313         ReadOnlyTransaction rtx = dataBroker.newReadOnlyTransaction();
314         InstanceIdentifier<ContextTag> ctagIID = getResourceByPath(path, rtx);
315         if(ctagIID == null){
316             return null;
317         }
318
319         queryInputBuilder.setRequest(costmapRequestBuilder.build());
320         queryInputBuilder.setType(ResourceTypeCostmap.class);
321         queryInputBuilder.setServiceReference(ctagIID);
322         return queryInputBuilder.build();
323     }
324
325     public InstanceIdentifier<ContextTag> getResourceByPath(String path, ReadTransaction transaction){
326         //get iid from (list Records)
327         InstanceIdentifier<Record> recordIID = InstanceIdentifier.builder(Records.class).child(Record.class, new RecordKey(new Uri(path))).build();
328
329         Future<Optional<Record>> recordFuture = transaction.read(LogicalDatastoreType.CONFIGURATION, recordIID);
330         Optional<Record> optional = null;
331         try{
332              optional = recordFuture.get();
333         }catch(Exception e){
334             LOG.error("Reading Record failed", e);
335             return null;
336         }
337         //get resourceIID from nbr-costmap.yang
338         InstanceIdentifier<?> record2resourceIID = null;
339         if(optional.isPresent())
340             record2resourceIID = optional.get().getResourceIid();
341         InstanceIdentifier<Resource> resourceIID = (InstanceIdentifier<Resource>)record2resourceIID;
342         Future<Optional<Resource>> resourceFuture = transaction.read(LogicalDatastoreType.OPERATIONAL, resourceIID);
343
344         Optional<Resource> optional1 = null;
345         try{
346             optional1 = resourceFuture.get();
347         }
348         catch(Exception e){
349             LOG.error("Read resource failed:", e);
350             return null;
351         }
352         Resource resource = null;
353         if(optional1.isPresent())
354             resource = optional1.get();
355         InstanceIdentifier<ContextTag> finalresourceIID = resourceIID.child(ContextTag.class, new ContextTagKey(resource.getDefaultTag()));
356         return finalresourceIID;
357
358     }
359
360
361
362     protected RFC7285CostMap.Meta buildMeta(InstanceIdentifier<?> iid, RFC7285CostType costtype) {
363         RFC7285CostMap.Meta meta = new RFC7285CostMap.Meta();
364         RFC7285VersionTag vtag = new RFC7285VersionTag();
365         vtag.rid = iid.firstKeyOf(Resource.class).getResourceId().getValue();
366         vtag.tag = iid.firstKeyOf(ContextTag.class).getTag().getValue();
367         meta.netmap_tags.add(vtag);
368         meta.costType = costtype;
369         return meta;
370     }
371
372     //Cost.toString is a String like Ordinal [_cost=2, augmentation=[]],and I need to extrat "_cost=2" from this String
373     protected String getCostValue(Cost cost){
374         String costValue = null;
375         String costString = cost.toString();
376         String[] a1 = costString.split(",");
377         String[] a2 = a1[0].split("=");
378         costValue = a2[1];
379         return costValue;
380     }
381
382     protected Response buildOutput(QueryInput input, QueryOutput output) throws JsonProcessingException{
383
384         CostmapResponse costmapResponse = (CostmapResponse)output.getResponse();
385         CostmapResponseData responseData = costmapResponse.getCostmapResponseData();
386
387         RFC7285CostMap rfccostmap = new RFC7285CostMap();
388         RFC7285CostType rfccosttype = new RFC7285CostType();
389
390         rfccosttype.mode = responseData.getCostType().getCostMode();
391         rfccosttype.metric = responseData.getCostType().getCostMetric().getValue();
392
393         Map<String, Map<String, Object>> costmapsource
394                 = new LinkedHashMap<String, Map<String, Object>>();
395
396         for(CostmapSource source:responseData.getCostmapSource()){
397
398             Map<String, Object> dst2cost = new HashMap<String, Object>();
399
400             for(CostmapDestination destination:source.getCostmapDestination()){
401                 String destinationName = destination.getPidDestination().getValue();
402                 Cost pidcost = destination.getCost();
403                 dst2cost.put(destinationName, getCostValue(pidcost));
404             }
405
406             costmapsource.put(source.getPidSource().getValue(), dst2cost);
407
408         }
409
410         rfccostmap.meta = buildMeta(input.getServiceReference(), rfccosttype);
411         rfccostmap.map = costmapsource;
412
413         String responseString = mapper.writeValueAsString(rfccostmap);
414
415         return Response.ok(responseString, ALTO_COSTMAP).build();
416
417     }
418 }