Implement the basic structure for multicost 18/53518/3
authorKai Gao <gaok12@mails.tsinghua.edu.cn>
Sun, 12 Mar 2017 03:36:22 +0000 (22:36 -0500)
committerjensenzhang <jingxuan.n.zhang@gmail.com>
Sun, 9 Apr 2017 15:43:11 +0000 (23:43 +0800)
Change-Id: Ia61d469772d701e5221da1ec9b20ba831a57111f
Signed-off-by: Kai Gao <gaok12@mails.tsinghua.edu.cn>
16 files changed:
.gitignore
alto-core/northbound/impl/src/main/resources/org/opendaylight/blueprint/impl-blueprint.xml
alto-extensions/multicost/impl/pom.xml
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostProvider.java
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostRequestListener.java [new file with mode: 0644]
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostService.java [new file with mode: 0644]
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/Condition.java [new file with mode: 0644]
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostRequest.java [new file with mode: 0644]
alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostResponse.java [new file with mode: 0644]
alto-extensions/multicost/impl/src/main/resources/org/opendaylight/blueprint/impl-blueprint.xml
alto-extensions/multicost/impl/src/test/java/org/opendaylight/alto/multicost/impl/MulticostServiceTest.java [new file with mode: 0644]
alto-release-features/features-alto/src/main/features/features.xml
alto-release-features/odl-alto-extension/pom.xml
alto-release-features/odl-alto-iupd/pom.xml
alto-release-features/odl-alto-multicost/pom.xml
alto-release-features/odl-alto-rsabs/pom.xml

index f2475ba27c0975fcaf84e143e5cabfcd9031c94b..63637467b6fb75758635109ea08ffd49bae1d68b 100644 (file)
@@ -38,3 +38,4 @@ xtend-gen
 yang-gen-code
 yang-gen-config
 yang-gen-sal
+.ensime
index 7d86efc8bc5eb8046e10244d59d0237dbb8e4e62..98620ced5ba9c0550a191f7245096236e4c61cfa 100644 (file)
@@ -8,11 +8,15 @@ terms of the Eclipse Public License v1.0 which accompanies this distribution,
 and is available at http://www.eclipse.org/legal/epl-v10.html
 -->
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-  odl:use-default-for-reference-types="true">
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           odl:use-default-for-reference-types="true">
   <bean id="provider"
-    class="org.opendaylight.alto.core.northbound.impl.AltoNorthboundProvider"
-    init-method="init" destroy-method="close">
+        class="org.opendaylight.alto.core.northbound.impl.AltoNorthboundProvider"
+        init-method="init" destroy-method="close">
   </bean>
-  <service ref="provider" interface="org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter"/>
+
+  <service ref="provider"
+           interface="org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter"
+           odl:type="default"/>
+
 </blueprint>
index 6911347970e287576340e4b0a1c7ade5780bdfd2..54e3d8c081fbb6fc1cba6bb2b0217a6d4ba797d8 100644 (file)
@@ -46,5 +46,47 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
       <artifactId>mockito-core</artifactId>
       <scope>test</scope>
     </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.alto.core</groupId>
+      <artifactId>alto-northbound-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.opendaylight.alto.core</groupId>
+      <artifactId>alto-northbound-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+
+    <dependency>
+      <groupId>org.eclipse.jetty</groupId>
+      <artifactId>jetty-servlets</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.jersey</groupId>
+      <artifactId>jersey-server</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>javax.servlet</groupId>
+      <artifactId>javax.servlet-api</artifactId>
+    </dependency>
+    <dependency>
+      <artifactId>junit</artifactId>
+      <groupId>junit</groupId>
+    </dependency>
+
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-databind</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.core</groupId>
+      <artifactId>jackson-core</artifactId>
+    </dependency>
   </dependencies>
 </project>
index 800f954e487b24a97d5afe14d57ddf1aa1d21ce6..b087e051961fec3370cb2e6839d0e58acdc7b2f0 100644 (file)
@@ -7,18 +7,53 @@
  */
 package org.opendaylight.alto.multicost.impl;
 
