CORD-349 Support VLAN cross-connect traffic
Change related to this topic:
- Support VLAN cross-connect traffic
Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample
Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix
Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
index 4866b82..124ffa7 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -26,7 +26,6 @@
import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for an edge device.
@@ -47,22 +46,13 @@
* 8) what about ecmp no label case
*/
public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
- // TODO Access stores through srManager
protected DefaultEdgeGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
- EventuallyConsistentMap<
- NeighborSetNextObjectiveStoreKey,
- Integer> nsNextObjStore,
- EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
- Integer> subnetNextObjStore,
- EventuallyConsistentMap<PortNextObjectiveStoreKey,
- Integer> portNextObjStore,
SegmentRoutingManager srManager) {
- super(deviceId, appId, config, linkService, flowObjService,
- nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+ super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index a4b89c1..4e64c4b 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -35,6 +35,7 @@
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
+import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
@@ -48,8 +49,6 @@
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.group.DefaultGroupKey;
-import org.onosproject.net.group.GroupKey;
import org.onosproject.net.link.LinkService;
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
@@ -81,12 +80,14 @@
//local store for ports on this device connected to neighbor-device-id
protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
new ConcurrentHashMap<>();
- protected EventuallyConsistentMap<
- NeighborSetNextObjectiveStoreKey, Integer> nsNextObjStore = null;
- protected EventuallyConsistentMap<
- SubnetNextObjectiveStoreKey, Integer> subnetNextObjStore = null;
- protected EventuallyConsistentMap<
- PortNextObjectiveStoreKey, Integer> portNextObjStore = null;
+ protected EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey, Integer>
+ nsNextObjStore = null;
+ protected EventuallyConsistentMap<SubnetNextObjectiveStoreKey, Integer>
+ subnetNextObjStore = null;
+ protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
+ portNextObjStore = null;
+ protected EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
+ xConnectNextObjStore = null;
private SegmentRoutingManager srManager;
protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
@@ -97,17 +98,10 @@
.register(GroupBucketIdentifier.class)
.register(GroupBucketIdentifier.BucketOutputType.class);
- // TODO Access stores through srManager
protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
- EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
- Integer> nsNextObjStore,
- EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
- Integer> subnetNextObjStore,
- EventuallyConsistentMap<PortNextObjectiveStoreKey,
- Integer> portNextObjStore,
SegmentRoutingManager srManager) {
this.deviceId = checkNotNull(deviceId);
this.appId = checkNotNull(appId);
@@ -123,9 +117,10 @@
+ " Skipping value assignment in DefaultGroupHandler");
}
this.flowObjectiveService = flowObjService;
- this.nsNextObjStore = nsNextObjStore;
- this.subnetNextObjStore = subnetNextObjStore;
- this.portNextObjStore = portNextObjStore;
+ this.nsNextObjStore = srManager.nsNextObjStore;
+ this.subnetNextObjStore = srManager.subnetNextObjStore;
+ this.portNextObjStore = srManager.portNextObjStore;
+ this.xConnectNextObjStore = srManager.xConnectNextObjStore;
this.srManager = srManager;
populateNeighborMaps();
@@ -141,8 +136,7 @@
* @param config interface to retrieve the device properties
* @param linkService link service object
* @param flowObjService flow objective service object
- * @param nsNextObjStore NeighborSet next objective store map
- * @param subnetNextObjStore subnet next objective store map
+ * @param srManager segment routing manager
* @throws DeviceConfigNotFoundException if the device configuration is not found
* @return default group handler type
*/
@@ -152,12 +146,6 @@
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
- EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
- Integer> nsNextObjStore,
- EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
- Integer> subnetNextObjStore,
- EventuallyConsistentMap<PortNextObjectiveStoreKey,
- Integer> portNextObjStore,
SegmentRoutingManager srManager)
throws DeviceConfigNotFoundException {
// handle possible exception in the caller
@@ -165,18 +153,12 @@
return new DefaultEdgeGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
- nsNextObjStore,
- subnetNextObjStore,
- portNextObjStore,
srManager
);
} else {
return new DefaultTransitGroupHandler(deviceId, appId, config,
linkService,
flowObjService,
- nsNextObjStore,
- subnetNextObjStore,
- portNextObjStore,
srManager);
}
}
@@ -194,6 +176,8 @@
* discovered on this device.
*
* @param newLink new neighbor link
+ * @param isMaster true if local instance is the master
+ *
*/
public void linkUp(Link newLink, boolean isMaster) {
@@ -296,6 +280,7 @@
* Performs group recovery procedures when a port goes down on this device.
*
* @param port port number that has gone down
+ * @param isMaster true if local instance is the master
*/
public void portDown(PortNumber port, boolean isMaster) {
if (portDeviceMap.get(port) == null) {
@@ -448,8 +433,8 @@
*/
public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
TrafficSelector meta) {
- Integer nextId = portNextObjStore.
- get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
+ Integer nextId = portNextObjStore
+ .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
if (nextId == null) {
log.trace("getPortNextObjectiveId in device{}: Next objective id "
+ "not found for {} and {} creating", deviceId, portNum);
@@ -458,7 +443,33 @@
new PortNextObjectiveStoreKey(deviceId, portNum, treatment));
if (nextId == null) {
log.warn("getPortNextObjectiveId: unable to create next obj"
- + "for dev:{} port{}", deviceId, portNum);
+ + "for dev:{} port:{}", deviceId, portNum);
+ return -1;
+ }
+ }
+ return nextId;
+ }
+
+ /**
+ * Returns the next objective ID of type broadcast associated with the VLAN
+ * cross-connection.
+ *
+ * @param vlanId VLAN ID for the cross-connection
+ * @return int if found or created, -1 if there are errors during the
+ * creation of the next objective
+ */
+ public int getXConnectNextObjectiveId(VlanId vlanId) {
+ Integer nextId = xConnectNextObjStore
+ .get(new XConnectNextObjectiveStoreKey(deviceId, vlanId));
+ if (nextId == null) {
+ log.trace("getXConnectNextObjectiveId: Next objective id "
+ + "not found for device {} and vlan {}. Creating", deviceId, vlanId);
+ createGroupsForXConnect(deviceId);
+ nextId = xConnectNextObjStore.get(
+ new XConnectNextObjectiveStoreKey(deviceId, vlanId));
+ if (nextId == null) {
+ log.warn("getXConnectNextObjectiveId: Next objective id "
+ + "not found for device {} and vlan {}.", deviceId, vlanId);
return -1;
}
}
@@ -655,7 +666,6 @@
/**
* Creates broadcast groups for all ports in the same configured subnet.
- *
*/
public void createGroupsFromSubnetConfig() {
Map<Ip4Prefix, List<PortNumber>> subnetPortMap =
@@ -700,6 +710,54 @@
});
}
+ /**
+ * Creates broadcast groups for VLAN cross-connect ports.
+ *
+ * @param deviceId the DPID of the switch
+ */
+ public void createGroupsForXConnect(DeviceId deviceId) {
+ Map<VlanId, List<ConnectPoint>> xConnectsForDevice = deviceConfig.getXConnects();
+
+ xConnectsForDevice.forEach((vlanId, connectPoints) -> {
+ // Only proceed the xConnect for given device
+ for (ConnectPoint connectPoint : connectPoints) {
+ if (!connectPoint.deviceId().equals(deviceId)) {
+ return;
+ }
+ }
+
+ // Check if the next obj is already in the store
+ XConnectNextObjectiveStoreKey key =
+ new XConnectNextObjectiveStoreKey(deviceId, vlanId);
+ if (xConnectNextObjStore.containsKey(key)) {
+ log.debug("Cross-connect Broadcast group for device {} and vlanId {} exists",
+ deviceId, vlanId);
+ return;
+ }
+
+ TrafficSelector metadata =
+ DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
+ int nextId = flowObjectiveService.allocateNextId();
+
+ NextObjective.Builder nextObjBuilder = DefaultNextObjective
+ .builder().withId(nextId)
+ .withType(NextObjective.Type.BROADCAST).fromApp(appId)
+ .withMeta(metadata);
+
+ connectPoints.forEach(connectPoint -> {
+ TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+ tBuilder.setOutput(connectPoint.port());
+ nextObjBuilder.addTreatment(tBuilder.build());
+ });
+
+ NextObjective nextObj = nextObjBuilder.add();
+ flowObjectiveService.next(deviceId, nextObj);
+ log.debug("createGroupsForXConnect: Submited next objective {} in device {}",
+ nextId, deviceId);
+ xConnectNextObjStore.put(key, nextId);
+ });
+ }
+
/**
* Create simple next objective for a single port. The treatments can include
@@ -730,11 +788,6 @@
portNextObjStore.put(key, nextId);
}
-
- public GroupKey getGroupKey(Object obj) {
- return new DefaultGroupKey(kryo.build().serialize(obj));
- }
-
/**
* Removes groups for the next objective ID given.
*
@@ -766,6 +819,9 @@
return false;
}
+ /**
+ * Removes all groups from all next objective stores.
+ */
public void removeAllGroups() {
for (Map.Entry<NeighborSetNextObjectiveStoreKey, Integer> entry:
nsNextObjStore.entrySet()) {
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
index 5bc7ede..9a93409 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultTransitGroupHandler.java
@@ -26,7 +26,6 @@
import org.onosproject.segmentrouting.SegmentRoutingManager;
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.store.service.EventuallyConsistentMap;
/**
* Default ECMP group handler creation module for a transit device.
@@ -41,22 +40,13 @@
* 2) all ports to D3 + with no label push,
*/
public class DefaultTransitGroupHandler extends DefaultGroupHandler {
- // TODO Access stores through srManager
protected DefaultTransitGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
- EventuallyConsistentMap<
- NeighborSetNextObjectiveStoreKey,
- Integer> nsNextObjStore,
- EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
- Integer> subnetNextObjStore,
- EventuallyConsistentMap<PortNextObjectiveStoreKey,
- Integer> portNextObjStore,
SegmentRoutingManager srManager) {
- super(deviceId, appId, config, linkService, flowObjService,
- nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+ super(deviceId, appId, config, linkService, flowObjService, srManager);
}
@Override
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
index 9ace531..ac33036 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/NeighborSetNextObjectiveStoreKey.java
@@ -21,22 +21,38 @@
import org.onosproject.net.DeviceId;
/**
- * Class definition of Key for Neighborset to NextObjective store.
+ * Key of Neighborset next objective store.
*/
public class NeighborSetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final NeighborSet ns;
+ /**
+ * Constructs the key of neighbor set next objective store.
+ *
+ * @param deviceId device ID
+ * @param ns neighbor set
+ */
public NeighborSetNextObjectiveStoreKey(DeviceId deviceId,
NeighborSet ns) {
this.deviceId = deviceId;
this.ns = ns;
}
+ /**
+ * Returns the device ID in the key.
+ *
+ * @return device ID
+ */
public DeviceId deviceId() {
return this.deviceId;
}
+ /**
+ * Returns the neighbor set in the key.
+ *
+ * @return neighbor set
+ */
public NeighborSet neighborSet() {
return this.ns;
}
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
index 4b0d518..4f32226 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
@@ -31,7 +31,6 @@
import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
import org.onosproject.segmentrouting.config.DeviceProperties;
import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
-import org.onosproject.store.service.EventuallyConsistentMap;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -51,33 +50,31 @@
private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
/**
- * Policy group handler constructor.
+ * Constructs policy group handler.
*
* @param deviceId device identifier
* @param appId application identifier
* @param config interface to retrieve the device properties
* @param linkService link service object
* @param flowObjService flow objective service object
- * @param nsNextObjStore NeighborSet next objective store map
- * @param subnetNextObjStore subnet next objective store map
+ * @param srManager segment routing manager
*/
- // TODO Access stores through srManager
public PolicyGroupHandler(DeviceId deviceId,
ApplicationId appId,
DeviceProperties config,
LinkService linkService,
FlowObjectiveService flowObjService,
- EventuallyConsistentMap<NeighborSetNextObjectiveStoreKey,
- Integer> nsNextObjStore,
- EventuallyConsistentMap<SubnetNextObjectiveStoreKey,
- Integer> subnetNextObjStore,
- EventuallyConsistentMap<PortNextObjectiveStoreKey,
- Integer> portNextObjStore,
SegmentRoutingManager srManager) {
- super(deviceId, appId, config, linkService, flowObjService,
- nsNextObjStore, subnetNextObjStore, portNextObjStore, srManager);
+ super(deviceId, appId, config, linkService, flowObjService, srManager);
}
+ /**
+ * Creates policy group chain.
+ *
+ * @param id unique identifier associated with the policy group
+ * @param params a list of policy group params
+ * @return policy group identifier
+ */
public PolicyGroupIdentifier createPolicyGroupChain(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
@@ -222,69 +219,18 @@
}
//TODO: Use nextObjective APIs to handle the group chains
- /*@Override
- protected void handleGroupEvent(GroupEvent event) {
- if (event.type() == GroupEvent.Type.GROUP_ADDED) {
- if (dependentGroups.get(event.subject().appCookie()) != null) {
- PolicyGroupIdentifier dependentGroupKey = dependentGroups.get(event.subject().appCookie());
- dependentGroups.remove(event.subject().appCookie());
- boolean fullyResolved = true;
- for (GroupBucketIdentifier bucketId:
- dependentGroupKey.bucketIds()) {
- if (bucketId.type() != BucketOutputType.GROUP) {
- continue;
- }
- if (dependentGroups.containsKey(bucketId.outGroup())) {
- fullyResolved = false;
- break;
- }
- }
+ /*
+ @Override
+ protected void handleGroupEvent(GroupEvent event) {}
+ */
- if (fullyResolved) {
- List<GroupBucket> outBuckets = new ArrayList<GroupBucket>();
- for (GroupBucketIdentifier bucketId:
- dependentGroupKey.bucketIds()) {
- TrafficTreatment.Builder tBuilder =
- DefaultTrafficTreatment.builder();
- if (bucketId.label() != NeighborSet.NO_EDGE_LABEL) {
- tBuilder.pushMpls()
- .setMpls(MplsLabel.
- mplsLabel(bucketId.label()));
- }
- //TODO: BoS
- if (bucketId.type() == BucketOutputType.PORT) {
- DeviceId neighbor = portDeviceMap.
- get(bucketId.outPort());
- tBuilder.setOutput(bucketId.outPort())
- .setEthDst(deviceConfig.
- getDeviceMac(neighbor))
- .setEthSrc(nodeMacAddr);
- } else {
- if (groupService.
- getGroup(deviceId,
- getGroupKey(bucketId.
- outGroup())) == null) {
- throw new IllegalStateException();
- }
- GroupId indirectGroupId = groupService.
- getGroup(deviceId,
- getGroupKey(bucketId.
- outGroup())).id();
- tBuilder.group(indirectGroupId);
- }
- outBuckets.add(DefaultGroupBucket.
- createSelectGroupBucket(tBuilder.build()));
- }
- GroupDescription desc = new
- DefaultGroupDescription(deviceId,
- GroupDescription.Type.SELECT,
- new GroupBuckets(outBuckets));
- groupService.addGroup(desc);
- }
- }
- }
- }*/
-
+ /**
+ * Generates policy group key.
+ *
+ * @param id unique identifier associated with the policy group
+ * @param params a list of policy group params
+ * @return policy group identifier
+ */
public PolicyGroupIdentifier generatePolicyGroupKey(String id,
List<PolicyGroupParams> params) {
List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
@@ -354,6 +300,11 @@
return innermostGroupkey;
}
+ /**
+ * Removes policy group chain.
+ *
+ * @param key policy group identifier
+ */
public void removeGroupChain(PolicyGroupIdentifier key) {
checkArgument(key != null);
List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
index 44a0a2c..ca283f0 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
@@ -28,7 +28,7 @@
private List<GroupBucketIdentifier> bucketIds;
/**
- * Constructor.
+ * Constructs policy group identifier.
*
* @param id unique identifier associated with the policy group
* @param input policy group params associated with this group
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
index 5555565..ec25ec3 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/PortNextObjectiveStoreKey.java
@@ -7,15 +7,23 @@
import java.util.Objects;
/**
- * Class definition of Key for Device/Port to NextObjective store. Since there
- * can be multiple next objectives to the same physical port, we differentiate
- * between them by including the treatment in the key.
+ * Key of Device/Port to NextObjective store.
+ *
+ * Since there can be multiple next objectives to the same physical port,
+ * we differentiate between them by including the treatment in the key.
*/
public class PortNextObjectiveStoreKey {
private final DeviceId deviceId;
private final PortNumber portNum;
private final TrafficTreatment treatment;
+ /**
+ * Constructs the key of port next objective store.
+ *
+ * @param deviceId device ID
+ * @param portNum port number
+ * @param treatment treatment that will be applied to the interface
+ */
public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
TrafficTreatment treatment) {
this.deviceId = deviceId;
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
index d6b16c7..8e5c3ce 100644
--- a/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/SubnetNextObjectiveStoreKey.java
@@ -22,12 +22,18 @@
import java.util.Objects;
/**
- * Class definition of Key for Subnet to NextObjective store.
+ * Key of Subnet to NextObjective store.
*/
public class SubnetNextObjectiveStoreKey {
private final DeviceId deviceId;
private final IpPrefix prefix;
+ /**
+ * Constructs the key of subnet next objective store.
+ *
+ * @param deviceId device ID
+ * @param prefix subnet information
+ */
public SubnetNextObjectiveStoreKey(DeviceId deviceId,
IpPrefix prefix) {
this.deviceId = deviceId;
diff --git a/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java b/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java
new file mode 100644
index 0000000..f3da880
--- /dev/null
+++ b/src/main/java/org/onosproject/segmentrouting/grouphandler/XConnectNextObjectiveStoreKey.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.grouphandler;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+
+import java.util.Objects;
+
+/**
+ * Key of VLAN cross-connect next objective store.
+ */
+public class XConnectNextObjectiveStoreKey {
+ private final DeviceId deviceId;
+ private final VlanId vlanId;
+
+ /**
+ * Constructs the key of cross-connect next objective store.
+ *
+ * @param deviceId device ID of the VLAN cross-connection
+ * @param vlanId VLAN ID of the VLAN cross-connection
+ */
+ public XConnectNextObjectiveStoreKey(DeviceId deviceId, VlanId vlanId) {
+ this.deviceId = deviceId;
+ this.vlanId = vlanId;
+ }
+
+ /**
+ * Returns the device ID of this key.
+ *
+ * @return device ID
+ */
+ public DeviceId deviceId() {
+ return this.deviceId;
+ }
+
+ /**
+ * Returns the VLAN ID of this key.
+ *
+ * @return VLAN ID
+ */
+ public VlanId vlanId() {
+ return this.vlanId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof XConnectNextObjectiveStoreKey)) {
+ return false;
+ }
+ XConnectNextObjectiveStoreKey that =
+ (XConnectNextObjectiveStoreKey) o;
+ return (Objects.equals(this.deviceId, that.deviceId) &&
+ Objects.equals(this.vlanId, that.vlanId));
+ }
+
+ // The list of neighbor ids and label are used for comparison.
+ @Override
+ public int hashCode() {
+ return Objects.hash(deviceId, vlanId);
+ }
+
+ @Override
+ public String toString() {
+ return "Device: " + deviceId + " VlanId: " + vlanId;
+ }
+}