Fix: blueprint ref wrong odl service interface
[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 m_router = null;
92
93     private  static AltoModelCostmapService mapService = null;
94     public void setDataBroker(DataBroker dataBroker) {
95         this.dataBroker = dataBroker;
96     }
97
98     public void setMapService(final AltoModelCostmapService mapService) {
99         this.mapService = mapService;
100     }
101
102     public void init() {
103
104         if (dataBroker == null) {
105             LOG.error("Failed to init: data broker is null");
106         }
107
108
109         LOG.info("AltoNorthboundRouteCostmap initiated");
110
111     }
112
113     public void register(AltoNorthboundRouter router) {
114         m_router = router;
115         m_router.addRoute("costmap", new AltoNorthboundRouteCostmap());
116     }
117
118     public void close() {
119         m_router.removeRoute("costmap");
120     }
121
122     @Path("{path}")
123     @GET
124     @Produces({ALTO_COSTMAP, ALTO_ERROR})
125     public Response getFullMap(@PathParam("path") String path) throws JsonProcessingException{
126         QueryInput input = prepareDefaultInput(path, "numerical", "hopcount");
127         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
128         QueryOutput output = null;
129         try {
130             output = outputFuture.get().getResult();
131         } catch (Exception e) {
132             LOG.warn("get output failed:" , e);
133         }
134         Response response = buildOutput(input, output);
135         if(response != null)
136             return response;
137         else
138             return Response.status(404).build();
139     }
140
141     @Path("{path}/{mode}")
142     @GET
143     @Produces({ALTO_COSTMAP, ALTO_ERROR})
144     public Response getFullMap(@PathParam("path") String path,
145                                @PathParam("mode") String mode) throws JsonProcessingException{
146         QueryInput input = prepareDefaultInput(path, mode, "hopcount");
147         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
148         QueryOutput output = null;
149         try {
150             output = outputFuture.get().getResult();
151         } catch (Exception e) {
152             LOG.warn("get output failed:" , e);
153         }
154         Response response = buildOutput(input, output);
155         if(response != null)
156             return response;
157         else
158             return Response.status(404).build();
159     }
160
161     @Path("{path}/{mode}/{metric}")
162     @GET
163     @Produces({ALTO_COSTMAP, ALTO_ERROR})
164     public Response getFullMap(@PathParam("path") String path,
165                                @PathParam("mode") String mode,
166                                @PathParam("metric") String metric) throws JsonProcessingException{
167         QueryInput input = prepareDefaultInput(path, mode, metric);
168         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
169         QueryOutput output = null;
170         try {
171             output = outputFuture.get().getResult();
172         } catch (Exception e) {
173             LOG.warn("get output failed:" , e);
174         }
175         Response response = buildOutput(input, output);
176         if(response != null)
177             return response;
178         else
179             return Response.status(404).build();
180     }
181
182     @Path("{path}")
183     @POST
184     @Consumes({ALTO_COSTMAP_FILTER})
185     @Produces({ALTO_COSTMAP, ALTO_ERROR})
186     public Response getFilteredMap(@PathParam("path") String path, String filter) throws JsonProcessingException {
187         Response error;
188
189         String cost_mode = null;
190         String cost_metric = null;
191         List<String> pid_source = null;
192         List<String> pid_destination = null;
193
194
195         try {
196             JsonNode filterNode = mapper.readTree(filter);
197
198             JsonNode _pids = filterNode.get(FIELD_PIDS);
199
200             error = CostmapRouteChecker.checkMissing(_pids, FIELD_PIDS, filter);
201             if (error != null)
202                 return error;
203
204             error = CostmapRouteChecker.checkList(_pids, FIELD_PIDS, filter);
205             if (error != null)
206                 return error;
207
208             //need check pid_source
209             JsonNode _pid_source = _pids.get(FIELD_PID_SOURCE);
210             error = CostmapRouteChecker.checkList(_pid_source, FIELD_PID_SOURCE, filter);
211             if (error != null){
212                 return error;
213             }
214             pid_source = arrayNode2List(FIELD_PID_SOURCE, (ArrayNode)_pid_source);
215
216             //need check pid_destination
217             JsonNode _pid_destination = _pids.get(FIELD_PID_DESTINSTION);
218             error = CostmapRouteChecker.checkList(_pid_destination, FIELD_PID_DESTINSTION, filter);
219             if (error != null){
220                 return error;
221             }
222             pid_destination = arrayNode2List(FIELD_PID_DESTINSTION, (ArrayNode) _pid_destination);
223
224             JsonNode _cost_type = filterNode.get(FIELD_COST_TYPE);
225             if(_cost_type == null){
226                 error = null;
227                 return error;
228             }else {
229                 cost_mode = _cost_type.get(FIELD_COST_MODE).asText();
230                 cost_metric = _cost_type.get(FIELD_COST_METRIC).asText();
231             }
232
233
234         } catch (JsonProcessingException e) {
235             throw e;
236         } catch (Exception e) {
237             return Response.status(500).build();
238         }
239
240         //TODO
241         QueryInput input = prepareInput(path, cost_mode, cost_metric, pid_source, pid_destination);
242         Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
243         QueryOutput output = null;
244         try {
245             output = outputFuture.get().getResult();
246         } catch (Exception e) {
247             LOG.warn("get output failed:" , e);
248         }
249         Response response = buildOutput(input, output);
250         if(response != null)
251             return response;
252         else
253             return Response.status(404).build();
254     }
255
256     protected List<String> arrayNode2List(String field, ArrayNode node) {
257         HashSet<String> retval = new HashSet<String>();
258
259         for (Iterator<JsonNode> itr = node.elements(); itr.hasNext(); ) {
260             JsonNode data = itr.next();
261
262             retval.add(data.asText());
263         }
264         return new LinkedList<String>(retval);
265     }
266
267     protected QueryInput prepareDefaultInput(String rid, String cost_mode, String cost_metric) {
268         /*
269          * Set source and destination pids as empty so all PID pairs should be returned.
270          *
271          * See https://tools.ietf.org/html/rfc7285#section-11.3.2.3
272          *
273          * */
274         return prepareInput(rid, cost_mode, cost_metric, new LinkedList<String>(), new LinkedList<String>());
275     }
276
277     protected QueryInput prepareInput(String path, String cost_mode, String cost_metric, List<String> pid_source, List<String> pid_destination) {
278         //TODO
279         QueryInputBuilder queryInputBuilder = new QueryInputBuilder();
280
281         CostmapRequestBuilder costmapRequestBuilder = new CostmapRequestBuilder();
282         CostmapParamsBuilder costmapParamsBuilder = new CostmapParamsBuilder();
283
284         CostTypeBuilder costTypeBuilder = new CostTypeBuilder();
285         costTypeBuilder.setCostMetric(new CostMetric(cost_metric));
286         costTypeBuilder.setCostMode(cost_mode);
287
288         CostmapFilterBuilder costmapFilterBuilder = new CostmapFilterBuilder();
289
290         List<PidName> pidNames1 = new LinkedList<PidName>();
291         for (String pid:pid_source){
292             PidName p = new PidName(pid);
293             pidNames1.add(p);
294         }
295         costmapFilterBuilder.setPidSource(pidNames1);
296
297         List<PidName> pidNames2 = new LinkedList<PidName>();
298         for (String pid:pid_destination){
299             PidName p = new PidName(pid);
300             pidNames2.add(p);
301         }
302         costmapFilterBuilder.setPidDestination(pidNames2);
303
304         CostmapFilterDataBuilder filterdata = new CostmapFilterDataBuilder();
305         filterdata.setCostmapFilter(costmapFilterBuilder.build());
306
307         costmapParamsBuilder.setFilter(filterdata.build());
308         costmapParamsBuilder.setCostType(costTypeBuilder.build());
309
310         costmapRequestBuilder.setCostmapParams(costmapParamsBuilder.build());
311
312         ReadOnlyTransaction rtx = dataBroker.newReadOnlyTransaction();
313         InstanceIdentifier<ContextTag> ctagIID = getResourceByPath(path, rtx);
314         if(ctagIID == null){
315             return null;
316         }
317
318         queryInputBuilder.setRequest(costmapRequestBuilder.build());
319         queryInputBuilder.setType(ResourceTypeCostmap.class);
320         queryInputBuilder.setServiceReference(ctagIID);
321         return queryInputBuilder.build();
322     }
323
324     public InstanceIdentifier<ContextTag> getResourceByPath(String path, ReadTransaction transaction){
325         //get iid from (list Records)
326         InstanceIdentifier<Record> recordIID = InstanceIdentifier.builder(Records.class).child(Record.class, new RecordKey(new Uri(path))).build();
327
328         Future<Optional<Record>> recordFuture = transaction.read(LogicalDatastoreType.CONFIGURATION, recordIID);
329         Optional<Record> optional = null;
330         try{
331              optional = recordFuture.get();
332         }catch(Exception e){
333             LOG.error("Reading Record failed", e);
334             return null;
335         }
336         //get resourceIID from nbr-costmap.yang
337         InstanceIdentifier<?> record2resourceIID = null;
338         if(optional.isPresent())
339             record2resourceIID = optional.get().getResourceIid();
340         InstanceIdentifier<Resource> resourceIID = (InstanceIdentifier<Resource>)record2resourceIID;
341         Future<Optional<Resource>> resourceFuture = transaction.read(LogicalDatastoreType.OPERATIONAL, resourceIID);
342
343         Optional<Resource> optional1 = null;
344         try{
345             optional1 = resourceFuture.get();
346         }
347         catch(Exception e){
348             LOG.error("Read resource failed:", e);
349             return null;
350         }
351         Resource resource = null;
352         if(optional1.isPresent())
353             resource = optional1.get();
354         InstanceIdentifier<ContextTag> finalresourceIID = resourceIID.child(ContextTag.class, new ContextTagKey(resource.getDefaultTag()));
355         return finalresourceIID;
356
357     }
358
359
360
361     protected RFC7285CostMap.Meta buildMeta(InstanceIdentifier<?> iid, RFC7285CostType costtype) {
362         RFC7285CostMap.Meta meta = new RFC7285CostMap.Meta();
363         RFC7285VersionTag vtag = new RFC7285VersionTag();
364         vtag.rid = iid.firstKeyOf(Resource.class).getResourceId().getValue();
365         vtag.tag = iid.firstKeyOf(ContextTag.class).getTag().getValue();
366         meta.netmap_tags.add(vtag);
367         meta.costType = costtype;
368         return meta;
369     }
370
371     //Cost.toString is a String like Ordinal [_cost=2, augmentation=[]],and I need to extrat "_cost=2" from this String
372     protected String getCostValue(Cost cost){
373         String costValue = null;
374         String costString = cost.toString();
375         String[] a1 = costString.split(",");
376         String[] a2 = a1[0].split("=");
377         costValue = a2[1];
378         return costValue;
379     }
380
381     protected Response buildOutput(QueryInput input, QueryOutput output) throws JsonProcessingException{
382
383         CostmapResponse costmapResponse = (CostmapResponse)output.getResponse();
384         CostmapResponseData responseData = costmapResponse.getCostmapResponseData();
385
386         RFC7285CostMap rfccostmap = new RFC7285CostMap();
387         RFC7285CostType rfccosttype = new RFC7285CostType();
388
389         rfccosttype.mode = responseData.getCostType().getCostMode();
390         rfccosttype.metric = responseData.getCostType().getCostMetric().getValue();
391
392         Map<String, Map<String, Object>> costmapsource
393                 = new LinkedHashMap<String, Map<String, Object>>();
394
395         for(CostmapSource source:responseData.getCostmapSource()){
396
397             Map<String, Object> dst2cost = new HashMap<String, Object>();
398
399             for(CostmapDestination destination:source.getCostmapDestination()){
400                 String destinationName = destination.getPidDestination().getValue();
401                 Cost pidcost = destination.getCost();
402                 dst2cost.put(destinationName, getCostValue(pidcost));
403             }
404
405             costmapsource.put(source.getPidSource().getValue(), dst2cost);
406
407         }
408
409         rfccostmap.meta = buildMeta(input.getServiceReference(), rfccosttype);
410         rfccostmap.map = costmapsource;
411
412         String responseString = mapper.writeValueAsString(rfccostmap);
413
414         return Response.ok(responseString, ALTO_COSTMAP).build();
415
416     }
417 }