+import org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.multicost.rev170302.MulticostData;
+import org.opendaylight.yangtools.concepts.ListenerRegistration;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Arrays;
+import java.util.List;
+
 public class MulticostProvider {
 
     private static final Logger LOG = LoggerFactory.getLogger(MulticostProvider.class);
 
+    private MulticostRequestListener listener;
+
+    private ListenerRegistration<?> reg;
+
     private final DataBroker dataBroker;
 
-    public MulticostProvider(final DataBroker dataBroker) {
+    private final AltoNorthboundRouter router;
+
+    private MulticostService service;
+
+    private static final DataTreeIdentifier<MulticostData> MULTICOST_DATAROOT;
+
+    private static final String NUMERTICAL = "numerical";
+
+    private static final String ROUTINGCOST = "routingcost";
+
+    private static final String HOPCOUNT = "hopcount";
+
+    private static final String BANDWIDTH = "bandwidth";
+
+    static {
+        InstanceIdentifier<MulticostData> iid = InstanceIdentifier.create(MulticostData.class);
+        MULTICOST_DATAROOT = new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, iid);
+    }
+
+    public MulticostProvider(final DataBroker dataBroker,
+                             final AltoNorthboundRouter router) {
         this.dataBroker = dataBroker;
+        this.router = router;
     }
 
     /**
@@ -26,6 +61,16 @@ public class MulticostProvider {
      */
     public void init() {
         LOG.info("MulticostProvider Session Initiated");
+
+        RFC7285CostType routingcost = new RFC7285CostType(ROUTINGCOST, NUMERTICAL);
+        RFC7285CostType hopcount = new RFC7285CostType(HOPCOUNT, NUMERTICAL);
+        RFC7285CostType bandwidth = new RFC7285CostType(BANDWIDTH, NUMERTICAL);
+
+        List<RFC7285CostType> types = Arrays.asList(routingcost, hopcount, bandwidth);
+
+        service = new MulticostService(types, false);
+        listener = new MulticostRequestListener(dataBroker, service);
+        reg = dataBroker.registerDataTreeChangeListener(MULTICOST_DATAROOT, listener);
     }
 
     /**
@@ -33,5 +78,10 @@ public class MulticostProvider {
      */
     public void close() {
         LOG.info("MulticostProvider Closed");
+        try {
+            reg.close();
+        } catch (Exception e) {
+            // ignore
+        }
     }
 }
