Partially extract segmentrouting-api
Change-Id: Iaf2a0bb387a6e7024fa3d75b42cb4874c93a09bb
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/BlockedPortsConfig.java b/api/src/main/java/org/onosproject/segmentrouting/config/BlockedPortsConfig.java
new file mode 100644
index 0000000..a5c5557
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/BlockedPortsConfig.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Network config to describe ports that should be blocked until authenticated.
+ */
+public class BlockedPortsConfig extends Config<ApplicationId> {
+
+ /**
+ * Returns the top level keys to the config,
+ * which should be device ID strings.
+ *
+ * @return this list of top level keys
+ */
+ public List<String> deviceIds() {
+ List<String> devIds = new ArrayList<>();
+ if (object != null) {
+ Iterator<String> it = object.fieldNames();
+ if (it != null) {
+ it.forEachRemaining(devIds::add);
+ }
+ }
+ return devIds;
+ }
+
+ /**
+ * Returns the port range strings associated with the given device id key.
+ *
+ * @param deviceId the device id key
+ * @return the associated port range strings
+ */
+ public List<String> portRanges(String deviceId) {
+ List<String> portRanges = new ArrayList<>();
+ if (object != null) {
+ JsonNode jnode = object.get(deviceId);
+ if (ArrayNode.class.isInstance(jnode)) {
+ ArrayNode array = (ArrayNode) jnode;
+ array.forEach(pr -> portRanges.add(pr.asText()));
+ }
+ }
+ return portRanges;
+ }
+
+ /**
+ * Returns an iterator over the port numbers defined by the port ranges
+ * defined in the configuration, for the given device.
+ *
+ * @param deviceId the specific device
+ * @return an iterator over the configured ports
+ */
+ public Iterator<Long> portIterator(String deviceId) {
+ List<String> ranges = portRanges(deviceId);
+ return new PortIterator(ranges);
+ }
+
+ /**
+ * Private implementation of an iterator that aggregates several range
+ * iterators into a single iterator.
+ */
+ class PortIterator implements Iterator<Long> {
+ private final List<Range> ranges;
+ private final int nRanges;
+ private int currentRange = 0;
+ private Iterator<Long> iterator;
+
+ PortIterator(List<String> rangeSpecs) {
+ nRanges = rangeSpecs.size();
+ ranges = new ArrayList<>(nRanges);
+ if (nRanges > 0) {
+ for (String rs : rangeSpecs) {
+ ranges.add(new Range(rs));
+ }
+ iterator = ranges.get(0).iterator();
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return nRanges > 0 &&
+ (currentRange < nRanges - 1 ||
+ (currentRange < nRanges && iterator.hasNext()));
+ }
+
+ @Override
+ public Long next() {
+ if (nRanges == 0) {
+ throw new NoSuchElementException();
+ }
+
+ Long value;
+ if (iterator.hasNext()) {
+ value = iterator.next();
+ } else {
+ currentRange++;
+ if (currentRange < nRanges) {
+ iterator = ranges.get(currentRange).iterator();
+ value = iterator.next();
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+ return value;
+ }
+ }
+
+ /**
+ * Private implementation of a "range" of long numbers, defined by a
+ * string of the form {@code "<lo>-<hi>"}, for example, "17-32".
+ */
+ static final class Range {
+ private static final Pattern RE_SINGLE = Pattern.compile("(\\d+)");
+ private static final Pattern RE_RANGE = Pattern.compile("(\\d+)-(\\d+)");
+ private static final String E_BAD_FORMAT = "Bad Range Format ";
+
+ private final long lo;
+ private final long hi;
+
+ /**
+ * Constructs a range from the given string definition.
+ * For example:
+ * <pre>
+ * Range r = new Range("17-32");
+ * </pre>
+ *
+ * @param s the string representation of the range
+ * @throws IllegalArgumentException if the range string is malformed
+ */
+ Range(String s) {
+ String lohi = s;
+ Matcher m = RE_SINGLE.matcher(s);
+ if (m.matches()) {
+ lohi = s + "-" + s;
+ }
+ m = RE_RANGE.matcher(lohi);
+ if (!m.matches()) {
+ throw new IllegalArgumentException(E_BAD_FORMAT + s);
+ }
+ try {
+ lo = Long.parseLong(m.group(1));
+ hi = Long.parseLong(m.group(2));
+
+ if (hi < lo) {
+ throw new IllegalArgumentException(E_BAD_FORMAT + s);
+ }
+ } catch (NumberFormatException nfe) {
+ // unlikely to be thrown, since the matcher will have failed first
+ throw new IllegalArgumentException(E_BAD_FORMAT + s, nfe);
+ }
+ }
+
+
+ /**
+ * Returns an iterator over this range, starting from the lowest value
+ * and iterating up to the highest value (inclusive).
+ *
+ * @return an iterator over this range
+ */
+ Iterator<Long> iterator() {
+ return new RangeIterator();
+ }
+
+ /**
+ * Private implementation of an iterator over the range.
+ */
+ class RangeIterator implements Iterator<Long> {
+ long current;
+
+ RangeIterator() {
+ current = lo - 1;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return current < hi;
+ }
+
+ @Override
+ public Long next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return ++current;
+ }
+ }
+ }
+}
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/DeviceConfigNotFoundException.java b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceConfigNotFoundException.java
new file mode 100644
index 0000000..773c7f6
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceConfigNotFoundException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+/**
+ * Signals that an error occurred during reading device configuration.
+ */
+public class DeviceConfigNotFoundException extends Exception {
+
+ /**
+ * Creates a new ConfigNotFoundException with the given message.
+ *
+ * @param message exception message
+ */
+ public DeviceConfigNotFoundException(String message) {
+ super(message);
+ }
+}
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
new file mode 100644
index 0000000..2d0bbd2
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.segmentrouting.config;
+
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Mechanism through which group handler module retrieves
+ * the device specific attributes such as segment ID,
+ * Mac address...etc from group handler applications.
+ */
+public interface DeviceProperties {
+ /**
+ * Checks if the device is configured.
+ *
+ * @param deviceId device identifier
+ * @return true if the device is configured
+ */
+ boolean isConfigured(DeviceId deviceId);
+
+ /**
+ * Returns the IPv4 segment id of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return segment id of a device
+ */
+ int getIPv4SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Returns the IPv6 segment id of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return segment id of a device
+ */
+ int getIPv6SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Returns the Mac address of a device to be used in group creation.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return mac address of a device
+ */
+ MacAddress getDeviceMac(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return the pseudowire routing label for a leaf node
+ */
+ int getPWRoutingLabel(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Returns the router ipv4 address of a segment router.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return router ip address
+ */
+ IpAddress getRouterIpv4(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Returns the router ipv6 address of a segment router.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return router ip address
+ */
+ IpAddress getRouterIpv6(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Indicates whether a device is edge device or transit/core device.
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return boolean
+ */
+ boolean isEdgeDevice(DeviceId deviceId) throws DeviceConfigNotFoundException;
+
+ /**
+ * Returns all segment IDs to be considered in building auto
+ *
+ * created groups.
+ * @return list of segment IDs
+ */
+ List<Integer> getAllDeviceSegmentIds();
+
+ /**
+ * Returns subnet-to-ports mapping of given device.
+ *
+ * For each entry of the map
+ * Key: a subnet
+ * Value: a list of ports, which are bound to the subnet
+ *
+ * @param deviceId device identifier
+ * @throws DeviceConfigNotFoundException if the device configuration is not found
+ * @return a map that contains all subnet-to-ports mapping of given device
+ */
+ Map<IpPrefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId)
+ throws DeviceConfigNotFoundException;
+}
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
new file mode 100644
index 0000000..de6ffb9
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.google.common.collect.ImmutableSet;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.config.Config;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Set;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+/**
+ * App configuration object for Segment Routing.
+ */
+public class SegmentRoutingAppConfig extends Config<ApplicationId> {
+
+ private static Logger log = LoggerFactory.getLogger(SegmentRoutingAppConfig.class);
+
+ private static final String VROUTER_MACS = "vRouterMacs";
+ private static final String SUPPRESS_SUBNET = "suppressSubnet";
+ private static final String SUPPRESS_HOST_BY_PORT = "suppressHostByPort";
+ // TODO We might want to move SUPPRESS_HOST_BY_PROVIDER to Component Config
+ private static final String SUPPRESS_HOST_BY_PROVIDER = "suppressHostByProvider";
+ private static final String MPLS_ECMP = "MPLS-ECMP";
+ private static final String BLACKHOLE_IPS = "blackholeIps";
+
+ @Override
+ public boolean isValid() {
+ return hasOnlyFields(VROUTER_MACS, SUPPRESS_SUBNET,
+ SUPPRESS_HOST_BY_PORT, SUPPRESS_HOST_BY_PROVIDER, MPLS_ECMP, BLACKHOLE_IPS) &&
+ vRouterMacs() != null &&
+ suppressSubnet() != null && suppressHostByPort() != null &&
+ suppressHostByProvider() != null &&
+ blackholeIPs() != null;
+ }
+
+ /**
+ * Gets ips to blackhole from the config.
+ *
+ * @return Set of ips to blackhole, empty is not specified,
+ * or null if not valid
+ */
+ public Set<IpPrefix> blackholeIPs() {
+ if (!object.has(BLACKHOLE_IPS)) {
+ return ImmutableSet.of();
+ }
+
+ ImmutableSet.Builder<IpPrefix> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(BLACKHOLE_IPS);
+ for (JsonNode jsonNode : arrayNode) {
+ IpPrefix address;
+
+ String addrStr = jsonNode.asText(null);
+ if (addrStr != null) {
+ try {
+ address = IpPrefix.valueOf(addrStr);
+ builder.add(address);
+ } catch (IllegalArgumentException e) {
+ log.debug("Not adding {}", jsonNode, e);
+ }
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets ips to blackhole to the config.
+ *
+ * @param blackholeIps a set of ips to blackhole
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setBalckholeIps(Set<IpPrefix> blackholeIps) {
+ if (blackholeIps == null) {
+ object.remove(BLACKHOLE_IPS);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+
+ blackholeIps.forEach(ip -> {
+ arrayNode.add(ip.toString());
+ });
+
+ object.set(BLACKHOLE_IPS, arrayNode);
+ }
+ return this;
+ }
+
+ /**
+ * Gets MPLS-ECMP configuration from the config.
+ *
+ * @return the configuration of MPLS-ECMP. If it is not
+ * specified, the default behavior is false.
+ */
+ public boolean mplsEcmp() {
+ return get(MPLS_ECMP, false);
+ }
+
+ /**
+ * Sets MPLS-ECMP to the config.
+ *
+ * @param mplsEcmp the MPLS-ECMP configuration
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setMplsEcmp(boolean mplsEcmp) {
+ object.put(MPLS_ECMP, mplsEcmp);
+ return this;
+ }
+
+ /**
+ * Gets vRouters from the config.
+ *
+ * @return Set of vRouter MAC addresses, empty is not specified,
+ * or null if not valid
+ */
+ public Set<MacAddress> vRouterMacs() {
+ if (!object.has(VROUTER_MACS)) {
+ return ImmutableSet.of();
+ }
+
+ ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(VROUTER_MACS);
+ for (JsonNode jsonNode : arrayNode) {
+ MacAddress mac;
+
+ String macStr = jsonNode.asText(null);
+ if (macStr == null) {
+ return null;
+ }
+ try {
+ mac = MacAddress.valueOf(macStr);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+
+ builder.add(mac);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets vRouters to the config.
+ *
+ * @param vRouterMacs a set of vRouter MAC addresses
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setVRouterMacs(Set<MacAddress> vRouterMacs) {
+ if (vRouterMacs == null) {
+ object.remove(VROUTER_MACS);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+
+ vRouterMacs.forEach(mac -> {
+ arrayNode.add(mac.toString());
+ });
+
+ object.set(VROUTER_MACS, arrayNode);
+ }
+ return this;
+ }
+
+ /**
+ * Gets names of ports to which SegmentRouting does not push subnet rules.
+ *
+ * @return Set of port names, empty if not specified, or null
+ * if not valid
+ */
+ public Set<ConnectPoint> suppressSubnet() {
+ if (!object.has(SUPPRESS_SUBNET)) {
+ return ImmutableSet.of();
+ }
+
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(SUPPRESS_SUBNET);
+ for (JsonNode jsonNode : arrayNode) {
+ String portName = jsonNode.asText(null);
+ if (portName == null) {
+ return null;
+ }
+ try {
+ builder.add(ConnectPoint.deviceConnectPoint(portName));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets names of ports to which SegmentRouting does not push subnet rules.
+ *
+ * @param suppressSubnet names of ports to which SegmentRouting does not push
+ * subnet rules
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setSuppressSubnet(Set<ConnectPoint> suppressSubnet) {
+ if (suppressSubnet == null) {
+ object.remove(SUPPRESS_SUBNET);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+ suppressSubnet.forEach(connectPoint -> {
+ arrayNode.add(connectPoint.deviceId() + "/" + connectPoint.port());
+ });
+ object.set(SUPPRESS_SUBNET, arrayNode);
+ }
+ return this;
+ }
+
+ /**
+ * Gets connect points to which SegmentRouting does not push host rules.
+ *
+ * @return Set of connect points, empty if not specified, or null
+ * if not valid
+ */
+ public Set<ConnectPoint> suppressHostByPort() {
+ if (!object.has(SUPPRESS_HOST_BY_PORT)) {
+ return ImmutableSet.of();
+ }
+
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(SUPPRESS_HOST_BY_PORT);
+ for (JsonNode jsonNode : arrayNode) {
+ String portName = jsonNode.asText(null);
+ if (portName == null) {
+ return null;
+ }
+ try {
+ builder.add(ConnectPoint.deviceConnectPoint(portName));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets connect points to which SegmentRouting does not push host rules.
+ *
+ * @param connectPoints connect points to which SegmentRouting does not push
+ * host rules
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setSuppressHostByPort(Set<ConnectPoint> connectPoints) {
+ if (connectPoints == null) {
+ object.remove(SUPPRESS_HOST_BY_PORT);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+ connectPoints.forEach(connectPoint -> {
+ arrayNode.add(connectPoint.deviceId() + "/" + connectPoint.port());
+ });
+ object.set(SUPPRESS_HOST_BY_PORT, arrayNode);
+ }
+ return this;
+ }
+
+ /**
+ * Gets provider names from which SegmentRouting does not learn host info.
+ *
+ * @return array of provider names that need to be ignored
+ */
+ public Set<String> suppressHostByProvider() {
+ if (!object.has(SUPPRESS_HOST_BY_PROVIDER)) {
+ return ImmutableSet.of();
+ }
+
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ ArrayNode arrayNode = (ArrayNode) object.path(SUPPRESS_HOST_BY_PROVIDER);
+ for (JsonNode jsonNode : arrayNode) {
+ String providerName = jsonNode.asText(null);
+ if (providerName == null) {
+ return null;
+ }
+ builder.add(providerName);
+ }
+ return builder.build();
+ }
+
+ /**
+ * Sets provider names from which SegmentRouting does not learn host info.
+ *
+ * @param providers set of provider names
+ * @return this {@link SegmentRoutingAppConfig}
+ */
+ public SegmentRoutingAppConfig setSuppressHostByProvider(Set<String> providers) {
+ if (providers == null) {
+ object.remove(SUPPRESS_HOST_BY_PROVIDER);
+ } else {
+ ArrayNode arrayNode = mapper.createArrayNode();
+ providers.forEach(arrayNode::add);
+ object.set(SUPPRESS_HOST_BY_PROVIDER, arrayNode);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("vRouterMacs", vRouterMacs())
+ .add("suppressSubnet", suppressSubnet())
+ .add("suppressHostByPort", suppressHostByPort())
+ .add("suppressHostByProvider", suppressHostByProvider())
+ .add("mplsEcmp", mplsEcmp())
+ .add("blackholeIps", blackholeIPs())
+ .toString();
+ }
+}
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java b/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
new file mode 100644
index 0000000..7959df6
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfig.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.collect.ImmutableMap;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.Config;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * Configuration object for Segment Routing Application.
+ */
+public class SegmentRoutingDeviceConfig extends Config<DeviceId> {
+ private static final String NAME = "name";
+ private static final String IP4 = "ipv4Loopback";
+ private static final String IP6 = "ipv6Loopback";
+ private static final String MAC = "routerMac";
+ private static final String IP4_SID = "ipv4NodeSid";
+ private static final String IP6_SID = "ipv6NodeSid";
+ private static final String EDGE = "isEdgeRouter";
+ /**
+ * Adjancency SIDs config.
+ *
+ * @deprecated in Loon (1.11). We are not using and do not plan to use it.
+ */
+ @Deprecated
+ private static final String ADJSIDS = "adjacencySids";
+
+ /**
+ * Adjancency SID config.
+ *
+ * @deprecated in Loon (1.11). We are not using and do not plan to use it.
+ */
+ @Deprecated
+ private static final String ADJSID = "adjSid";
+
+ /**
+ * Adjancency port config.
+ *
+ * @deprecated in Loon (1.11). We are not using and do not plan to use it.
+ */
+ @Deprecated
+ private static final String PORTS = "ports";
+
+ private static final String PAIR_DEVICE_ID = "pairDeviceId";
+ private static final String PAIR_LOCAL_PORT = "pairLocalPort";
+
+ @Override
+ public boolean isValid() {
+ return hasOnlyFields(NAME, IP4, IP6, MAC, IP4_SID, IP6_SID, EDGE, ADJSIDS, ADJSID, PORTS,
+ PAIR_DEVICE_ID, PAIR_LOCAL_PORT) &&
+ name() != null &&
+ routerIpv4() != null && (!hasField(IP6) || routerIpv6() != null) &&
+ routerMac() != null &&
+ nodeSidIPv4() != -1 && (!hasField(IP6_SID) || nodeSidIPv6() != -1) &&
+ isEdgeRouter() != null &&
+ adjacencySids() != null &&
+ // pairDeviceId and pairLocalPort must be both configured or both omitted
+ (hasField(PAIR_DEVICE_ID) == hasField(PAIR_LOCAL_PORT)) &&
+ (!hasField(PAIR_DEVICE_ID) || pairDeviceId() != null) &&
+ (!hasField(PAIR_LOCAL_PORT) || pairLocalPort() != null);
+ }
+
+ /**
+ * Gets the name of the router.
+ *
+ * @return Optional name of the router. May be empty if not configured.
+ */
+ public Optional<String> name() {
+ String name = get(NAME, null);
+ return name != null ? Optional.of(name) : Optional.empty();
+ }
+
+ /**
+ * Sets the name of the router.
+ *
+ * @param name name of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setName(String name) {
+ return (SegmentRoutingDeviceConfig) setOrClear(NAME, name);
+ }
+
+ /**
+ * Gets the IPv4 address of the router.
+ *
+ * @return IP address of the router. Or null if not configured.
+ */
+ public Ip4Address routerIpv4() {
+ String ip = get(IP4, null);
+ return ip != null ? Ip4Address.valueOf(ip) : null;
+ }
+
+ /**
+ * Gets the IPv6 address of the router.
+ *
+ * @return IP address of the router. Or null if not configured.
+ */
+ public Ip6Address routerIpv6() {
+ String ip = get(IP6, null);
+ return ip != null ? Ip6Address.valueOf(ip) : null;
+ }
+
+ /**
+ * Sets the IPv4 address of the router.
+ *
+ * @param ip IPv4 address of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setRouterIpv4(String ip) {
+ return (SegmentRoutingDeviceConfig) setOrClear(IP4, ip);
+ }
+
+ /**
+ * Sets the IPv6 address of the router.
+ *
+ * @param ip IPv6 address of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setRouterIpv6(String ip) {
+ return (SegmentRoutingDeviceConfig) setOrClear(IP6, ip);
+ }
+
+ /**
+ * Gets the MAC address of the router.
+ *
+ * @return MAC address of the router. Or null if not configured.
+ */
+ public MacAddress routerMac() {
+ String mac = get(MAC, null);
+ return mac != null ? MacAddress.valueOf(mac) : null;
+ }
+
+ /**
+ * Sets the MAC address of the router.
+ *
+ * @param mac MAC address of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setRouterMac(String mac) {
+ return (SegmentRoutingDeviceConfig) setOrClear(MAC, mac);
+ }
+
+ /**
+ * Gets the IPv4 node SID of the router.
+ *
+ * @return node SID of the router. Or -1 if not configured.
+ */
+ public int nodeSidIPv4() {
+ return get(IP4_SID, -1);
+ }
+
+ /**
+ * Gets the IPv6 node SID of the router.
+ *
+ * @return node SID of the router. Or -1 if not configured.
+ */
+ public int nodeSidIPv6() {
+ return get(IP6_SID, -1);
+ }
+
+ /**
+ * Sets the node IPv4 node SID of the router.
+ *
+ * @param sid node SID of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setNodeSidIPv4(int sid) {
+ return (SegmentRoutingDeviceConfig) setOrClear(IP4_SID, sid);
+ }
+
+ /**
+ * Sets the node IPv6 node SID of the router.
+ *
+ * @param sid node SID of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setNodeSidIPv6(int sid) {
+ return (SegmentRoutingDeviceConfig) setOrClear(IP6_SID, sid);
+ }
+
+ /**
+ * Checks if the router is an edge router.
+ *
+ * @return true if the router is an edge router.
+ * false if the router is not an edge router.
+ * null if the value is not configured.
+ */
+ public Boolean isEdgeRouter() {
+ String isEdgeRouter = get(EDGE, null);
+ return isEdgeRouter != null ?
+ Boolean.valueOf(isEdgeRouter) :
+ null;
+ }
+
+ /**
+ * Specifies if the router is an edge router.
+ *
+ * @param isEdgeRouter true if the router is an edge router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setIsEdgeRouter(boolean isEdgeRouter) {
+ return (SegmentRoutingDeviceConfig) setOrClear(EDGE, isEdgeRouter);
+ }
+
+ /**
+ * Gets the adjacency SIDs of the router.
+ *
+ * @return adjacency SIDs of the router. Or null if not configured.
+ */
+ public Map<Integer, Set<Integer>> adjacencySids() {
+ if (!object.has(ADJSIDS)) {
+ return null;
+ }
+
+ Map<Integer, Set<Integer>> adjacencySids = new HashMap<>();
+ ArrayNode adjacencySidsNode = (ArrayNode) object.path(ADJSIDS);
+ for (JsonNode adjacencySidNode : adjacencySidsNode) {
+ int asid = adjacencySidNode.path(ADJSID).asInt(-1);
+ if (asid == -1) {
+ return null;
+ }
+
+ HashSet<Integer> ports = new HashSet<>();
+ ArrayNode portsNode = (ArrayNode) adjacencySidNode.path(PORTS);
+ for (JsonNode portNode : portsNode) {
+ int port = portNode.asInt(-1);
+ if (port == -1) {
+ return null;
+ }
+ ports.add(port);
+ }
+ adjacencySids.put(asid, ports);
+ }
+
+ return ImmutableMap.copyOf(adjacencySids);
+ }
+
+ /**
+ * Sets the adjacency SIDs of the router.
+ *
+ * @param adjacencySids adjacency SIDs of the router.
+ * @return the config of the router.
+ */
+ public SegmentRoutingDeviceConfig setAdjacencySids(Map<Integer, Set<Integer>> adjacencySids) {
+ if (adjacencySids == null) {
+ object.remove(ADJSIDS);
+ } else {
+ ArrayNode adjacencySidsNode = mapper.createArrayNode();
+
+ adjacencySids.forEach((sid, ports) -> {
+ ObjectNode adjacencySidNode = mapper.createObjectNode();
+
+ adjacencySidNode.put(ADJSID, sid);
+
+ ArrayNode portsNode = mapper.createArrayNode();
+ ports.forEach(port -> {
+ portsNode.add(port.toString());
+ });
+ adjacencySidNode.set(PORTS, portsNode);
+
+ adjacencySidsNode.add(adjacencySidNode);
+ });
+
+ object.set(ADJSIDS, adjacencySidsNode);
+ }
+
+ return this;
+ }
+
+ /**
+ * Gets the pair device id.
+ *
+ * @return pair device id; Or null if not configured.
+ */
+ public DeviceId pairDeviceId() {
+ String pairDeviceId = get(PAIR_DEVICE_ID, null);
+ return pairDeviceId != null ? DeviceId.deviceId(pairDeviceId) : null;
+ }
+
+ /**
+ * Sets the pair device id.
+ *
+ * @param deviceId pair device id
+ * @return this configuration
+ */
+ public SegmentRoutingDeviceConfig setPairDeviceId(DeviceId deviceId) {
+ return (SegmentRoutingDeviceConfig) setOrClear(PAIR_DEVICE_ID, deviceId.toString());
+ }
+
+ /**
+ * Gets the pair local port.
+ *
+ * @return pair local port; Or null if not configured.
+ */
+ public PortNumber pairLocalPort() {
+ long pairLocalPort = get(PAIR_LOCAL_PORT, -1L);
+ return pairLocalPort != -1L ? PortNumber.portNumber(pairLocalPort) : null;
+ }
+
+ /**
+ * Sets the pair local port.
+ *
+ * @param portNumber pair local port
+ * @return this configuration
+ */
+ public SegmentRoutingDeviceConfig setPairLocalPort(PortNumber portNumber) {
+ return (SegmentRoutingDeviceConfig) setOrClear(PAIR_LOCAL_PORT, portNumber.toLong());
+ }
+}
diff --git a/api/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/api/src/main/java/org/onosproject/segmentrouting/config/package-info.java
new file mode 100644
index 0000000..a664a8f
--- /dev/null
+++ b/api/src/main/java/org/onosproject/segmentrouting/config/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Segment routing network configuration mechanism.
+ */
+package org.onosproject.segmentrouting.config;
\ No newline at end of file
diff --git a/api/src/test/java/org/onosproject/segmentrouting/config/BlockedPortsConfigTest.java b/api/src/test/java/org/onosproject/segmentrouting/config/BlockedPortsConfigTest.java
new file mode 100644
index 0000000..e457896
--- /dev/null
+++ b/api/src/test/java/org/onosproject/segmentrouting/config/BlockedPortsConfigTest.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright 2017-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.DefaultApplicationId;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+/**
+ * Unit tests for {@link BlockedPortsConfig}.
+ */
+public class BlockedPortsConfigTest {
+
+ private static final ApplicationId APP_ID = new DefaultApplicationId(1, "foo");
+ private static final String KEY = "blocked";
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ private static final String DEV1 = "of:0000000000000001";
+ private static final String DEV2 = "of:0000000000000002";
+ private static final String DEV3 = "of:0000000000000003";
+ private static final String DEV4 = "of:0000000000000004";
+ private static final String RANGE_14 = "1-4";
+ private static final String RANGE_79 = "7-9";
+ private static final String P1 = "1";
+ private static final String P5 = "5";
+ private static final String P9 = "9";
+
+ private BlockedPortsConfig cfg;
+ private BlockedPortsConfig.Range range;
+
+ private void print(String s) {
+ System.out.println(s);
+ }
+
+ private void print(Object o) {
+ print(o.toString());
+ }
+
+ @Before
+ public void setUp() throws IOException {
+ InputStream blockedPortsJson = BlockedPortsConfigTest.class
+ .getResourceAsStream("/blocked-ports.json");
+ JsonNode node = MAPPER.readTree(blockedPortsJson);
+ cfg = new BlockedPortsConfig();
+ cfg.init(APP_ID, KEY, node, MAPPER, null);
+ }
+
+ @Test
+ public void basic() {
+ cfg = new BlockedPortsConfig();
+ print(cfg);
+
+ assertEquals("non-empty devices list", 0, cfg.deviceIds().size());
+ assertEquals("non-empty port-ranges list", 0, cfg.portRanges("non-exist").size());
+ }
+
+
+ @Test
+ public void overIteratePort() {
+ Iterator<Long> iterator = cfg.portIterator(DEV3);
+ while (iterator.hasNext()) {
+ print(iterator.next());
+ }
+
+ try {
+ print(iterator.next());
+ fail("NoSuchElement exception NOT thrown");
+ } catch (NoSuchElementException e) {
+ print("<good> " + e);
+ }
+ }
+
+ @Test
+ public void overIterateRange() {
+ range = new BlockedPortsConfig.Range("4-6");
+
+ Iterator<Long> iterator = range.iterator();
+ while (iterator.hasNext()) {
+ print(iterator.next());
+ }
+
+ try {
+ print(iterator.next());
+ fail("NoSuchElement exception NOT thrown");
+ } catch (NoSuchElementException e) {
+ print("<good> " + e);
+ }
+ }
+
+
+ @Test
+ public void simple() {
+ List<String> devIds = cfg.deviceIds();
+ print(devIds);
+ assertEquals("wrong dev id count", 3, devIds.size());
+ assertEquals("missing dev 1", true, devIds.contains(DEV1));
+ assertEquals("dev 2??", false, devIds.contains(DEV2));
+ assertEquals("missing dev 3", true, devIds.contains(DEV3));
+
+ List<String> d1ranges = cfg.portRanges(DEV1);
+ print(d1ranges);
+ assertEquals("wrong d1 range count", 2, d1ranges.size());
+ assertEquals("missing 1-4", true, d1ranges.contains(RANGE_14));
+ assertEquals("missing 7-9", true, d1ranges.contains(RANGE_79));
+
+ List<String> d2ranges = cfg.portRanges(DEV2);
+ print(d2ranges);
+ assertEquals("wrong d2 range count", 0, d2ranges.size());
+
+ List<String> d3ranges = cfg.portRanges(DEV3);
+ print(d3ranges);
+ assertEquals("wrong d3 range count", 1, d3ranges.size());
+ assertEquals("range 1-4?", false, d3ranges.contains(RANGE_14));
+ assertEquals("missing 7-9", true, d3ranges.contains(RANGE_79));
+ }
+
+
+ private void verifyPorts(List<Long> ports, long... exp) {
+ assertEquals("Wrong port count", exp.length, ports.size());
+ for (long e : exp) {
+ assertEquals("missing port", true, ports.contains(e));
+ }
+ }
+
+ private void verifyPortIterator(String devid, long... exp) {
+ List<Long> ports = new ArrayList<>();
+ Iterator<Long> iter = cfg.portIterator(devid);
+ iter.forEachRemaining(ports::add);
+ print(ports);
+ verifyPorts(ports, exp);
+ }
+
+ @Test
+ public void rangeIterators() {
+ verifyPortIterator(DEV1, 1, 2, 3, 4, 7, 8, 9);
+ verifyPortIterator(DEV2);
+ verifyPortIterator(DEV3, 7, 8, 9);
+ }
+
+ @Test
+ public void singlePorts() {
+ List<String> devIds = cfg.deviceIds();
+ print(devIds);
+ assertEquals("wrong dev id count", 3, devIds.size());
+ assertEquals("missing dev 4", true, devIds.contains(DEV4));
+
+ List<String> d1ranges = cfg.portRanges(DEV4);
+ print(d1ranges);
+ assertEquals("wrong d4 range count", 3, d1ranges.size());
+ assertEquals("missing 1", true, d1ranges.contains(P1));
+ assertEquals("missing 5", true, d1ranges.contains(P5));
+ assertEquals("missing 9", true, d1ranges.contains(P9));
+
+ verifyPortIterator(DEV4, 1, 5, 9);
+ }
+
+
+ // test Range inner class
+
+ @Test
+ public void rangeBadFormat() {
+ try {
+ range = new BlockedPortsConfig.Range("not-a-range-format");
+ fail("no exception thrown");
+ } catch (IllegalArgumentException iar) {
+ print(iar);
+ assertEquals("wrong msg", "Bad Range Format not-a-range-format", iar.getMessage());
+ }
+ }
+
+ @Test
+ public void rangeBadHi() {
+ try {
+ range = new BlockedPortsConfig.Range("2-nine");
+ fail("no exception thrown");
+ } catch (IllegalArgumentException iar) {
+ print(iar);
+ assertEquals("wrong msg", "Bad Range Format 2-nine", iar.getMessage());
+ }
+ }
+
+ @Test
+ public void rangeHiLessThanLo() {
+ try {
+ range = new BlockedPortsConfig.Range("9-5");
+ fail("no exception thrown");
+ } catch (IllegalArgumentException iar) {
+ print(iar);
+ assertEquals("wrong msg", "Bad Range Format 9-5", iar.getMessage());
+ }
+ }
+
+ @Test
+ public void rangeNegative() {
+ try {
+ range = new BlockedPortsConfig.Range("-2-4");
+ fail("no exception thrown");
+ } catch (IllegalArgumentException iar) {
+ print(iar);
+ assertEquals("wrong msg", "Bad Range Format -2-4", iar.getMessage());
+ }
+ }
+
+ @Test
+ public void rangeGood() {
+ range = new BlockedPortsConfig.Range("100-104");
+ List<Long> values = new ArrayList<>();
+ range.iterator().forEachRemaining(values::add);
+ print(values);
+ verifyPorts(values, 100, 101, 102, 103, 104);
+ }
+}
diff --git a/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java b/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
new file mode 100644
index 0000000..fa667c0
--- /dev/null
+++ b/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableSet;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.MacAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.TestApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+
+import java.io.InputStream;
+import java.util.Set;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.*;
+
+/**
+ * Tests for class {@link SegmentRoutingAppConfig}.
+ */
+public class SegmentRoutingAppConfigTest {
+ private SegmentRoutingAppConfig config;
+ private SegmentRoutingAppConfig invalidConfig;
+ private SegmentRoutingAppConfig mplsEcmpConfig;
+
+ private static final String APP_NAME = "org.onosproject.segmentrouting";
+ private static final MacAddress ROUTER_MAC_1 = MacAddress.valueOf("00:00:00:00:00:01");
+ private static final MacAddress ROUTER_MAC_2 = MacAddress.valueOf("00:00:00:00:00:02");
+ private static final MacAddress ROUTER_MAC_3 = MacAddress.valueOf("00:00:00:00:00:03");
+ private static final ConnectPoint PORT_1 = ConnectPoint.deviceConnectPoint("of:1/1");
+ private static final ConnectPoint PORT_2 = ConnectPoint.deviceConnectPoint("of:1/2");
+ private static final ConnectPoint PORT_3 = ConnectPoint.deviceConnectPoint("of:1/3");
+ private static final DeviceId VROUTER_ID_1 = DeviceId.deviceId("of:1");
+ private static final DeviceId VROUTER_ID_2 = DeviceId.deviceId("of:2");
+ private static final String PROVIDER_1 = "org.onosproject.provider.host";
+ private static final String PROVIDER_2 = "org.onosproject.netcfghost";
+ private static final String PROVIDER_3 = "org.onosproject.anotherprovider";
+ private static final IpPrefix BLACKHOLE_IP = IpPrefix.valueOf("10.0.0.0/8");
+ private static final IpPrefix BLACKHOLE_IP_2 = IpPrefix.valueOf("20.0.0.0/8");
+
+ /**
+ * Initialize test related variables.
+ *
+ * @throws Exception
+ */
+ @Before
+ public void setUp() throws Exception {
+ InputStream jsonStream = SegmentRoutingAppConfigTest.class
+ .getResourceAsStream("/app.json");
+ InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class
+ .getResourceAsStream("/app-invalid.json");
+ InputStream mplsEcmpJsonStream = SegmentRoutingAppConfigTest.class
+ .getResourceAsStream("/app-ecmp.json");
+
+ String key = APP_NAME;
+ ApplicationId subject = new TestApplicationId(key);
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(jsonStream);
+ JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
+ JsonNode mplsEcmpJsonNode = mapper.readTree(mplsEcmpJsonStream);
+ ConfigApplyDelegate delegate = new MockDelegate();
+
+ config = new SegmentRoutingAppConfig();
+ config.init(subject, key, jsonNode, mapper, delegate);
+ invalidConfig = new SegmentRoutingAppConfig();
+ invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
+ mplsEcmpConfig = new SegmentRoutingAppConfig();
+ mplsEcmpConfig.init(subject, key, mplsEcmpJsonNode, mapper, delegate);
+ }
+
+ /**
+ * Tests config validity.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testIsValid() throws Exception {
+ assertTrue(config.isValid());
+ assertFalse(invalidConfig.isValid());
+ assertTrue(mplsEcmpConfig.isValid());
+ }
+
+ /**
+ * Test MPLS-ECMP default getter. By-default
+ * MPLS-ECMPS is false.
+ */
+ @Test
+ public void testDefaultMplsEcmp() {
+ boolean mplsEcmp = config.mplsEcmp();
+ assertThat(mplsEcmp, is(false));
+ }
+
+ /**
+ * Test MPLS-ECMP getter.
+ */
+ @Test
+ public void testMplsEcmp() {
+ boolean mplsEcmp = mplsEcmpConfig.mplsEcmp();
+ assertThat(mplsEcmp, is(true));
+ }
+
+ /**
+ * Test MPLS-ECMP setter.
+ */
+ @Test
+ public void testSetMplsEcmp() {
+ /*
+ * In the config the value is not set.
+ */
+ boolean mplsEcmp = config.mplsEcmp();
+ assertThat(mplsEcmp, is(false));
+ config.setMplsEcmp(true);
+ mplsEcmp = config.mplsEcmp();
+ assertThat(mplsEcmp, is(true));
+ /*
+ * In the mplsEcmpConfig the value is true,
+ */
+ mplsEcmp = mplsEcmpConfig.mplsEcmp();
+ assertThat(mplsEcmp, is(true));
+ config.setMplsEcmp(false);
+ mplsEcmp = config.mplsEcmp();
+ assertThat(mplsEcmp, is(false));
+ }
+
+ /**
+ * Tests vRouterMacs getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testVRouterMacs() throws Exception {
+ Set<MacAddress> vRouterMacs = config.vRouterMacs();
+ assertNotNull("vRouterMacs should not be null", vRouterMacs);
+ assertThat(vRouterMacs.size(), is(2));
+ assertTrue(vRouterMacs.contains(ROUTER_MAC_1));
+ assertTrue(vRouterMacs.contains(ROUTER_MAC_2));
+ }
+
+ /**
+ * Tests vRouterMacs setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetVRouterMacs() throws Exception {
+ ImmutableSet.Builder<MacAddress> builder = ImmutableSet.builder();
+ builder.add(ROUTER_MAC_3);
+ config.setVRouterMacs(builder.build());
+
+ Set<MacAddress> vRouterMacs = config.vRouterMacs();
+ assertThat(vRouterMacs.size(), is(1));
+ assertTrue(vRouterMacs.contains(ROUTER_MAC_3));
+ }
+
+ /**
+ * Tests suppressSubnet getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSuppressSubnet() throws Exception {
+ Set<ConnectPoint> suppressSubnet = config.suppressSubnet();
+ assertNotNull("suppressSubnet should not be null", suppressSubnet);
+ assertThat(suppressSubnet.size(), is(2));
+ assertTrue(suppressSubnet.contains(PORT_1));
+ assertTrue(suppressSubnet.contains(PORT_2));
+ }
+
+ /**
+ * Tests suppressSubnet setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetSuppressSubnet() throws Exception {
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ builder.add(PORT_3);
+ config.setSuppressSubnet(builder.build());
+
+ Set<ConnectPoint> suppressSubnet = config.suppressSubnet();
+ assertNotNull("suppressSubnet should not be null", suppressSubnet);
+ assertThat(suppressSubnet.size(), is(1));
+ assertTrue(suppressSubnet.contains(PORT_3));
+ }
+
+ /**
+ * Tests suppressHostByPort getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSuppressHostByPort() throws Exception {
+ Set<ConnectPoint> suppressHostByPort = config.suppressHostByPort();
+ assertNotNull("suppressHostByPort should not be null", suppressHostByPort);
+ assertThat(suppressHostByPort.size(), is(2));
+ assertTrue(suppressHostByPort.contains(PORT_1));
+ assertTrue(suppressHostByPort.contains(PORT_2));
+ }
+
+ /**
+ * Tests suppressHostByPort setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetSuppressHostByPort() throws Exception {
+ ImmutableSet.Builder<ConnectPoint> builder = ImmutableSet.builder();
+ builder.add(PORT_3);
+ config.setSuppressHostByPort(builder.build());
+
+ Set<ConnectPoint> suppressHostByPort = config.suppressHostByPort();
+ assertNotNull("suppressHostByPort should not be null", suppressHostByPort);
+ assertThat(suppressHostByPort.size(), is(1));
+ assertTrue(suppressHostByPort.contains(PORT_3));
+ }
+
+ /**
+ * Tests suppressHostByProvider getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSuppressHostByProvider() throws Exception {
+ Set<String> supprsuppressHostByProvider = config.suppressHostByProvider();
+ assertNotNull("suppressHostByProvider should not be null", supprsuppressHostByProvider);
+ assertThat(supprsuppressHostByProvider.size(), is(2));
+ assertTrue(supprsuppressHostByProvider.contains(PROVIDER_1));
+ assertTrue(supprsuppressHostByProvider.contains(PROVIDER_2));
+ }
+
+ /**
+ * Tests suppressHostByProvider setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetHostLearning() throws Exception {
+ ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+ builder.add(PROVIDER_3);
+ config.setSuppressHostByProvider(builder.build());
+
+ Set<String> supprsuppressHostByProvider = config.suppressHostByProvider();
+ assertNotNull("suppressHostByProvider should not be null", supprsuppressHostByProvider);
+ assertThat(supprsuppressHostByProvider.size(), is(1));
+ assertTrue(supprsuppressHostByProvider.contains(PROVIDER_3));
+ }
+
+ /**
+ * Tests BlackHoleIps getter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testBlackHoleIps() throws Exception {
+ Set<IpPrefix> blackHoleIps = config.blackholeIPs();
+ assertNotNull("BlackHoleIps should not be null", blackHoleIps);
+ assertThat(blackHoleIps.size(), is(1));
+ assertTrue(blackHoleIps.contains(BLACKHOLE_IP));
+ }
+
+ /**
+ * Tests BlackHoleIps setter.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testSetBlackHoleIps() throws Exception {
+
+ config.setBalckholeIps(ImmutableSet.of(BLACKHOLE_IP_2));
+
+ Set<IpPrefix> blackHoleIps = config.blackholeIPs();
+ assertThat(blackHoleIps.size(), is(1));
+ assertTrue(blackHoleIps.contains(BLACKHOLE_IP_2));
+ }
+
+ private class MockDelegate implements ConfigApplyDelegate {
+ @Override
+ public void onApply(Config config) {
+ }
+ }
+}
diff --git a/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java b/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java
new file mode 100644
index 0000000..ddf90bb
--- /dev/null
+++ b/api/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingDeviceConfigTest.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2016-present Open Networking Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.segmentrouting.config;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.ConfigApplyDelegate;
+
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for class {@link SegmentRoutingDeviceConfig}.
+ */
+public class SegmentRoutingDeviceConfigTest {
+ private SegmentRoutingDeviceConfig config;
+ private SegmentRoutingDeviceConfig ipv6Config;
+ private SegmentRoutingDeviceConfig pairConfig;
+ private SegmentRoutingDeviceConfig invalidConfig;
+ private Map<Integer, Set<Integer>> adjacencySids1;
+ private Map<Integer, Set<Integer>> adjacencySids2;
+ private static final DeviceId PAIR_DEVICE_ID = DeviceId.deviceId("of:123456789ABCDEF0");
+ private static final PortNumber PAIR_LOCAL_PORT = PortNumber.portNumber(10);
+
+ @Before
+ public void setUp() throws Exception {
+ InputStream jsonStream = SegmentRoutingDeviceConfigTest.class
+ .getResourceAsStream("/device.json");
+ InputStream ipv6JsonStream = SegmentRoutingDeviceConfigTest.class
+ .getResourceAsStream("/device-ipv6.json");
+ InputStream pairJsonStream = SegmentRoutingDeviceConfigTest.class
+ .getResourceAsStream("/device-pair.json");
+ InputStream invalidJsonStream = SegmentRoutingDeviceConfigTest.class
+ .getResourceAsStream("/device-invalid.json");
+
+ adjacencySids1 = new HashMap<>();
+ Set<Integer> ports1 = new HashSet<>();
+ ports1.add(2);
+ ports1.add(3);
+ adjacencySids1.put(100, ports1);
+ Set<Integer> ports2 = new HashSet<>();
+ ports2.add(4);
+ ports2.add(5);
+ adjacencySids1.put(200, ports2);
+
+ adjacencySids2 = new HashMap<>();
+ Set<Integer> ports3 = new HashSet<>();
+ ports3.add(6);
+ adjacencySids2.put(300, ports3);
+
+ DeviceId subject = DeviceId.deviceId("of:0000000000000001");
+ String key = "segmentrouting";
+ ObjectMapper mapper = new ObjectMapper();
+ JsonNode jsonNode = mapper.readTree(jsonStream);
+ JsonNode ipv6JsonNode = mapper.readTree(ipv6JsonStream);
+ JsonNode pairJsonNode = mapper.readTree(pairJsonStream);
+ JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
+ ConfigApplyDelegate delegate = new MockDelegate();
+
+ config = new SegmentRoutingDeviceConfig();
+ config.init(subject, key, jsonNode, mapper, delegate);
+
+ ipv6Config = new SegmentRoutingDeviceConfig();
+ ipv6Config.init(subject, key, ipv6JsonNode, mapper, delegate);
+
+ pairConfig = new SegmentRoutingDeviceConfig();
+ pairConfig.init(subject, key, pairJsonNode, mapper, delegate);
+
+ invalidConfig = new SegmentRoutingDeviceConfig();
+ invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
+ }
+
+ @Test
+ public void testIsValid() {
+ assertTrue(config.isValid());
+ assertTrue(ipv6Config.isValid());
+ assertTrue(pairConfig.isValid());
+ assertFalse(invalidConfig.isValid());
+ }
+
+ @Test
+ public void testName() throws Exception {
+ assertTrue(config.name().isPresent());
+ assertThat(config.name().get(), is("Leaf-R1"));
+ }
+
+ @Test
+ public void testSetName() throws Exception {
+ config.setName("Spine-R1");
+ assertTrue(config.name().isPresent());
+ assertThat(config.name().get(), is("Spine-R1"));
+ }
+
+ @Test
+ public void testRouterIp() throws Exception {
+ assertThat(config.routerIpv4(), is(IpAddress.valueOf("10.0.1.254")));
+ assertThat(ipv6Config.routerIpv4(), is(IpAddress.valueOf("10.0.1.254")));
+ assertThat(ipv6Config.routerIpv6(), is(IpAddress.valueOf("2000::c0a8:0101")));
+ }
+
+ @Test
+ public void testSetRouterIp() throws Exception {
+ config.setRouterIpv4("10.0.2.254");
+ assertThat(config.routerIpv4(), is(IpAddress.valueOf("10.0.2.254")));
+ ipv6Config.setRouterIpv4("10.0.2.254");
+ assertThat(ipv6Config.routerIpv4(), is(IpAddress.valueOf("10.0.2.254")));
+ ipv6Config.setRouterIpv6("2000::c0a9:0101");
+ assertThat(ipv6Config.routerIpv6(), is(IpAddress.valueOf("2000::c0a9:0101")));
+ }
+
+ @Test
+ public void testRouterMac() throws Exception {
+ assertThat(config.routerMac(), is(MacAddress.valueOf("00:00:00:00:01:80")));
+ }
+
+ @Test
+ public void testSetRouterMac() throws Exception {
+ config.setRouterMac("00:00:00:00:02:80");
+ assertThat(config.routerMac(), is(MacAddress.valueOf("00:00:00:00:02:80")));
+ }
+
+ @Test
+ public void testNodeSid() throws Exception {
+ assertThat(config.nodeSidIPv4(), is(101));
+ assertThat(ipv6Config.nodeSidIPv4(), is(101));
+ assertThat(ipv6Config.nodeSidIPv6(), is(111));
+ }
+
+ @Test
+ public void testSetNodeSid() throws Exception {
+ config.setNodeSidIPv4(200);
+ assertThat(config.nodeSidIPv4(), is(200));
+ ipv6Config.setNodeSidIPv4(200);
+ assertThat(ipv6Config.nodeSidIPv4(), is(200));
+ ipv6Config.setNodeSidIPv6(201);
+ assertThat(ipv6Config.nodeSidIPv6(), is(201));
+ }
+
+ @Test
+ public void testIsEdgeRouter() throws Exception {
+ assertThat(config.isEdgeRouter(), is(true));
+ }
+
+ @Test
+ public void testSetIsEdgeRouter() throws Exception {
+ config.setIsEdgeRouter(false);
+ assertThat(config.isEdgeRouter(), is(false));
+ }
+
+ @Test
+ public void testAdjacencySids() throws Exception {
+ assertThat(config.adjacencySids(), is(adjacencySids1));
+ }
+
+ @Test
+ public void testSetAdjacencySids() throws Exception {
+ config.setAdjacencySids(adjacencySids2);
+ assertThat(config.adjacencySids(), is(adjacencySids2));
+ }
+
+ @Test
+ public void testPairDeviceId() throws Exception {
+ assertNull(config.pairDeviceId());
+ assertNull(ipv6Config.pairDeviceId());
+ assertThat(pairConfig.pairDeviceId(), is(PAIR_DEVICE_ID));
+ }
+
+ @Test
+ public void testSetPairDeviceId() throws Exception {
+ config.setPairDeviceId(PAIR_DEVICE_ID);
+ assertThat(config.pairDeviceId(), is(PAIR_DEVICE_ID));
+ }
+
+ @Test
+ public void testPairLocalPort() throws Exception {
+ assertNull(config.pairLocalPort());
+ assertNull(ipv6Config.pairLocalPort());
+ assertThat(pairConfig.pairLocalPort(), is(PAIR_LOCAL_PORT));
+ }
+
+ @Test
+ public void testSetPairLocalPort() throws Exception {
+ config.setPairLocalPort(PAIR_LOCAL_PORT);
+ assertThat(config.pairLocalPort(), is(PAIR_LOCAL_PORT));
+ }
+
+ private class MockDelegate implements ConfigApplyDelegate {
+ @Override
+ public void onApply(Config configFile) {
+ }
+ }
+}
diff --git a/api/src/test/resources/app-ecmp.json b/api/src/test/resources/app-ecmp.json
new file mode 100644
index 0000000..e94b1c5
--- /dev/null
+++ b/api/src/test/resources/app-ecmp.json
@@ -0,0 +1,19 @@
+{
+ "vRouterMacs" : [
+ "00:00:00:00:00:01",
+ "00:00:00:00:00:02"
+ ],
+ "suppressSubnet" : [
+ "of:1/1",
+ "of:1/2"
+ ],
+ "suppressHostByPort" : [
+ "of:1/1",
+ "of:1/2"
+ ],
+ "suppressHostByProvider" : [
+ "org.onosproject.provider.host",
+ "org.onosproject.netcfghost"
+ ],
+ "MPLS-ECMP" : true
+}
diff --git a/api/src/test/resources/app-invalid.json b/api/src/test/resources/app-invalid.json
new file mode 100644
index 0000000..01508f8
--- /dev/null
+++ b/api/src/test/resources/app-invalid.json
@@ -0,0 +1,15 @@
+{
+ "vRouterMacs" : [
+ "00:00:00:00:00:01",
+ "00:00:00:00:00:02"
+ ],
+ "suppressSubnet" : [
+ "of:1/1",
+ "of:1/2"
+ ],
+ "suppressHostByPort" : [
+ "of:1/1",
+ "wrongPort"
+ ],
+ "suppressHostByProvider" : []
+}
diff --git a/api/src/test/resources/app.json b/api/src/test/resources/app.json
new file mode 100644
index 0000000..2e59b0d
--- /dev/null
+++ b/api/src/test/resources/app.json
@@ -0,0 +1,21 @@
+{
+ "vRouterMacs" : [
+ "00:00:00:00:00:01",
+ "00:00:00:00:00:02"
+ ],
+ "suppressSubnet" : [
+ "of:1/1",
+ "of:1/2"
+ ],
+ "suppressHostByPort" : [
+ "of:1/1",
+ "of:1/2"
+ ],
+ "suppressHostByProvider" : [
+ "org.onosproject.provider.host",
+ "org.onosproject.netcfghost"
+ ],
+ "blackholeIps": [
+ "10.0.0.0/8"
+ ]
+}
diff --git a/api/src/test/resources/blocked-ports-alt.json b/api/src/test/resources/blocked-ports-alt.json
new file mode 100644
index 0000000..3d8749e
--- /dev/null
+++ b/api/src/test/resources/blocked-ports-alt.json
@@ -0,0 +1,5 @@
+{
+ "of:0000000000000001": ["1-9"],
+ "of:0000000000000003": ["7"],
+ "of:0000000000000004": ["1"]
+}
diff --git a/api/src/test/resources/blocked-ports.json b/api/src/test/resources/blocked-ports.json
new file mode 100644
index 0000000..2543f3d
--- /dev/null
+++ b/api/src/test/resources/blocked-ports.json
@@ -0,0 +1,5 @@
+{
+ "of:0000000000000001": ["1-4", "7-9"],
+ "of:0000000000000003": ["7-9"],
+ "of:0000000000000004": ["1", "5", "9"]
+}
diff --git a/api/src/test/resources/device-invalid.json b/api/src/test/resources/device-invalid.json
new file mode 100644
index 0000000..dfcbdb8
--- /dev/null
+++ b/api/src/test/resources/device-invalid.json
@@ -0,0 +1,12 @@
+{
+ "name" : "Leaf-R1",
+ "ipv4NodeSid" : 101,
+ "ipv4Loopback" : "10.0.1.254",
+ "routerMac" : "00:00:00:00:01:80",
+ "isEdgeRouter" : true,
+ "adjacencySids" : [
+ { "adjSid" : 100, "ports" : [2, 3] },
+ { "adjSid" : 200, "ports" : [4, 5] }
+ ],
+ "pairDeviceId" : "of:123456789ABCDEF0"
+}
diff --git a/api/src/test/resources/device-ipv6.json b/api/src/test/resources/device-ipv6.json
new file mode 100644
index 0000000..9832f8c
--- /dev/null
+++ b/api/src/test/resources/device-ipv6.json
@@ -0,0 +1,13 @@
+{
+ "name" : "Leaf-R1",
+ "ipv4NodeSid" : 101,
+ "ipv4Loopback" : "10.0.1.254",
+ "ipv6NodeSid" : 111,
+ "ipv6Loopback" : "2000::c0a8:0101",
+ "routerMac" : "00:00:00:00:01:80",
+ "isEdgeRouter" : true,
+ "adjacencySids" : [
+ { "adjSid" : 100, "ports" : [2, 3] },
+ { "adjSid" : 200, "ports" : [4, 5] }
+ ]
+}
diff --git a/api/src/test/resources/device-pair.json b/api/src/test/resources/device-pair.json
new file mode 100644
index 0000000..c699ff5
--- /dev/null
+++ b/api/src/test/resources/device-pair.json
@@ -0,0 +1,13 @@
+{
+ "name" : "Leaf-R1",
+ "ipv4NodeSid" : 101,
+ "ipv4Loopback" : "10.0.1.254",
+ "routerMac" : "00:00:00:00:01:80",
+ "isEdgeRouter" : true,
+ "adjacencySids" : [
+ { "adjSid" : 100, "ports" : [2, 3] },
+ { "adjSid" : 200, "ports" : [4, 5] }
+ ],
+ "pairDeviceId" : "of:123456789ABCDEF0",
+ "pairLocalPort" : "10"
+}
diff --git a/api/src/test/resources/device.json b/api/src/test/resources/device.json
new file mode 100644
index 0000000..83aec6e
--- /dev/null
+++ b/api/src/test/resources/device.json
@@ -0,0 +1,11 @@
+{
+ "name" : "Leaf-R1",
+ "ipv4NodeSid" : 101,
+ "ipv4Loopback" : "10.0.1.254",
+ "routerMac" : "00:00:00:00:01:80",
+ "isEdgeRouter" : true,
+ "adjacencySids" : [
+ { "adjSid" : 100, "ports" : [2, 3] },
+ { "adjSid" : 200, "ports" : [4, 5] }
+ ]
+}