--- /dev/null
+/*
+ * Copyright (c) 2015 Yale University and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.alto.core.northbound.route.endpointcost.impl;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.base.Optional;
+import org.opendaylight.alto.core.northbound.api.AltoNorthboundRoute;
+import org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285Endpoint;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
+import org.opendaylight.controller.md.sal.binding.api.ReadTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
+import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv6Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.northbound.route.endpointcost.rev151021.Records;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.northbound.route.endpointcost.rev151021.records.Record;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.northbound.route.endpointcost.rev151021.records.RecordKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.resourcepool.rev150921.context.Resource;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.resourcepool.rev150921.context.resource.ContextTag;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.resourcepool.rev150921.context.resource.ContextTagKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.core.types.rev150921.CostMetric;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.AltoModelEndpointcostService;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.QueryInput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.QueryInputBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.QueryOutput;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.ResourceTypeEndpointcost;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.alto.request.endpointcost.request.EndpointcostRequest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.alto.request.endpointcost.request.EndpointcostRequestBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.alto.response.endpointcost.response.EndpointcostResponse;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.cost.type.container.CostType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.cost.type.container.CostTypeBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.endpointcost.request.data.EndpointcostParams;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.endpointcost.request.data.EndpointcostParamsBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rev151021.endpointcost.response.data.EndpointcostData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.Ipv4AddressData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.Ipv6AddressData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpoint.filter.data.EndpointFilterBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpoint.filter.data.endpoint.filter.Destination;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpoint.filter.data.endpoint.filter.DestinationBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpoint.filter.data.endpoint.filter.Source;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpoint.filter.data.endpoint.filter.SourceBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpointcostmap.response.data.EndpointCostMap;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpointcostmap.response.data.endpoint.cost.map.EndpointCost;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.endpointcostmap.response.data.endpoint.cost.map.endpoint.cost.Cost;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.query.input.request.endpointcost.request.endpointcost.params.filter.EndpointFilterData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.query.input.request.endpointcost.request.endpointcost.params.filter.EndpointFilterDataBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.query.input.request.endpointcost.request.endpointcost.params.filter.endpoint.filter.data.endpoint.filter.source.address.Ipv4Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.query.input.request.endpointcost.request.endpointcost.params.filter.endpoint.filter.data.endpoint.filter.source.address.Ipv6Builder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.query.output.response.endpointcost.response.endpointcost.data.EndpointCostmapData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.service.model.endpointcost.rfc7285.rev151021.typed.address.data.Address;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Response;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+public class AltoNorthboundRouteEndpointcost implements BindingAwareProvider, AutoCloseable, AltoNorthboundRoute {
+ public static final String ENDPOINTCOST_ROUTE = "endpointcost";
+
+ public static final String ALTO_ENDPOINTCOST_FILTER = "application/alto-endpointcostfilter+json";
+ public static final String ALTO_ENDPOINTCOST = "application/alto-endpointcost+json";
+
+ public static final String FIELD_ENDPOINTS = "endpoints";
+ public static final String FIELD_COST_TYPE = "cost-type";
+ public static final String FIELD_COST_MODE = "cost-mode";
+ public static final String FIELD_COST_METRIC = "cost-metric";
+ public static final String FIELD_SOURCE = "srcs";
+ public static final String FIELD_DESTINSTION = "dsts";
+
+ private ObjectMapper mapper = new ObjectMapper();
+ private static final Logger LOG = LoggerFactory.getLogger(AltoNorthboundRouteEndpointcost.class);
+ private static DataBroker m_dataBroker = null;
+ private AltoNorthboundRouter m_router = null;
+
+ private static AltoModelEndpointcostService mapService = null;
+ @Override
+ public void onSessionInitiated(ProviderContext session) {
+ m_dataBroker = session.getSALService(DataBroker.class);
+ if (m_dataBroker == null) {
+ LOG.error("Failed to init: data broker is null");
+ }
+
+ mapService = session.getRpcService(AltoModelEndpointcostService.class);
+ LOG.info("AltoNorthboundRouteEndpointcost initiated");
+
+ }
+
+ public void register(AltoNorthboundRouter router) {
+ m_router = router;
+ m_router.addRoute("endpointcost", new AltoNorthboundRouteEndpointcost());
+ }
+
+ @Override
+ public void close() {
+ m_router.removeRoute("endpointcost");
+ }
+
+ @Path("{path}")
+ @GET
+ @Produces({ALTO_ENDPOINTCOST, ALTO_ERROR})
+ public Response getFullMap(@PathParam("path") String path) throws JsonProcessingException{
+ QueryInput input = prepareDefaultInput(path);
+ Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
+ QueryOutput output = null;
+ try {
+ output = outputFuture.get().getResult();
+ } catch (Exception e) {
+ LOG.warn("get output failed:" , e);
+ }
+ Response response = buildOutput(input, output);
+ if(response != null)
+ return response;
+ else
+ return Response.status(404).build();
+ }
+
+ @Path("{path}")
+ @POST
+ @Consumes({ALTO_ENDPOINTCOST_FILTER})
+ @Produces({ALTO_ENDPOINTCOST, ALTO_ERROR})
+ public Response getFilteredMap(@PathParam("path") String path, String filter) throws JsonProcessingException {
+ Response error;
+
+ String cost_mode = null;
+ String cost_metric = null;
+ List<String> endpoints_source = null;
+ List<String> endpoints_destination = null;
+
+
+ try {
+ JsonNode filterNode = mapper.readTree(filter);
+
+ JsonNode _endpoints = filterNode.get(FIELD_ENDPOINTS);
+
+ error = EndpointcostRouteChecker.checkMissing(_endpoints, FIELD_ENDPOINTS, filter);
+ if (error != null)
+ return error;
+
+ error = EndpointcostRouteChecker.checkList(_endpoints, FIELD_ENDPOINTS, filter);
+ if (error != null)
+ return error;
+
+ //need check endpoints_source
+ JsonNode _endpoints_source = _endpoints.get(FIELD_SOURCE);
+ error = EndpointcostRouteChecker.checkList(_endpoints_source, FIELD_SOURCE, filter);
+ if (error != null){
+ return error;
+ }
+ endpoints_source = arrayNode2List(FIELD_SOURCE, (ArrayNode)_endpoints_source);
+
+ //need check endpoints_destination
+ JsonNode _endpoints_destination = _endpoints.get(FIELD_DESTINSTION);
+ error = EndpointcostRouteChecker.checkList(_endpoints_destination, FIELD_DESTINSTION, filter);
+ if (error != null){
+ return error;
+ }
+ endpoints_destination = arrayNode2List(FIELD_DESTINSTION, (ArrayNode) _endpoints_destination);
+
+ JsonNode _cost_type = filterNode.get(FIELD_COST_TYPE);
+ if(_cost_type == null){
+ error = null;
+ return error;
+ }else {
+ cost_mode = _cost_type.get(FIELD_COST_MODE).asText();
+ cost_metric = _cost_type.get(FIELD_COST_METRIC).asText();
+ }
+
+
+ } catch (JsonProcessingException e) {
+ throw e;
+ } catch (Exception e) {
+ return Response.status(500).build();
+ }
+
+ //TODO
+ QueryInput input = prepareInput(path, cost_mode, cost_metric, endpoints_source, endpoints_destination);
+ Future<RpcResult<QueryOutput>> outputFuture = mapService.query(input);
+ QueryOutput output = null;
+ try {
+ output = outputFuture.get().getResult();
+ } catch (Exception e) {
+ LOG.warn("get output failed:" , e);
+ }
+ Response response = buildOutput(input, output);
+ if(response != null)
+ return response;
+ else
+ return Response.status(404).build();
+ }
+
+ protected List<String> arrayNode2List(String field, ArrayNode node) {
+ HashSet<String> retval = new HashSet<String>();
+
+ for (Iterator<JsonNode> itr = node.elements(); itr.hasNext(); ) {
+ JsonNode data = itr.next();
+
+ retval.add(data.asText());
+ }
+ return new LinkedList<String>(retval);
+ }
+
+ protected QueryInput prepareDefaultInput(String rid) {
+ /*
+ * Set pids as empty so all PID should be returned.
+ *
+ * Set address-types as missing so all address types should be returned.
+ *
+ * See https://tools.ietf.org/html/rfc7285#section-11.3.1.3
+ *
+ * */
+ return prepareInput(rid, null, null, new LinkedList<String>(), new LinkedList<String>());
+ }
+
+ protected QueryInput prepareInput(String path, String cost_mode, String cost_metric, List<String> endpoints_source, List<String> endpoints_destination) {
+ //TODO
+ QueryInputBuilder queryInputBuilder = new QueryInputBuilder();
+
+ //set request
+ EndpointcostRequestBuilder endpointcostRequestBuilder = new EndpointcostRequestBuilder();
+ EndpointcostParamsBuilder endpointcostParamsBuilder = new EndpointcostParamsBuilder();
+
+ CostTypeBuilder costTypeBuilder = new CostTypeBuilder();
+ costTypeBuilder.setCostMetric(new CostMetric(cost_metric));
+ costTypeBuilder.setCostMode(cost_mode);
+
+ EndpointFilterBuilder endpointFilterBuilder = new EndpointFilterBuilder();
+
+ List<Source> sources = new ArrayList<Source>();
+ List<Destination> destinations = new ArrayList<Destination>();
+
+ for(String source : endpoints_source){
+ SourceBuilder sourceBuilder = new SourceBuilder();
+ String[] tmp= source.split(":");
+ Ipv4Builder ipv4 = new Ipv4Builder();
+ Ipv6Builder ipv6 = new Ipv6Builder();
+ if(tmp[0].equals("ipv4")){
+ ipv4.setIpv4(new Ipv4Address(tmp[1]));
+ sourceBuilder.setAddress(ipv4.build());
+ }else if(tmp[0].equals("ipv6")){
+ ipv6.setIpv6(new Ipv6Address(tmp[1]));
+ sourceBuilder.setAddress(ipv6.build());
+ }
+ sources.add(sourceBuilder.build());
+ }
+ for(String destination : endpoints_destination){
+ DestinationBuilder destinationBuilder = new DestinationBuilder();
+ String[] tmp= destination.split(":");
+ Ipv4Builder ipv4 = new Ipv4Builder();
+ Ipv6Builder ipv6 = new Ipv6Builder();
+ if(tmp[0].equals("ipv4")){
+ ipv4.setIpv4(new Ipv4Address(tmp[1]));
+ destinationBuilder.setAddress(ipv4.build());
+ }else if(tmp[0].equals("ipv4")){
+ ipv6.setIpv6(new Ipv6Address(tmp[1]));
+ destinationBuilder.setAddress(ipv6.build());
+ }else return null;
+ destinations.add(destinationBuilder.build());
+ }
+ endpointFilterBuilder.setSource(sources);
+ endpointFilterBuilder.setDestination(destinations);
+ EndpointFilterDataBuilder endpointFilterDataBuilder = new EndpointFilterDataBuilder();
+ endpointFilterDataBuilder.setEndpointFilter(endpointFilterBuilder.build());
+
+ endpointcostParamsBuilder.setCostType(costTypeBuilder.build());
+ endpointcostParamsBuilder.setFilter(endpointFilterDataBuilder.build());
+ endpointcostRequestBuilder.setEndpointcostParams(endpointcostParamsBuilder.build());
+
+ //create servicereference by getting contexttag from resourcepool
+ ReadOnlyTransaction rtx = m_dataBroker.newReadOnlyTransaction();
+ InstanceIdentifier<ContextTag> ctagIID = getResourceByPath(path, rtx);
+ if(ctagIID == null){
+ return null;
+ }
+
+ queryInputBuilder.setRequest(endpointcostRequestBuilder.build());
+ queryInputBuilder.setServiceReference(ctagIID);
+ queryInputBuilder.setType(ResourceTypeEndpointcost.class);
+ return queryInputBuilder.build();
+ }
+
+ public InstanceIdentifier<ContextTag> getResourceByPath(String path, ReadTransaction transaction){
+ //get iid from (list Records)
+ InstanceIdentifier<Record> recordIID = InstanceIdentifier.builder(Records.class).child(Record.class, new RecordKey(new Uri(path))).build();
+
+ Future<Optional<Record>> recordFuture = transaction.read(LogicalDatastoreType.CONFIGURATION, recordIID);
+ Optional<Record> optional = null;
+ try{
+ optional = recordFuture.get();
+ }catch(Exception e){
+ LOG.error("Reading Record failed", e);
+ return null;
+ }
+ //get resourceIID from nbr-costmap.yang
+ InstanceIdentifier<?> record2resourceIID = null;
+ if(optional.isPresent())
+ record2resourceIID = optional.get().getResourceIid();
+ InstanceIdentifier<Resource> resourceIID = (InstanceIdentifier<Resource>)record2resourceIID;
+ Future<Optional<Resource>> resourceFuture = transaction.read(LogicalDatastoreType.OPERATIONAL, resourceIID);
+
+ Optional<Resource> optional1 = null;
+ try{
+ optional1 = resourceFuture.get();
+ }
+ catch(Exception e){
+ LOG.error("Read resource failed:", e);
+ return null;
+ }
+ Resource resource = null;
+ if(optional1.isPresent())
+ resource = optional1.get();
+ InstanceIdentifier<ContextTag> finalresourceIID = resourceIID.child(ContextTag.class, new ContextTagKey(resource.getDefaultTag()));
+ return finalresourceIID;
+ }
+
+
+
+ protected RFC7285Endpoint.CostResponse.Meta buildMeta(RFC7285CostType rfccostType) {
+ RFC7285Endpoint.CostResponse.Meta meta = new RFC7285Endpoint.CostResponse.Meta();
+ meta.costType = rfccostType;
+ return meta;
+ }
+
+ //Cost.toString is a String like Ordinal [_cost=2, augmentation=[]],and I need to extrat "_cost=2" from this String
+ protected String getCostValue(Cost cost){
+ String costValue = null;
+ String costString = cost.toString();
+ String[] a1 = costString.split(",");
+ String[] a2 = a1[0].split("=");
+ costValue = a2[1];
+ return costValue;
+ }
+ protected String getAddressValue(Address address){
+ String addressValue = null;
+ if(address instanceof Ipv4AddressData){
+ Ipv4AddressData ipv4 = (Ipv4AddressData)address;
+ addressValue ="ipv4:" + ipv4.getIpv4().getValue();
+ }else if(address instanceof Ipv6AddressData){
+ Ipv6AddressData ipv6 = (Ipv6AddressData)address;
+ addressValue = "ipv6:"+ipv6.getIpv6().getValue();
+ }
+ return addressValue;
+ }
+
+ protected Response buildOutput(QueryInput input, QueryOutput output) throws JsonProcessingException{
+
+ //get costtype and endpoint source
+ EndpointcostRequest request = (EndpointcostRequest) input.getRequest();
+ EndpointcostParams params = request.getEndpointcostParams();
+ EndpointFilterData filterData = (EndpointFilterData) params.getFilter();
+
+ CostType costType = params.getCostType();
+ String cost_metric = costType.getCostMetric().getValue();
+ String cost_mode = costType.getCostMode();
+
+ List<Source> endpoints_source = filterData.getEndpointFilter().getSource();
+
+ //translate costtype to rfccosttype
+ RFC7285CostType rfccostType = new RFC7285CostType();
+ rfccostType.metric = cost_metric;
+ rfccostType.mode = cost_mode;
+
+ EndpointcostResponse ecResponse = (EndpointcostResponse)output.getResponse();
+ EndpointcostData ecData = ecResponse.getEndpointcostData();
+ EndpointCostmapData ecmData = null;
+ if (ecData instanceof EndpointCostmapData) {
+ ecmData = (EndpointCostmapData) ecResponse.getEndpointcostData();
+ }else return Response.ok("there is not endpointcostmap").build();
+
+ EndpointCostMap endpointCostMap = ecmData.getEndpointCostMap();
+ List<EndpointCost> endpointCostList = endpointCostMap.getEndpointCost();
+
+ Map<String, Map<String, Object>> answer = new LinkedHashMap<String, Map<String, Object>>();
+
+ for(Source endpointsource : endpoints_source) {
+ Map<String, Object> dst2cost = new HashMap<>();
+ String endpointSourceAddressValue = getAddressValue(endpointsource.getAddress());
+ for (EndpointCost endpointCost : endpointCostList) {
+ String costSourceAddressValue = getAddressValue(endpointCost.getSource().getAddress());
+ String costDestinactionAddressValue = getAddressValue(endpointCost.getDestination().getAddress());
+ if(endpointSourceAddressValue.equals(costSourceAddressValue))
+ dst2cost.put(costDestinactionAddressValue, getCostValue(endpointCost.getCost()));
+ }
+ answer.put(endpointSourceAddressValue, dst2cost);
+ }
+
+ RFC7285Endpoint.CostResponse rfcEndpointcost = new RFC7285Endpoint.CostResponse();
+ rfcEndpointcost.meta = buildMeta(rfccostType);
+ rfcEndpointcost.answer = answer;
+
+ String responseString = mapper.writeValueAsString(rfcEndpointcost);
+ return Response.ok(responseString, ALTO_ENDPOINTCOST).build();
+ }
+}