diff --git a/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostRequestListener.java b/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostRequestListener.java
new file mode 100644 (file)
index 0000000..320a72e
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright © 2017 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.multicost.impl;
+
+import java.util.Collection;
+
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
+import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.multicost.rev170302.MulticostData;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.alto.multicost.rev170302.MulticostDataBuilder;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class MulticostRequestListener implements DataTreeChangeListener<MulticostData> {
+
+    private static final Logger LOG = LoggerFactory.getLogger(MulticostRequestListener.class);
+
+    private final DataBroker db;
+
+    private MulticostService service;
+
+    public MulticostRequestListener(final DataBroker db, final MulticostService service) {
+        this.db = db;
+        this.service = service;
+    }
+
+    private void handleRequest(WriteTransaction tx,
+                               InstanceIdentifier<?> iid, DataObject value) {
+        if (iid.getTargetType() != MulticostData.class) {
+            return;
+        }
+        if (!(value instanceof MulticostData)) {
+            return;
+        }
+        try {
+            InstanceIdentifier<MulticostData> miid = iid.firstIdentifierOf(MulticostData.class);
+            MulticostData data = (MulticostData) value;
+            String request = data.getRequestBody();
+
+            String response = service.accept(request);
+
+            MulticostDataBuilder builder = new MulticostDataBuilder(data);
+            builder.setResponseBody(response);
+
+            tx.put(LogicalDatastoreType.OPERATIONAL, miid, builder.build());
+        } catch (Exception e) {
+            LOG.error("Failed to handle request: ", iid.firstKeyOf(MulticostData.class));
+            return;
+        }
+    }
+
+    void dispatch(WriteTransaction tx, DataTreeModification<MulticostData> mod) {
+        InstanceIdentifier<MulticostData> iid = mod.getRootPath().getRootIdentifier();
+        if (mod.getRootNode().getModificationType()
+            .equals(DataObjectModification.ModificationType.DELETE)) {
+            tx.delete(LogicalDatastoreType.OPERATIONAL, iid);
+        } else {
+            handleRequest(tx, iid, mod.getRootNode().getDataAfter());
+        }
+    }
+
+    @Override
+    public void onDataTreeChanged(Collection<DataTreeModification<MulticostData>> e) {
+        WriteTransaction tx = db.newWriteOnlyTransaction();
+        try {
+            e.forEach(mod -> dispatch(tx, mod));
+            tx.submit();
+        } catch (Exception exception) {
+            tx.cancel();
+        }
+    }
+
+}
diff --git a/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostService.java b/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/MulticostService.java
new file mode 100644 (file)
index 0000000..9cb53ee
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright © 2017 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.multicost.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.opendaylight.alto.core.northbound.api.exception.AltoBadFormatException;
+import org.opendaylight.alto.core.northbound.api.exception.AltoBasicException;
+import org.opendaylight.alto.core.northbound.api.exception.AltoErrorInvalidFieldValue;
+import org.opendaylight.alto.core.northbound.api.exception.AltoErrorInvalideFieldType;
+import org.opendaylight.alto.core.northbound.api.exception.AltoErrorMissingField;
+import org.opendaylight.alto.core.northbound.api.exception.AltoErrorSyntax;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+import org.opendaylight.alto.multicost.impl.data.Condition;
+import org.opendaylight.alto.multicost.impl.data.MulticostRequest;
+import org.opendaylight.alto.multicost.impl.data.MulticostResponse;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableList;
+
+class MulticostService {
+
+    private final ImmutableList<RFC7285CostType> types;
+
+    private final boolean testable;
+
+    public MulticostService(final List<RFC7285CostType> types, boolean testable) {
+        this.types = ImmutableList.copyOf(types);
+        this.testable = testable;
+    }
+
+    public String accept(final String input) {
+        try {
+            MulticostRequest req = decode(input);
+
+            MulticostResponse rep = respond(req);
+
+            return encode(rep);
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
+    private ObjectMapper mapper = new ObjectMapper();
+
+    private MulticostResponse respond(MulticostRequest request) {
+        return null;
+    }
+
+    private void checkSemantic(MulticostRequest request) throws AltoBasicException {
+        if ((request.multicostTypes == null) && (request.costType == null)) {
+            /* Either field MUST be specified */
+            throw new AltoErrorMissingField(MulticostRequest.FIELD_MULCOST);
+        }
+        if ((request.multicostTypes != null) && (request.costType != null)) {
+            /* ... but not both */
+            throw new AltoBadFormatException();
+        }
+        if (request.multicostTypes == null) {
+            /* Fallback mode */
+            request.fallbackMode = true;
+            request.multicostTypes = Arrays.asList(request.costType);
+            if (!types.contains(request.costType)) {
+                throw new AltoErrorInvalidFieldValue("cost-type");
+            }
+        } else {
+            boolean allMatch = request.multicostTypes
+                .stream()
+                .allMatch(t -> types.contains(t));
+            if (!allMatch) {
+                throw new AltoErrorInvalidFieldValue(MulticostRequest.FIELD_MULCOST);
+            }
+        }
+        if (!this.testable) {
+            if (request.constraints != null) {
+                throw new AltoErrorInvalideFieldType("constraints");
+            }
+            if (request.orConstraintsRepr != null) {
+                throw new AltoErrorInvalideFieldType(MulticostRequest.FIELD_ORCONSTRAINT);
+            }
+        } else {
+            if ((request.constraints != null) || (request.orConstraintsRepr != null)) {
+                /* ... MUST not both be specified */
+                throw new AltoErrorInvalideFieldType(MulticostRequest.FIELD_ORCONSTRAINT);
+            }
+            if (request.constraints != null) {
+                request.orConstraintsRepr = Arrays.asList(request.constraints);
+            }
+
+            if (request.testableTypes == null) {
+                request.testableTypes = request.multicostTypes;
+            }
+            boolean allMatch = request.testableTypes
+                .stream()
+                .allMatch(t -> types.contains(t));
+            if (!allMatch) {
+                throw new AltoErrorInvalidFieldValue(MulticostRequest.FIELD_TESTABLE);
+            }
+
+            AltoBasicException e;
+            e = new AltoErrorInvalidFieldValue(MulticostRequest.FIELD_ORCONSTRAINT);
+
+            request.orConstraints = request.orConstraintsRepr
+                .stream()
+                .map(l -> l.stream()
+                     .map(repr -> Condition.compile(repr, request.testableTypes, e))
+                     .collect(Collectors.toList()))
+                .collect(Collectors.toList());
+        }
+    }
+
+    private MulticostRequest decode(String input) throws AltoErrorSyntax {
+        try {
+            MulticostRequest req = mapper.readValue(input, MulticostRequest.class);
+            checkSemantic(req);
+            return req;
+        } catch (Exception e) {
+            throw new AltoErrorSyntax();
+        }
+    }
+
+    private String encode(MulticostResponse response) throws JsonProcessingException {
+        String output = mapper.writeValueAsString(response);
+        return output;
+    }
+}
diff --git a/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/Condition.java b/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/Condition.java
new file mode 100644 (file)
index 0000000..35d9c74
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright © 2017 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.multicost.impl.data;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+
+import org.opendaylight.alto.core.northbound.api.exception.AltoBasicException;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+
+public class Condition {
+
+    public static final ImmutableMap<String, BiFunction<Double, Double, Boolean>> OPERATOR_MAP;
+
+    public static final String OP_LTREPR = "lt";
+
+    public static final String OP_LEREPR = "le";
+
+    public static final String OP_EQREPR = "eq";
+
+    public static final String OP_GTREPR = "gt";
+
+    public static final String OP_GEREPR = "ge";
+
+    public static final double PRECISION = 1e-5;
+
+    public static final BiFunction<Double, Double, Boolean> OP_LT;
+    public static final BiFunction<Double, Double, Boolean> OP_LE;
+    public static final BiFunction<Double, Double, Boolean> OP_EQ;
+    public static final BiFunction<Double, Double, Boolean> OP_GT;
+    public static final BiFunction<Double, Double, Boolean> OP_GE;
+
+    static {
+        OP_LT = (x, y) -> (x < y);
+        OP_LE = (x, y) -> (x <= y);
+        OP_EQ = (x, y) -> (Math.abs(y - x) < PRECISION);
+        OP_GT = (x, y) -> (x > y);
+        OP_GE = (x, y) -> (x >= y);
+
+        Map<String, BiFunction<Double, Double, Boolean>> map = Maps.newHashMap();
+        map.put(OP_LTREPR, OP_LT);
+        map.put(OP_LEREPR, OP_LE);
+        map.put(OP_EQREPR, OP_EQ);
+        map.put(OP_GTREPR, OP_GT);
+        map.put(OP_GEREPR, OP_GE);
+
+        OPERATOR_MAP = ImmutableMap.copyOf(map);
+    }
+
+    public static Condition compile(final String repr,
+                                    List<RFC7285CostType> types,
+                                    AltoBasicException e) throws AltoBasicException {
+        String[] parts = repr.split(" ");
+        if (parts.length < 2) {
+            throw e;
+        }
+        if (parts.length == 2) {
+            String[] newParts = { "[0]", parts[0], parts[1] };
+            parts = newParts;
+        }
+        if (parts.length > 3) {
+            throw e;
+        }
+        try {
+            String irepr = parts[0].replaceAll("\\s", "");
+            irepr = irepr.substring(1, irepr.length() - 1);
+            int index = Integer.valueOf(irepr);
+            if (index > types.size()) {
+                throw e;
+            }
+            RFC7285CostType type = types.get(index);
+            BiFunction<Double, Double, Boolean> op = OPERATOR_MAP.get(parts[1]);
+            Double bound = Double.valueOf(parts[2]);
+
+            if ((type == null) || (op == null)) {
+                throw e;
+            }
+
+            return new Condition(type, op, bound);
+        } catch (Exception ignore) {
+            throw e;
+        }
+    }
+
+    private Condition(final RFC7285CostType type,
+                      final BiFunction<Double, Double, Boolean> operator,
+                      final Double bound) {
+        this.type = type;
+        this.operator = operator;
+        this.bound = bound;
+    }
+
+    public final RFC7285CostType type;
+
+    public final BiFunction<Double, Double, Boolean> operator;
+
+    public final Double bound;
+}
diff --git a/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostRequest.java b/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostRequest.java
new file mode 100644 (file)
index 0000000..3188ed8
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright © 2017 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.multicost.impl.data;
+
+import java.util.List;
+
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostMap;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MulticostRequest extends RFC7285CostMap.Filter {
+
+    public static final String FIELD_MULCOST = "multi-cost-types";
+
+    public static final String FIELD_TESTABLE = "testable-cost-types";
+
+    public static final String FIELD_ORCONSTRAINT = "or-constraints";
+
+    @JsonProperty(FIELD_MULCOST)
+    public List<RFC7285CostType> multicostTypes;
+
+    @JsonProperty(FIELD_TESTABLE)
+    public List<RFC7285CostType> testableTypes;
+
+    @JsonProperty(FIELD_ORCONSTRAINT)
+    public List<List<String>> orConstraintsRepr;
+
+    @JsonIgnore
+    public boolean fallbackMode = false;
+
+    @JsonIgnore
+    public List<List<Condition>> orConstraints;
+
+}
diff --git a/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostResponse.java b/alto-extensions/multicost/impl/src/main/java/org/opendaylight/alto/multicost/impl/data/MulticostResponse.java
new file mode 100644 (file)
index 0000000..89b8ba8
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright © 2017 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.multicost.impl.data;
+
+import java.util.List;
+
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostMap;
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+public class MulticostResponse extends RFC7285CostMap {
+
+    public static class Meta extends RFC7285CostMap.Meta {
+
+        public Meta(RFC7285CostMap.Meta base) {
+            this.netmap_tags = base.netmap_tags;
+            this.costType = base.costType;
+        }
+
+        @JsonProperty(MulticostRequest.FIELD_MULCOST)
+        public List<RFC7285CostType> multicostTypes;
+    }
+
+}
index 8d3e0e9d8e5a381d731042d9adee118a08bca484..3fb7887c4572494411bfa5a90119ea88e117960e 100644 (file)
@@ -8,17 +8,22 @@ terms of the Eclipse Public License v1.0 which accompanies this distribution,
 and is available at http://www.eclipse.org/legal/epl-v10.html
 -->
 <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
-  odl:use-default-for-reference-types="true">
+           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
+           odl:use-default-for-reference-types="true">
 
   <reference id="dataBroker"
-    interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
-    odl:type="default" />
+             interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
+             odl:type="default" />
+
+  <reference id="altonbRouter"
+             interface="org.opendaylight.alto.core.northbound.api.AltoNorthboundRouter"
+             odl:type="default" />
 
   <bean id="provider"
-    class="org.opendaylight.alto.multicost.impl.MulticostProvider"
-    init-method="init" destroy-method="close">
+        class="org.opendaylight.alto.multicost.impl.MulticostProvider"
+        init-method="init" destroy-method="close">
     <argument ref="dataBroker" />
+    <argument ref="altonbRouter" />
   </bean>
 
 </blueprint>
diff --git a/alto-extensions/multicost/impl/src/test/java/org/opendaylight/alto/multicost/impl/MulticostServiceTest.java b/alto-extensions/multicost/impl/src/test/java/org/opendaylight/alto/multicost/impl/MulticostServiceTest.java
new file mode 100644 (file)
index 0000000..6b17cd3
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright © 2017 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.multicost.impl;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+
+import org.opendaylight.alto.core.northbound.api.utils.rfc7285.RFC7285CostType;
+import org.opendaylight.alto.core.northbound.api.exception.AltoBasicException;
+import org.opendaylight.alto.core.northbound.api.exception.AltoErrorInvalidFieldValue;
+
+import org.opendaylight.alto.multicost.impl.data.Condition;
+import org.opendaylight.alto.multicost.impl.data.MulticostRequest;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class MulticostServiceTest {
+
+    static final RFC7285CostType c1 = new RFC7285CostType("routincost", "numerical");
+    static final RFC7285CostType c2 = new RFC7285CostType("hopcount", "numerical");
+    static final List<RFC7285CostType> clist = Arrays.asList(c1, c2);
+    static final AltoErrorInvalidFieldValue err = new AltoErrorInvalidFieldValue("test");
+
+    @Test
+    public void testCondition() {
+        String repr;
+        Condition condition;
+
+        repr = "[0] le 100";
+        condition = Condition.compile(repr, clist, err);
+        assertEquals(condition.type, c1);
+        assertEquals(condition.operator, Condition.OP_LE);
+        assertEquals(Math.round(condition.bound), 100);
+
+        repr = "eq 100";
+        condition = Condition.compile(repr, clist, err);
+        assertEquals(condition.type, c1);
+        assertEquals(condition.operator, Condition.OP_EQ);
+        assertEquals(Math.round(condition.bound), 100);
+    }
+
+    void testCheckSemantic(MulticostService service,
+                           MulticostRequest request) throws Exception {
+        Method method = MulticostService.class
+            .getDeclaredMethod("checkSemantic",
+                               MulticostRequest.class);
+
+        method.setAccessible(true);
+        try {
+            method.invoke(service, request);
+        } catch (InvocationTargetException e) {
+            throw (Exception) e.getCause();
+        }
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testMultipleCostTypes() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.costType = c1;
+        request.multicostTypes = clist;
+
+        MulticostService service = new MulticostService(clist, true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testExtraTestable() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.costType = c1;
+        request.multicostTypes = clist;
+
+        MulticostService service = new MulticostService(clist, true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testMissingCostType() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+
+        MulticostService service = new MulticostService(clist, true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testUnsupportedCostType() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = clist;
+        request.orConstraintsRepr = Arrays.asList(Arrays.asList("le 100"),
+                                                  Arrays.asList("[1] lt 20"));
+
+        MulticostService service = new MulticostService(Arrays.asList(c1), true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testExtraConstraints1() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = clist;
+        request.constraints = Arrays.asList("le 100");
+
+        MulticostService service = new MulticostService(clist, false);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testExtraConstraints2() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = clist;
+        request.orConstraintsRepr = Arrays.asList(Arrays.asList("le 100"),
+                                                  Arrays.asList("[1] lt 20"));
+
+        MulticostService service = new MulticostService(clist, false);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testMultipleConstraints() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = clist;
+        request.constraints = Arrays.asList("le 100");
+        request.orConstraintsRepr = Arrays.asList(Arrays.asList("le 100"),
+                                                  Arrays.asList("[1] lt 20"));
+
+        MulticostService service = new MulticostService(clist, true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testInvalidConstraints() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = clist;
+        request.constraints = Arrays.asList("what 100");
+
+        MulticostService service = new MulticostService(clist, true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test(expected=AltoBasicException.class)
+    public void testUntestableConstraints() throws Exception {
+        MulticostRequest request = new MulticostRequest();
+        request.multicostTypes = Arrays.asList(c1);
+        request.testableTypes = Arrays.asList(c2);
+        request.orConstraintsRepr = Arrays.asList(Arrays.asList("le 100"),
+                                                  Arrays.asList("[1] lt 20"));
+
+        MulticostService service = new MulticostService(Arrays.asList(c1), true);
+
+        testCheckSemantic(service, request);
+    }
+
+    @Test
+    public void testMulticostCheck() throws Exception {
+         MulticostRequest request = new MulticostRequest();
+         request.costType = c1;
+         request.constraints = Arrays.asList("[0] le 100", "[1] le 6");
+
+         ObjectMapper mapper = new ObjectMapper();
+
+         String repr = mapper.writeValueAsString(request);
+         MulticostService service = new MulticostService(clist, true);
+
+         String rep = service.accept(repr);
+    }
+
+    void testConditionFailure(String repr) {
+        Condition.compile(repr, clist, err);
+    }
+
+    @Test(expected=AltoErrorInvalidFieldValue.class)
+    public void test1Parameter() {
+        testConditionFailure("[0]");
+    }
+
+    @Test(expected=AltoErrorInvalidFieldValue.class)
+    public void test4Parameter() {
+        testConditionFailure("[0] lt 100 200");
+    }
+
+    @Test(expected=AltoErrorInvalidFieldValue.class)
+    public void testInvalidIndex() {
+        testConditionFailure("[2] ge 200");
+    }
+
+    @Test(expected=AltoErrorInvalidFieldValue.class)
+    public void testInvalidOp() {
+        testConditionFailure("[0] what 100");
+    }
+
+    @Test(expected=AltoErrorInvalidFieldValue.class)
+    public void testInvalidValue() {
+        testConditionFailure("[0] gt abc");
+    }
+}
index a4838d59ea024e68a79b72903df4317a93e10afa..f8c69ea4ade5bef3fe1d3909e905fee247550144 100644 (file)
@@ -37,17 +37,14 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
 
   <feature name='odl-alto-extension' version='${project.version}'
             description='OpenDaylight :: alto :: Extension'>
+    <feature version='${project.version}'>odl-alto-northbound</feature>
     <feature version='${project.version}'>odl-alto-nonstandard-types</feature>
     <feature version='${project.version}'>odl-alto-nonstandard-service-models</feature>
     <feature version='${project.version}'>odl-alto-nonstandard-northbound-route</feature>
     <feature version='${project.version}'>odl-alto-spce</feature>
-
-    <bundle>mvn:org.opendaylight.alto/multicost-api/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.alto/multicost-impl/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.alto/incremental-update-api/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.alto/incremental-update-impl/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.alto/rsade-api/${project.version}</bundle>
-    <bundle>mvn:org.opendaylight.alto/rsade-impl/${project.version}</bundle>
+    <feature version='${project.version}'>odl-alto-multicost</feature>
+    <feature version='${project.version}'>odl-alto-iupd</feature>
+    <feature version='${project.version}'>odl-alto-rsabs</feature>
   </feature>
 
   <feature name='odl-alto-spce' version='${project.version}'
@@ -207,17 +204,30 @@ and is available at http://www.eclipse.org/legal/epl-v10.html
 
   <feature name='odl-alto-multicost' version='${project.version}'
            description='OpenDaylight :: alto :: Mutlicost support'>
-    <!-- new feature in Carbon -->
+    <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
+    <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
+    <feature version='${project.version}'>odl-alto-northbound</feature>
+
+    <bundle>mvn:org.opendaylight.alto/multicost-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.alto/multicost-impl/${project.version}</bundle>
   </feature>
 
   <feature name='odl-alto-iupd' version='${project.version}'
            description='OpenDaylight :: alto :: Incremental update'>
-    <!-- new feature in Carbon -->
+    <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
+    <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
+
+    <bundle>mvn:org.opendaylight.alto/incremental-update-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.alto/incremental-update-impl/${project.version}</bundle>
   </feature>
 
   <feature name='odl-alto-rsabs' version='${project.version}'
            description='OpenDaylight :: alto :: Routing state abstraction'>
-    <!-- new feature in Carbon -->
+    <feature version='${mdsal.model.version}'>odl-mdsal-models</feature>
+    <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
+
+    <bundle>mvn:org.opendaylight.alto/rsade-api/${project.version}</bundle>
+    <bundle>mvn:org.opendaylight.alto/rsade-impl/${project.version}</bundle>
   </feature>
 
 </features>
index fa8ac5d23fab72a54d4849a61156e4fe24ddda96..9b5e95a050c71df5b9301280d0d9b114504a14bd 100644 (file)
@@ -25,6 +25,13 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
   <name>OpenDaylight :: alto :: Extension</name>
 
   <dependencies>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>odl-alto-northbound</artifactId>
+      <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
     <dependency>
       <groupId>${project.groupId}</groupId>
       <artifactId>odl-alto-nonstandard-types</artifactId>
@@ -54,34 +61,25 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
       <classifier>features</classifier>
     </dependency>
     <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>multicost-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>multicost-impl</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>incremental-update-api</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>incremental-update-impl</artifactId>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>odl-alto-multicost</artifactId>
       <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
     </dependency>
     <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>rsade-api</artifactId>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>odl-alto-iupd</artifactId>
       <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
     </dependency>
     <dependency>
-      <groupId>org.opendaylight.alto</groupId>
-      <artifactId>rsade-impl</artifactId>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>odl-alto-rsabs</artifactId>
       <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
     </dependency>
   </dependencies>
 </project>
index aec2b5fe5fcb8ca76205ac7266b305b45092e36c..2d965cc96865f9152377012346ad99bef44dbded 100644 (file)
@@ -24,7 +24,37 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
 
   <name>OpenDaylight :: alto :: Incremental update</name>
 
+  <properties>
+    <mdsal.model.groupId>org.opendaylight.mdsal.model</mdsal.model.groupId>
+    <mdsal.groupId>org.opendaylight.controller</mdsal.groupId>
+    <mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
+    <mdsal.version>1.5.0-SNAPSHOT</mdsal.version>
+  </properties>
+
   <dependencies>
-    <!-- new feature in Carbon -->
+    <dependency>
+      <groupId>${mdsal.model.groupId}</groupId>
+      <artifactId>odl-mdsal-models</artifactId>
+      <version>${mdsal.model.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>${mdsal.groupId}</groupId>
+      <artifactId>odl-mdsal-broker</artifactId>
+      <version>${mdsal.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>incremental-update-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>incremental-update-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
index 2823adb6831afb3fa88ed6460201ac5e12b2404a..5372aa4bd004b773f3ede500e6ab525bc958a983 100644 (file)
@@ -24,7 +24,44 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
 
   <name>OpenDaylight :: alto :: Mutlicost support</name>
 
+  <properties>
+    <mdsal.model.groupId>org.opendaylight.mdsal.model</mdsal.model.groupId>
+    <mdsal.groupId>org.opendaylight.controller</mdsal.groupId>
+    <mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
+    <mdsal.version>1.5.0-SNAPSHOT</mdsal.version>
+  </properties>
+
   <dependencies>
-    <!-- new feature in Carbon -->
+    <dependency>
+      <groupId>${mdsal.model.groupId}</groupId>
+      <artifactId>odl-mdsal-models</artifactId>
+      <version>${mdsal.model.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>${mdsal.groupId}</groupId>
+      <artifactId>odl-mdsal-broker</artifactId>
+      <version>${mdsal.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>odl-alto-northbound</artifactId>
+      <version>${project.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>multicost-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>multicost-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>
index 0bc81915ecfa402599f278fee8d13efcfcef0409..58b0624325d276d50048aee334f1b0658b8610ec 100644 (file)
@@ -24,7 +24,37 @@ and is available at http://www.eclipse.org/legal/epl-v10.html INTERNAL
 
   <name>OpenDaylight :: alto :: Routing state abstraction</name>
 
+  <properties>
+    <mdsal.model.groupId>org.opendaylight.mdsal.model</mdsal.model.groupId>
+    <mdsal.groupId>org.opendaylight.controller</mdsal.groupId>
+    <mdsal.model.version>0.10.0-SNAPSHOT</mdsal.model.version>
+    <mdsal.version>1.5.0-SNAPSHOT</mdsal.version>
+  </properties>
+
   <dependencies>
-    <!-- new feature in Carbon -->
+    <dependency>
+      <groupId>${mdsal.model.groupId}</groupId>
+      <artifactId>odl-mdsal-models</artifactId>
+      <version>${mdsal.model.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>${mdsal.groupId}</groupId>
+      <artifactId>odl-mdsal-broker</artifactId>
+      <version>${mdsal.version}</version>
+      <type>xml</type>
+      <classifier>features</classifier>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>rsade-api</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.opendaylight.alto</groupId>
+      <artifactId>rsade-impl</artifactId>
+      <version>${project.version}</version>
+    </dependency>
   </dependencies>
 </project>