2 * Copyright (c) 2015 Yale University and others. All rights reserved.
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
9 package org.opendaylight.alto.core.northbound.route.costmap.impl;
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
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;
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;
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;
71 import java.util.concurrent.Future;
73 public class AltoNorthboundRouteCostmap implements AltoNorthboundRoute {
74 public static final String COSTMAP_ROUTE = "costmap";
76 public static final String ALTO_COSTMAP_FILTER = "application/alto-costmapfilter+json";
77 public static final String ALTO_COSTMAP = "application/alto-costmap+json";
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";
86 private ObjectMapper mapper = new ObjectMapper();
87 private static final Logger LOG = LoggerFactory.getLogger(AltoNorthboundRouteCostmap.class);
89 private DataBroker dataBroker = null;
91 private AltoNorthboundRouter m_router = null;
93 private static AltoModelCostmapService mapService = null;
94 public void setDataBroker(DataBroker dataBroker) {
95 this.dataBroker = dataBroker;
98 public void setMapService(final AltoModelCostmapService mapService) {
99 this.mapService = mapService;
104 if (dataBroker == null) {
105 LOG.error("Failed to init: data broker is null");
109 LOG.info("AltoNorthboundRouteCostmap initiated");
113 public void register(AltoNorthboundRouter router) {
115 m_router.addRoute("costmap", new AltoNorthboundRouteCostmap());
118 public void close() {
119 m_router.removeRoute("costmap");
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;
130 output = outputFuture.get().getResult();
131 } catch (Exception e) {
132 LOG.warn("get output failed:" , e);
134 Response response = buildOutput(input, output);
138 return Response.status(404).build();
141 @Path("{path}/{mode}")
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;
150 output = outputFuture.get().getResult();
151 } catch (Exception e) {
152 LOG.warn("get output failed:" , e);
154 Response response = buildOutput(input, output);
158 return Response.status(404).build();
161 @Path("{path}/{mode}/{metric}")
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;
171 output = outputFuture.get().getResult();
172 } catch (Exception e) {
173 LOG.warn("get output failed:" , e);
175 Response response = buildOutput(input, output);
179 return Response.status(404).build();
184 @Consumes({ALTO_COSTMAP_FILTER})
185 @Produces({ALTO_COSTMAP, ALTO_ERROR})
186 public Response getFilteredMap(@PathParam("path") String path, String filter) throws JsonProcessingException {
189 String cost_mode = null;
190 String cost_metric = null;
191 List<String> pid_source = null;
192 List<String> pid_destination = null;
196 JsonNode filterNode = mapper.readTree(filter);
198 JsonNode _pids = filterNode.get(FIELD_PIDS);
200 error = CostmapRouteChecker.checkMissing(_pids, FIELD_PIDS, filter);
204 error = CostmapRouteChecker.checkList(_pids, FIELD_PIDS, filter);
208 //need check pid_source
209 JsonNode _pid_source = _pids.get(FIELD_PID_SOURCE);
210 error = CostmapRouteChecker.checkList(_pid_source, FIELD_PID_SOURCE, filter);
214 pid_source = arrayNode2List(FIELD_PID_SOURCE, (ArrayNode)_pid_source);
216 //need check pid_destination
217 JsonNode _pid_destination = _pids.get(FIELD_PID_DESTINSTION);
218 error = CostmapRouteChecker.checkList(_pid_destination, FIELD_PID_DESTINSTION, filter);
222 pid_destination = arrayNode2List(FIELD_PID_DESTINSTION, (ArrayNode) _pid_destination);
224 JsonNode _cost_type = filterNode.get(FIELD_COST_TYPE);
225 if(_cost_type == null){
229 cost_mode = _cost_type.get(FIELD_COST_MODE).asText();
230 cost_metric = _cost_type.get(FIELD_COST_METRIC).asText();
234 } catch (JsonProcessingException e) {
236 } catch (Exception e) {
237 return Response.status(500).build();
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;
245 output = outputFuture.get().getResult();
246 } catch (Exception e) {
247 LOG.warn("get output failed:" , e);
249 Response response = buildOutput(input, output);
253 return Response.status(404).build();
256 protected List<String> arrayNode2List(String field, ArrayNode node) {
257 HashSet<String> retval = new HashSet<String>();
259 for (Iterator<JsonNode> itr = node.elements(); itr.hasNext(); ) {
260 JsonNode data = itr.next();
262 retval.add(data.asText());
264 return new LinkedList<String>(retval);
267 protected QueryInput prepareDefaultInput(String rid, String cost_mode, String cost_metric) {
269 * Set source and destination pids as empty so all PID pairs should be returned.
271 * See https://tools.ietf.org/html/rfc7285#section-11.3.2.3
274 return prepareInput(rid, cost_mode, cost_metric, new LinkedList<String>(), new LinkedList<String>());
277 protected QueryInput prepareInput(String path, String cost_mode, String cost_metric, List<String> pid_source, List<String> pid_destination) {
279 QueryInputBuilder queryInputBuilder = new QueryInputBuilder();
281 CostmapRequestBuilder costmapRequestBuilder = new CostmapRequestBuilder();
282 CostmapParamsBuilder costmapParamsBuilder = new CostmapParamsBuilder();
284 CostTypeBuilder costTypeBuilder = new CostTypeBuilder();
285 costTypeBuilder.setCostMetric(new CostMetric(cost_metric));
286 costTypeBuilder.setCostMode(cost_mode);
288 CostmapFilterBuilder costmapFilterBuilder = new CostmapFilterBuilder();
290 List<PidName> pidNames1 = new LinkedList<PidName>();
291 for (String pid:pid_source){
292 PidName p = new PidName(pid);
295 costmapFilterBuilder.setPidSource(pidNames1);
297 List<PidName> pidNames2 = new LinkedList<PidName>();
298 for (String pid:pid_destination){
299 PidName p = new PidName(pid);
302 costmapFilterBuilder.setPidDestination(pidNames2);
304 CostmapFilterDataBuilder filterdata = new CostmapFilterDataBuilder();
305 filterdata.setCostmapFilter(costmapFilterBuilder.build());
307 costmapParamsBuilder.setFilter(filterdata.build());
308 costmapParamsBuilder.setCostType(costTypeBuilder.build());
310 costmapRequestBuilder.setCostmapParams(costmapParamsBuilder.build());
312 ReadOnlyTransaction rtx = dataBroker.newReadOnlyTransaction();
313 InstanceIdentifier<ContextTag> ctagIID = getResourceByPath(path, rtx);
318 queryInputBuilder.setRequest(costmapRequestBuilder.build());
319 queryInputBuilder.setType(ResourceTypeCostmap.class);
320 queryInputBuilder.setServiceReference(ctagIID);
321 return queryInputBuilder.build();
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();
328 Future<Optional<Record>> recordFuture = transaction.read(LogicalDatastoreType.CONFIGURATION, recordIID);
329 Optional<Record> optional = null;
331 optional = recordFuture.get();
333 LOG.error("Reading Record failed", e);
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);
343 Optional<Resource> optional1 = null;
345 optional1 = resourceFuture.get();
348 LOG.error("Read resource failed:", e);
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;
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;
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("=");
381 protected Response buildOutput(QueryInput input, QueryOutput output) throws JsonProcessingException{
383 CostmapResponse costmapResponse = (CostmapResponse)output.getResponse();
384 CostmapResponseData responseData = costmapResponse.getCostmapResponseData();
386 RFC7285CostMap rfccostmap = new RFC7285CostMap();
387 RFC7285CostType rfccosttype = new RFC7285CostType();
389 rfccosttype.mode = responseData.getCostType().getCostMode();
390 rfccosttype.metric = responseData.getCostType().getCostMetric().getValue();
392 Map<String, Map<String, Object>> costmapsource
393 = new LinkedHashMap<String, Map<String, Object>>();
395 for(CostmapSource source:responseData.getCostmapSource()){
397 Map<String, Object> dst2cost = new HashMap<String, Object>();
399 for(CostmapDestination destination:source.getCostmapDestination()){
400 String destinationName = destination.getPidDestination().getValue();
401 Cost pidcost = destination.getCost();
402 dst2cost.put(destinationName, getCostValue(pidcost));
405 costmapsource.put(source.getPidSource().getValue(), dst2cost);
409 rfccostmap.meta = buildMeta(input.getServiceReference(), rfccosttype);
410 rfccostmap.map = costmapsource;
412 String responseString = mapper.writeValueAsString(rfccostmap);
414 return Response.ok(responseString, ALTO_COSTMAP).build();