Charles Chan
Committed by Ray Milkey

CORD-61 Dynamic XConnect support

- Add new XConnectConfig with unit test
- Gather XConnect features into XConnectHandler
- Introduce ObjectiveError.Type.GROUPREMOVALFAILED
- Rename
    - NetworkConfigEventHandler -> AppConfigHandler
    - XConnectNextObjectiveStoreKey -> XConnectStoreKey
    - Test json file
- Refactor

Change-Id: I8ca3176ed976c71ce9e28b7f3722ce80d49c816f
Showing 21 changed files with 893 additions and 297 deletions
...@@ -35,29 +35,29 @@ import java.util.HashSet; ...@@ -35,29 +35,29 @@ import java.util.HashSet;
35 import java.util.Set; 35 import java.util.Set;
36 36
37 /** 37 /**
38 - * Handles network config events. 38 + * Handles Segment Routing app config events.
39 */ 39 */
40 -public class NetworkConfigEventHandler { 40 +public class AppConfigHandler {
41 - private static final Logger log = LoggerFactory.getLogger(NetworkConfigEventHandler.class); 41 + private static final Logger log = LoggerFactory.getLogger(AppConfigHandler.class);
42 private final SegmentRoutingManager srManager; 42 private final SegmentRoutingManager srManager;
43 private final DeviceService deviceService; 43 private final DeviceService deviceService;
44 44
45 /** 45 /**
46 - * Constructs Network Config Event Handler. 46 + * Constructs Segment Routing App Config Handler.
47 * 47 *
48 * @param srManager instance of {@link SegmentRoutingManager} 48 * @param srManager instance of {@link SegmentRoutingManager}
49 */ 49 */
50 - public NetworkConfigEventHandler(SegmentRoutingManager srManager) { 50 + public AppConfigHandler(SegmentRoutingManager srManager) {
51 this.srManager = srManager; 51 this.srManager = srManager;
52 this.deviceService = srManager.deviceService; 52 this.deviceService = srManager.deviceService;
53 } 53 }
54 54
55 /** 55 /**
56 - * Processes vRouter config added event. 56 + * Processes Segment Routing App Config added event.
57 * 57 *
58 * @param event network config added event 58 * @param event network config added event
59 */ 59 */
60 - protected void processVRouterConfigAdded(NetworkConfigEvent event) { 60 + protected void processAppConfigAdded(NetworkConfigEvent event) {
61 log.info("Processing vRouter CONFIG_ADDED"); 61 log.info("Processing vRouter CONFIG_ADDED");
62 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get(); 62 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
63 deviceService.getAvailableDevices().forEach(device -> { 63 deviceService.getAvailableDevices().forEach(device -> {
...@@ -66,11 +66,11 @@ public class NetworkConfigEventHandler { ...@@ -66,11 +66,11 @@ public class NetworkConfigEventHandler {
66 } 66 }
67 67
68 /** 68 /**
69 - * Processes vRouter config updated event. 69 + * Processes Segment Routing App Config updated event.
70 * 70 *
71 * @param event network config updated event 71 * @param event network config updated event
72 */ 72 */
73 - protected void processVRouterConfigUpdated(NetworkConfigEvent event) { 73 + protected void processAppConfigUpdated(NetworkConfigEvent event) {
74 log.info("Processing vRouter CONFIG_UPDATED"); 74 log.info("Processing vRouter CONFIG_UPDATED");
75 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get(); 75 SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
76 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get(); 76 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
...@@ -91,11 +91,11 @@ public class NetworkConfigEventHandler { ...@@ -91,11 +91,11 @@ public class NetworkConfigEventHandler {
91 } 91 }
92 92
93 /** 93 /**
94 - * Processes vRouter config removed event. 94 + * Processes Segment Routing App Config removed event.
95 * 95 *
96 * @param event network config removed event 96 * @param event network config removed event
97 */ 97 */
98 - protected void processVRouterConfigRemoved(NetworkConfigEvent event) { 98 + protected void processAppConfigRemoved(NetworkConfigEvent event) {
99 log.info("Processing vRouter CONFIG_REMOVED"); 99 log.info("Processing vRouter CONFIG_REMOVED");
100 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get(); 100 SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
101 deviceService.getAvailableDevices().forEach(device -> { 101 deviceService.getAvailableDevices().forEach(device -> {
......
...@@ -564,7 +564,6 @@ public class DefaultRoutingHandler { ...@@ -564,7 +564,6 @@ public class DefaultRoutingHandler {
564 * @param deviceId Switch ID to set the rules 564 * @param deviceId Switch ID to set the rules
565 */ 565 */
566 public void populatePortAddressingRules(DeviceId deviceId) { 566 public void populatePortAddressingRules(DeviceId deviceId) {
567 - rulePopulator.populateXConnectVlanFilters(deviceId);
568 rulePopulator.populateRouterIpPunts(deviceId); 567 rulePopulator.populateRouterIpPunts(deviceId);
569 568
570 // Although device is added, sometimes device store does not have the 569 // Although device is added, sometimes device store does not have the
......
...@@ -79,8 +79,8 @@ public class McastHandler { ...@@ -79,8 +79,8 @@ public class McastHandler {
79 private static final Logger log = LoggerFactory.getLogger(McastHandler.class); 79 private static final Logger log = LoggerFactory.getLogger(McastHandler.class);
80 private final SegmentRoutingManager srManager; 80 private final SegmentRoutingManager srManager;
81 private final ApplicationId coreAppId; 81 private final ApplicationId coreAppId;
82 - private StorageService storageService; 82 + private final StorageService storageService;
83 - private TopologyService topologyService; 83 + private final TopologyService topologyService;
84 private final ConsistentMap<McastStoreKey, NextObjective> mcastNextObjStore; 84 private final ConsistentMap<McastStoreKey, NextObjective> mcastNextObjStore;
85 private final KryoNamespace.Builder mcastKryo; 85 private final KryoNamespace.Builder mcastKryo;
86 private final ConsistentMap<McastStoreKey, McastRole> mcastRoleStore; 86 private final ConsistentMap<McastStoreKey, McastRole> mcastRoleStore;
...@@ -132,7 +132,7 @@ public class McastHandler { ...@@ -132,7 +132,7 @@ public class McastHandler {
132 /** 132 /**
133 * Read initial multicast from mcast store. 133 * Read initial multicast from mcast store.
134 */ 134 */
135 - public void init() { 135 + protected void init() {
136 srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> { 136 srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
137 ConnectPoint source = srManager.multicastRouteService.fetchSource(mcastRoute); 137 ConnectPoint source = srManager.multicastRouteService.fetchSource(mcastRoute);
138 Set<ConnectPoint> sinks = srManager.multicastRouteService.fetchSinks(mcastRoute); 138 Set<ConnectPoint> sinks = srManager.multicastRouteService.fetchSinks(mcastRoute);
...@@ -472,7 +472,7 @@ public class McastHandler { ...@@ -472,7 +472,7 @@ public class McastHandler {
472 log.warn("Failed to update {} on {}/{}, vlan {}: {}", 472 log.warn("Failed to update {} on {}/{}, vlan {}: {}",
473 mcastIp, deviceId, port.toLong(), assignedVlan, error)); 473 mcastIp, deviceId, port.toLong(), assignedVlan, error));
474 newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add(); 474 newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add();
475 - fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(); 475 + fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(context);
476 mcastNextObjStore.put(mcastStoreKey, newNextObj); 476 mcastNextObjStore.put(mcastStoreKey, newNextObj);
477 srManager.flowObjectiveService.next(deviceId, newNextObj); 477 srManager.flowObjectiveService.next(deviceId, newNextObj);
478 srManager.flowObjectiveService.forward(deviceId, fwdObj); 478 srManager.flowObjectiveService.forward(deviceId, fwdObj);
...@@ -779,11 +779,7 @@ public class McastHandler { ...@@ -779,11 +779,7 @@ public class McastHandler {
779 // Spine-facing port should have no subnet and no xconnect 779 // Spine-facing port should have no subnet and no xconnect
780 if (srManager.deviceConfiguration != null && 780 if (srManager.deviceConfiguration != null &&
781 srManager.deviceConfiguration.getPortSubnet(ingressDevice, port) == null && 781 srManager.deviceConfiguration.getPortSubnet(ingressDevice, port) == null &&
782 - srManager.deviceConfiguration.getXConnects().values().stream() 782 + !srManager.xConnectHandler.hasXConnect(new ConnectPoint(ingressDevice, port))) {
783 - .allMatch(connectPoints ->
784 - connectPoints.stream().noneMatch(connectPoint ->
785 - connectPoint.port().equals(port))
786 - )) {
787 return port; 783 return port;
788 } 784 }
789 } 785 }
......
...@@ -50,7 +50,6 @@ import org.slf4j.LoggerFactory; ...@@ -50,7 +50,6 @@ import org.slf4j.LoggerFactory;
50 import java.util.ArrayList; 50 import java.util.ArrayList;
51 import java.util.HashSet; 51 import java.util.HashSet;
52 import java.util.List; 52 import java.util.List;
53 -import java.util.Map;
54 import java.util.Set; 53 import java.util.Set;
55 import java.util.concurrent.atomic.AtomicLong; 54 import java.util.concurrent.atomic.AtomicLong;
56 55
...@@ -694,85 +693,6 @@ public class RoutingRulePopulator { ...@@ -694,85 +693,6 @@ public class RoutingRulePopulator {
694 }); 693 });
695 } 694 }
696 695
697 - /**
698 - * Creates a filtering objective to permit VLAN cross-connect traffic.
699 - *
700 - * @param deviceId the DPID of the switch
701 - */
702 - public void populateXConnectVlanFilters(DeviceId deviceId) {
703 - Map<VlanId, List<ConnectPoint>> xConnectsForDevice =
704 - config.getXConnects();
705 - xConnectsForDevice.forEach((vlanId, connectPoints) -> {
706 - // Only proceed the xConnect for given device
707 - for (ConnectPoint connectPoint : connectPoints) {
708 - if (!connectPoint.deviceId().equals(deviceId)) {
709 - return;
710 - }
711 - }
712 -
713 - connectPoints.forEach(connectPoint -> {
714 - FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
715 - fob.withKey(Criteria.matchInPort(connectPoint.port()))
716 - .addCondition(Criteria.matchVlanId(vlanId))
717 - .addCondition(Criteria.matchEthDst(MacAddress.NONE))
718 - .withPriority(SegmentRoutingService.XCONNECT_PRIORITY);
719 - fob.permit().fromApp(srManager.appId);
720 - ObjectiveContext context = new DefaultObjectiveContext(
721 - (objective) -> log.debug("XConnect filter for {} populated", connectPoint),
722 - (objective, error) ->
723 - log.warn("Failed to populate xconnect filter for {}: {}", connectPoint, error));
724 - srManager.flowObjectiveService.filter(deviceId, fob.add(context));
725 - });
726 - });
727 - }
728 -
729 - /**
730 - * Populates a forwarding objective that points the VLAN cross-connect
731 - * packets to a broadcast group.
732 - *
733 - * @param deviceId switch ID to set the rules
734 - */
735 - public void populateXConnectBroadcastRule(DeviceId deviceId) {
736 - Map<VlanId, List<ConnectPoint>> xConnects =
737 - config.getXConnects();
738 - xConnects.forEach((vlanId, connectPoints) -> {
739 - // Only proceed the xConnect for given device
740 - for (ConnectPoint connectPoint : connectPoints) {
741 - if (!connectPoint.deviceId().equals(deviceId)) {
742 - return;
743 - }
744 - }
745 -
746 - int nextId = srManager.getXConnectNextObjectiveId(deviceId, vlanId);
747 - if (nextId < 0) {
748 - log.error("Cannot install cross-connect broadcast rule in dev:{} " +
749 - "due to missing nextId:{}", deviceId, nextId);
750 - return;
751 - }
752 -
753 - /*
754 - * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
755 - * as the VLAN cross-connect broadcast rules
756 - */
757 - TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
758 - sbuilder.matchVlanId(vlanId);
759 - sbuilder.matchEthDst(MacAddress.NONE);
760 -
761 - ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
762 - fob.withFlag(Flag.SPECIFIC)
763 - .withSelector(sbuilder.build())
764 - .nextStep(nextId)
765 - .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
766 - .fromApp(srManager.appId)
767 - .makePermanent();
768 - ObjectiveContext context = new DefaultObjectiveContext(
769 - (objective) -> log.debug("XConnect rule for {} populated", xConnects),
770 - (objective, error) ->
771 - log.warn("Failed to populate xconnect rule for {}: {}", xConnects, error));
772 - srManager.flowObjectiveService.forward(deviceId, fob.add(context));
773 - });
774 - }
775 -
776 private int getPriorityFromPrefix(IpPrefix prefix) { 696 private int getPriorityFromPrefix(IpPrefix prefix) {
777 return (prefix.isIp4()) ? 697 return (prefix.isIp4()) ?
778 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY : 698 2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
......
...@@ -61,6 +61,7 @@ import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; ...@@ -61,6 +61,7 @@ import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
61 import org.onosproject.segmentrouting.config.DeviceConfiguration; 61 import org.onosproject.segmentrouting.config.DeviceConfiguration;
62 import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig; 62 import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
63 import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig; 63 import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
64 +import org.onosproject.segmentrouting.config.XConnectConfig;
64 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; 65 import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
65 import org.onosproject.segmentrouting.grouphandler.NeighborSet; 66 import org.onosproject.segmentrouting.grouphandler.NeighborSet;
66 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey; 67 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
...@@ -75,7 +76,7 @@ import org.onosproject.net.packet.PacketProcessor; ...@@ -75,7 +76,7 @@ import org.onosproject.net.packet.PacketProcessor;
75 import org.onosproject.net.packet.PacketService; 76 import org.onosproject.net.packet.PacketService;
76 import org.onosproject.segmentrouting.storekey.SubnetAssignedVidStoreKey; 77 import org.onosproject.segmentrouting.storekey.SubnetAssignedVidStoreKey;
77 import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey; 78 import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey;
78 -import org.onosproject.segmentrouting.storekey.XConnectNextObjectiveStoreKey; 79 +import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
79 import org.onosproject.store.serializers.KryoNamespaces; 80 import org.onosproject.store.serializers.KryoNamespaces;
80 import org.onosproject.store.service.EventuallyConsistentMap; 81 import org.onosproject.store.service.EventuallyConsistentMap;
81 import org.onosproject.store.service.EventuallyConsistentMapBuilder; 82 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
...@@ -159,7 +160,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -159,7 +160,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
159 private InternalPacketProcessor processor = null; 160 private InternalPacketProcessor processor = null;
160 private InternalLinkListener linkListener = null; 161 private InternalLinkListener linkListener = null;
161 private InternalDeviceListener deviceListener = null; 162 private InternalDeviceListener deviceListener = null;
162 - private NetworkConfigEventHandler netcfgHandler = null; 163 + private AppConfigHandler appCfgHandler = null;
164 + protected XConnectHandler xConnectHandler = null;
163 private McastHandler mcastHandler = null; 165 private McastHandler mcastHandler = null;
164 private HostHandler hostHandler = null; 166 private HostHandler hostHandler = null;
165 private InternalEventHandler eventHandler = new InternalEventHandler(); 167 private InternalEventHandler eventHandler = new InternalEventHandler();
...@@ -191,11 +193,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -191,11 +193,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
191 */ 193 */
192 public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer> 194 public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
193 portNextObjStore = null; 195 portNextObjStore = null;
194 - /**
195 - * Per cross-connect objective ID store with VLAN ID as key.
196 - */
197 - public EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
198 - xConnectNextObjStore = null;
199 // Per device, per-subnet assigned-vlans store, with (device id + subnet 196 // Per device, per-subnet assigned-vlans store, with (device id + subnet
200 // IPv4 prefix) as key 197 // IPv4 prefix) as key
201 private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId> 198 private EventuallyConsistentMap<SubnetAssignedVidStoreKey, VlanId>
...@@ -204,7 +201,8 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -204,7 +201,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
204 private EventuallyConsistentMap<String, Policy> policyStore = null; 201 private EventuallyConsistentMap<String, Policy> policyStore = null;
205 202
206 private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> deviceConfigFactory = 203 private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> deviceConfigFactory =
207 - new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY, 204 + new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(
205 + SubjectFactories.DEVICE_SUBJECT_FACTORY,
208 SegmentRoutingDeviceConfig.class, "segmentrouting") { 206 SegmentRoutingDeviceConfig.class, "segmentrouting") {
209 @Override 207 @Override
210 public SegmentRoutingDeviceConfig createConfig() { 208 public SegmentRoutingDeviceConfig createConfig() {
...@@ -212,16 +210,26 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -212,16 +210,26 @@ public class SegmentRoutingManager implements SegmentRoutingService {
212 } 210 }
213 }; 211 };
214 private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> appConfigFactory = 212 private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> appConfigFactory =
215 - new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY, 213 + new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(
214 + SubjectFactories.APP_SUBJECT_FACTORY,
216 SegmentRoutingAppConfig.class, "segmentrouting") { 215 SegmentRoutingAppConfig.class, "segmentrouting") {
217 @Override 216 @Override
218 public SegmentRoutingAppConfig createConfig() { 217 public SegmentRoutingAppConfig createConfig() {
219 return new SegmentRoutingAppConfig(); 218 return new SegmentRoutingAppConfig();
220 } 219 }
221 }; 220 };
222 - 221 + private final ConfigFactory<ApplicationId, XConnectConfig> xConnectConfigFactory =
222 + new ConfigFactory<ApplicationId, XConnectConfig>(
223 + SubjectFactories.APP_SUBJECT_FACTORY,
224 + XConnectConfig.class, "xconnect") {
225 + @Override
226 + public XConnectConfig createConfig() {
227 + return new XConnectConfig();
228 + }
229 + };
223 private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory = 230 private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory =
224 - new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY, 231 + new ConfigFactory<ApplicationId, McastConfig>(
232 + SubjectFactories.APP_SUBJECT_FACTORY,
225 McastConfig.class, "multicast") { 233 McastConfig.class, "multicast") {
226 @Override 234 @Override
227 public McastConfig createConfig() { 235 public McastConfig createConfig() {
...@@ -280,15 +288,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -280,15 +288,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
280 .withTimestampProvider((k, v) -> new WallClockTimestamp()) 288 .withTimestampProvider((k, v) -> new WallClockTimestamp())
281 .build(); 289 .build();
282 290
283 - log.debug("Creating EC map xconnectnextobjectivestore");
284 - EventuallyConsistentMapBuilder<XConnectNextObjectiveStoreKey, Integer>
285 - xConnectNextObjStoreBuilder = storageService.eventuallyConsistentMapBuilder();
286 - xConnectNextObjStore = xConnectNextObjStoreBuilder
287 - .withName("xconnectnextobjectivestore")
288 - .withSerializer(createSerializer())
289 - .withTimestampProvider((k, v) -> new WallClockTimestamp())
290 - .build();
291 -
292 EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder = 291 EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
293 storageService.eventuallyConsistentMapBuilder(); 292 storageService.eventuallyConsistentMapBuilder();
294 tunnelStore = tunnelMapBuilder 293 tunnelStore = tunnelMapBuilder
...@@ -321,13 +320,15 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -321,13 +320,15 @@ public class SegmentRoutingManager implements SegmentRoutingService {
321 processor = new InternalPacketProcessor(); 320 processor = new InternalPacketProcessor();
322 linkListener = new InternalLinkListener(); 321 linkListener = new InternalLinkListener();
323 deviceListener = new InternalDeviceListener(); 322 deviceListener = new InternalDeviceListener();
324 - netcfgHandler = new NetworkConfigEventHandler(this); 323 + appCfgHandler = new AppConfigHandler(this);
324 + xConnectHandler = new XConnectHandler(this);
325 mcastHandler = new McastHandler(this); 325 mcastHandler = new McastHandler(this);
326 hostHandler = new HostHandler(this); 326 hostHandler = new HostHandler(this);
327 327
328 cfgService.addListener(cfgListener); 328 cfgService.addListener(cfgListener);
329 cfgService.registerConfigFactory(deviceConfigFactory); 329 cfgService.registerConfigFactory(deviceConfigFactory);
330 cfgService.registerConfigFactory(appConfigFactory); 330 cfgService.registerConfigFactory(appConfigFactory);
331 + cfgService.registerConfigFactory(xConnectConfigFactory);
331 cfgService.registerConfigFactory(mcastConfigFactory); 332 cfgService.registerConfigFactory(mcastConfigFactory);
332 hostService.addListener(hostListener); 333 hostService.addListener(hostListener);
333 packetService.addProcessor(processor, PacketProcessor.director(2)); 334 packetService.addProcessor(processor, PacketProcessor.director(2));
...@@ -358,7 +359,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -358,7 +359,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
358 TunnelPolicy.class, 359 TunnelPolicy.class,
359 Policy.Type.class, 360 Policy.Type.class,
360 PortNextObjectiveStoreKey.class, 361 PortNextObjectiveStoreKey.class,
361 - XConnectNextObjectiveStoreKey.class 362 + XConnectStoreKey.class
362 ); 363 );
363 } 364 }
364 365
...@@ -387,7 +388,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -387,7 +388,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
387 nsNextObjStore.destroy(); 388 nsNextObjStore.destroy();
388 subnetNextObjStore.destroy(); 389 subnetNextObjStore.destroy();
389 portNextObjStore.destroy(); 390 portNextObjStore.destroy();
390 - xConnectNextObjStore.destroy();
391 tunnelStore.destroy(); 391 tunnelStore.destroy();
392 policyStore.destroy(); 392 policyStore.destroy();
393 subnetVidStore.destroy(); 393 subnetVidStore.destroy();
...@@ -591,25 +591,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -591,25 +591,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
591 } 591 }
592 } 592 }
593 593
594 - /**
595 - * Returns the next objective ID of type broadcast associated with the VLAN
596 - * cross-connection.
597 - *
598 - * @param deviceId Device ID for the cross-connection
599 - * @param vlanId VLAN ID for the cross-connection
600 - * @return next objective ID or -1 if it was not found
601 - */
602 - public int getXConnectNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
603 - DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
604 - if (ghdlr != null) {
605 - return ghdlr.getXConnectNextObjectiveId(vlanId);
606 - } else {
607 - log.warn("getPortNextObjectiveId query - groupHandler for device {}"
608 - + " not found", deviceId);
609 - return -1;
610 - }
611 - }
612 -
613 private class InternalPacketProcessor implements PacketProcessor { 594 private class InternalPacketProcessor implements PacketProcessor {
614 @Override 595 @Override
615 public void process(PacketContext context) { 596 public void process(PacketContext context) {
...@@ -836,14 +817,13 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -836,14 +817,13 @@ public class SegmentRoutingManager implements SegmentRoutingService {
836 817
837 if (mastershipService.isLocalMaster(deviceId)) { 818 if (mastershipService.isLocalMaster(deviceId)) {
838 hostHandler.readInitialHosts(deviceId); 819 hostHandler.readInitialHosts(deviceId);
820 + xConnectHandler.init(deviceId);
839 DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId); 821 DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
840 groupHandler.createGroupsFromSubnetConfig(); 822 groupHandler.createGroupsFromSubnetConfig();
841 routingRulePopulator.populateSubnetBroadcastRule(deviceId); 823 routingRulePopulator.populateSubnetBroadcastRule(deviceId);
842 - groupHandler.createGroupsForXConnect(deviceId);
843 - routingRulePopulator.populateXConnectBroadcastRule(deviceId);
844 } 824 }
845 825
846 - netcfgHandler.initVRouters(deviceId); 826 + appCfgHandler.initVRouters(deviceId);
847 } 827 }
848 828
849 private void processDeviceRemoved(Device device) { 829 private void processDeviceRemoved(Device device) {
...@@ -862,11 +842,6 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -862,11 +842,6 @@ public class SegmentRoutingManager implements SegmentRoutingService {
862 .forEach(entry -> { 842 .forEach(entry -> {
863 portNextObjStore.remove(entry.getKey()); 843 portNextObjStore.remove(entry.getKey());
864 }); 844 });
865 - xConnectNextObjStore.entrySet().stream()
866 - .filter(entry -> entry.getKey().deviceId().equals(device.id()))
867 - .forEach(entry -> {
868 - xConnectNextObjStore.remove(entry.getKey());
869 - });
870 subnetVidStore.entrySet().stream() 845 subnetVidStore.entrySet().stream()
871 .filter(entry -> entry.getKey().deviceId().equals(device.id())) 846 .filter(entry -> entry.getKey().deviceId().equals(device.id()))
872 .forEach(entry -> { 847 .forEach(entry -> {
...@@ -875,6 +850,7 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -875,6 +850,7 @@ public class SegmentRoutingManager implements SegmentRoutingService {
875 groupHandlerMap.remove(device.id()); 850 groupHandlerMap.remove(device.id());
876 defaultRoutingHandler.purgeEcmpGraph(device.id()); 851 defaultRoutingHandler.purgeEcmpGraph(device.id());
877 mcastHandler.removeDevice(device.id()); 852 mcastHandler.removeDevice(device.id());
853 + xConnectHandler.removeDevice(device.id());
878 } 854 }
879 855
880 private void processPortRemoved(Device device, Port port) { 856 private void processPortRemoved(Device device, Port port) {
...@@ -942,16 +918,31 @@ public class SegmentRoutingManager implements SegmentRoutingService { ...@@ -942,16 +918,31 @@ public class SegmentRoutingManager implements SegmentRoutingService {
942 break; 918 break;
943 } 919 }
944 } else if (event.configClass().equals(SegmentRoutingAppConfig.class)) { 920 } else if (event.configClass().equals(SegmentRoutingAppConfig.class)) {
945 - checkState(netcfgHandler != null, "NetworkConfigEventHandler is not initialized"); 921 + checkState(appCfgHandler != null, "NetworkConfigEventHandler is not initialized");
922 + switch (event.type()) {
923 + case CONFIG_ADDED:
924 + appCfgHandler.processAppConfigAdded(event);
925 + break;
926 + case CONFIG_UPDATED:
927 + appCfgHandler.processAppConfigUpdated(event);
928 + break;
929 + case CONFIG_REMOVED:
930 + appCfgHandler.processAppConfigRemoved(event);
931 + break;
932 + default:
933 + break;
934 + }
935 + } else if (event.configClass().equals(XConnectConfig.class)) {
936 + checkState(xConnectHandler != null, "XConnectHandler is not initialized");
946 switch (event.type()) { 937 switch (event.type()) {
947 case CONFIG_ADDED: 938 case CONFIG_ADDED:
948 - netcfgHandler.processVRouterConfigAdded(event); 939 + xConnectHandler.processXConnectConfigAdded(event);
949 break; 940 break;
950 case CONFIG_UPDATED: 941 case CONFIG_UPDATED:
951 - netcfgHandler.processVRouterConfigUpdated(event); 942 + xConnectHandler.processXConnectConfigUpdated(event);
952 break; 943 break;
953 case CONFIG_REMOVED: 944 case CONFIG_REMOVED:
954 - netcfgHandler.processVRouterConfigRemoved(event); 945 + xConnectHandler.processXConnectConfigRemoved(event);
955 break; 946 break;
956 default: 947 default:
957 break; 948 break;
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.segmentrouting;
18 +
19 +import com.google.common.collect.ImmutableSet;
20 +import org.onlab.packet.MacAddress;
21 +import org.onlab.util.KryoNamespace;
22 +import org.onosproject.net.ConnectPoint;
23 +import org.onosproject.net.DeviceId;
24 +import org.onosproject.net.PortNumber;
25 +import org.onosproject.net.config.NetworkConfigEvent;
26 +import org.onosproject.net.flow.DefaultTrafficSelector;
27 +import org.onosproject.net.flow.DefaultTrafficTreatment;
28 +import org.onosproject.net.flow.TrafficSelector;
29 +import org.onosproject.net.flow.TrafficTreatment;
30 +import org.onosproject.net.flow.criteria.Criteria;
31 +import org.onosproject.net.flowobjective.DefaultFilteringObjective;
32 +import org.onosproject.net.flowobjective.DefaultForwardingObjective;
33 +import org.onosproject.net.flowobjective.DefaultNextObjective;
34 +import org.onosproject.net.flowobjective.DefaultObjectiveContext;
35 +import org.onosproject.net.flowobjective.FilteringObjective;
36 +import org.onosproject.net.flowobjective.ForwardingObjective;
37 +import org.onosproject.net.flowobjective.NextObjective;
38 +import org.onosproject.net.flowobjective.Objective;
39 +import org.onosproject.net.flowobjective.ObjectiveContext;
40 +import org.onosproject.net.flowobjective.ObjectiveError;
41 +import org.onosproject.segmentrouting.config.XConnectConfig;
42 +import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
43 +import org.onosproject.store.serializers.KryoNamespaces;
44 +import org.onosproject.store.service.ConsistentMap;
45 +import org.onosproject.store.service.Serializer;
46 +import org.onosproject.store.service.StorageService;
47 +import org.onosproject.store.service.Versioned;
48 +import org.slf4j.Logger;
49 +import org.slf4j.LoggerFactory;
50 +
51 +import java.util.Iterator;
52 +import java.util.Map;
53 +import java.util.Set;
54 +import java.util.concurrent.CompletableFuture;
55 +import java.util.stream.Collectors;
56 +
57 +/**
58 + * Handles cross connect related events.
59 + */
60 +public class XConnectHandler {
61 + private static final Logger log = LoggerFactory.getLogger(XConnectHandler.class);
62 + private static final String CONFIG_NOT_FOUND = "XConnect config missing";
63 + private static final String NOT_MASTER = "Not master controller";
64 + private final SegmentRoutingManager srManager;
65 + private final StorageService storageService;
66 + private final ConsistentMap<XConnectStoreKey, NextObjective> xConnectNextObjStore;
67 + private final KryoNamespace.Builder xConnectKryo;
68 +
69 + protected XConnectHandler(SegmentRoutingManager srManager) {
70 + this.srManager = srManager;
71 + this.storageService = srManager.storageService;
72 + xConnectKryo = new KryoNamespace.Builder()
73 + .register(KryoNamespaces.API)
74 + .register(XConnectStoreKey.class)
75 + .register(NextObjContext.class);
76 + xConnectNextObjStore = storageService
77 + .<XConnectStoreKey, NextObjective>consistentMapBuilder()
78 + .withName("onos-xconnect-nextobj-store")
79 + .withSerializer(Serializer.using(xConnectKryo.build()))
80 + .build();
81 + }
82 +
83 + /**
84 + * Read initial XConnect for given device.
85 + *
86 + * @param deviceId ID of the device to be initialized
87 + */
88 + public void init(DeviceId deviceId) {
89 + // Try to read XConnect config
90 + XConnectConfig config =
91 + srManager.cfgService.getConfig(srManager.appId, XConnectConfig.class);
92 + if (config == null) {
93 + log.warn("Failed to read XConnect config: {}", CONFIG_NOT_FOUND);
94 + return;
95 + }
96 +
97 + config.getXconnects(deviceId).forEach(key -> {
98 + populateXConnect(key, config.getPorts(key));
99 + });
100 + }
101 +
102 + /**
103 + * Processes Segment Routing App Config added event.
104 + *
105 + * @param event network config added event
106 + */
107 + protected void processXConnectConfigAdded(NetworkConfigEvent event) {
108 + log.info("Processing XConnect CONFIG_ADDED");
109 + XConnectConfig config = (XConnectConfig) event.config().get();
110 + config.getXconnects().forEach(key -> {
111 + populateXConnect(key, config.getPorts(key));
112 + });
113 + }
114 +
115 + /**
116 + * Processes Segment Routing App Config updated event.
117 + *
118 + * @param event network config updated event
119 + */
120 + protected void processXConnectConfigUpdated(NetworkConfigEvent event) {
121 + log.info("Processing XConnect CONFIG_UPDATED");
122 + XConnectConfig prevConfig = (XConnectConfig) event.prevConfig().get();
123 + XConnectConfig config = (XConnectConfig) event.config().get();
124 + Set<XConnectStoreKey> prevKeys = prevConfig.getXconnects();
125 + Set<XConnectStoreKey> keys = config.getXconnects();
126 +
127 + Set<XConnectStoreKey> pendingRemove = prevKeys.stream()
128 + .filter(key -> !keys.contains(key)).collect(Collectors.toSet());
129 + Set<XConnectStoreKey> pendingAdd = keys.stream()
130 + .filter(key -> !prevKeys.contains(key)).collect(Collectors.toSet());
131 + Set<XConnectStoreKey> pendingUpdate = keys.stream()
132 + .filter(key -> prevKeys.contains(key) &&
133 + !config.getPorts(key).equals(prevConfig.getPorts(key)))
134 + .collect(Collectors.toSet());
135 +
136 + pendingRemove.forEach(key -> {
137 + revokeXConnect(key, prevConfig.getPorts(key));
138 + });
139 + pendingAdd.forEach(key -> {
140 + populateXConnect(key, config.getPorts(key));
141 + });
142 + pendingUpdate.forEach(key -> {
143 + updateXConnect(key, prevConfig.getPorts(key), config.getPorts(key));
144 + });
145 + }
146 +
147 + /**
148 + * Processes Segment Routing App Config removed event.
149 + *
150 + * @param event network config removed event
151 + */
152 + protected void processXConnectConfigRemoved(NetworkConfigEvent event) {
153 + log.info("Processing XConnect CONFIG_REMOVED");
154 + XConnectConfig prevConfig = (XConnectConfig) event.prevConfig().get();
155 + prevConfig.getXconnects().forEach(key -> {
156 + revokeXConnect(key, prevConfig.getPorts(key));
157 + });
158 + }
159 +
160 + /**
161 + * Checks if there is any XConnect configured on given connect point.
162 + *
163 + * @param cp connect point
164 + * @return true if there is XConnect configured on given connect point.
165 + */
166 + public boolean hasXConnect(ConnectPoint cp) {
167 + // Try to read XConnect config
168 + XConnectConfig config =
169 + srManager.cfgService.getConfig(srManager.appId, XConnectConfig.class);
170 + if (config == null) {
171 + log.warn("Failed to read XConnect config: {}", CONFIG_NOT_FOUND);
172 + return false;
173 + }
174 + return config.getXconnects(cp.deviceId()).stream()
175 + .anyMatch(key -> config.getPorts(key).contains(cp.port()));
176 + }
177 +
178 + /**
179 + * Populates XConnect groups and flows for given key.
180 + *
181 + * @param key XConnect key
182 + * @param ports a set of ports to be cross-connected
183 + */
184 + private void populateXConnect(XConnectStoreKey key, Set<PortNumber> ports) {
185 + if (!srManager.mastershipService.isLocalMaster(key.deviceId())) {
186 + log.info("Abort populating XConnect {}: {}", key, NOT_MASTER);
187 + return;
188 + }
189 + populateFilter(key, ports);
190 + populateFwd(key, populateNext(key, ports));
191 + }
192 +
193 + /**
194 + * Populates filtering objectives for given XConnect.
195 + *
196 + * @param key XConnect store key
197 + * @param ports XConnect ports
198 + */
199 + private void populateFilter(XConnectStoreKey key, Set<PortNumber> ports) {
200 + ports.forEach(port -> {
201 + FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
202 + ObjectiveContext context = new DefaultObjectiveContext(
203 + (objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
204 + key, port),
205 + (objective, error) ->
206 + log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
207 + key, port, error));
208 + srManager.flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
209 + });
210 + }
211 +
212 + /**
213 + * Populates next objectives for given XConnect.
214 + *
215 + * @param key XConnect store key
216 + * @param ports XConnect ports
217 + */
218 + private NextObjective populateNext(XConnectStoreKey key, Set<PortNumber> ports) {
219 + NextObjective nextObj = null;
220 + if (xConnectNextObjStore.containsKey(key)) {
221 + nextObj = xConnectNextObjStore.get(key).value();
222 + log.debug("NextObj for {} found, id={}", key, nextObj.id());
223 + } else {
224 + NextObjective.Builder nextObjBuilder = nextObjBuilder(key, ports);
225 + ObjectiveContext nextContext = new NextObjContext(Objective.Operation.ADD, key);
226 + nextObj = nextObjBuilder.add(nextContext);
227 + srManager.flowObjectiveService.next(key.deviceId(), nextObj);
228 + xConnectNextObjStore.put(key, nextObj);
229 + log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
230 + }
231 + return nextObj;
232 + }
233 +
234 + /**
235 + * Populates forwarding objectives for given XConnect.
236 + *
237 + * @param key XConnect store key
238 + * @param nextObj next objective
239 + */
240 + private void populateFwd(XConnectStoreKey key, NextObjective nextObj) {
241 + ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
242 + ObjectiveContext fwdContext = new DefaultObjectiveContext(
243 + (objective) -> log.debug("XConnect FwdObj for {} populated", key),
244 + (objective, error) ->
245 + log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
246 + srManager.flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
247 + }
248 +
249 + /**
250 + * Revokes XConnect groups and flows for given key.
251 + *
252 + * @param key XConnect key
253 + * @param ports XConnect ports
254 + */
255 + private void revokeXConnect(XConnectStoreKey key, Set<PortNumber> ports) {
256 + if (!srManager.mastershipService.isLocalMaster(key.deviceId())) {
257 + log.info("Abort populating XConnect {}: {}", key, NOT_MASTER);
258 + return;
259 + }
260 +
261 + revokeFilter(key, ports);
262 + if (xConnectNextObjStore.containsKey(key)) {
263 + NextObjective nextObj = xConnectNextObjStore.get(key).value();
264 + revokeFwd(key, nextObj, null);
265 + revokeNext(key, nextObj, null);
266 + } else {
267 + log.warn("NextObj for {} does not exist in the store.", key);
268 + }
269 + }
270 +
271 + /**
272 + * Revokes filtering objectives for given XConnect.
273 + *
274 + * @param key XConnect store key
275 + * @param ports XConnect ports
276 + */
277 + private void revokeFilter(XConnectStoreKey key, Set<PortNumber> ports) {
278 + ports.forEach(port -> {
279 + FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port);
280 + ObjectiveContext context = new DefaultObjectiveContext(
281 + (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
282 + key, port),
283 + (objective, error) ->
284 + log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
285 + key, port, error));
286 + srManager.flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
287 + });
288 + }
289 +
290 + /**
291 + * Revokes next objectives for given XConnect.
292 + *
293 + * @param key XConnect store key
294 + * @param nextObj next objective
295 + * @param nextFuture completable future for this next objective operation
296 + */
297 + private void revokeNext(XConnectStoreKey key, NextObjective nextObj,
298 + CompletableFuture<ObjectiveError> nextFuture) {
299 + ObjectiveContext context = new ObjectiveContext() {
300 + @Override
301 + public void onSuccess(Objective objective) {
302 + log.debug("Previous NextObj for {} removed", key);
303 + if (nextFuture != null) {
304 + nextFuture.complete(null);
305 + }
306 + }
307 +
308 + @Override
309 + public void onError(Objective objective, ObjectiveError error) {
310 + log.warn("Failed to remove previous NextObj for {}: {}", key, error);
311 + if (nextFuture != null) {
312 + nextFuture.complete(error);
313 + }
314 + }
315 + };
316 + srManager.flowObjectiveService.next(key.deviceId(),
317 + (NextObjective) nextObj.copy().remove(context));
318 + xConnectNextObjStore.remove(key);
319 + }
320 +
321 + /**
322 + * Revokes forwarding objectives for given XConnect.
323 + *
324 + * @param key XConnect store key
325 + * @param nextObj next objective
326 + * @param fwdFuture completable future for this forwarding objective operation
327 + */
328 + private void revokeFwd(XConnectStoreKey key, NextObjective nextObj,
329 + CompletableFuture<ObjectiveError> fwdFuture) {
330 + ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextObj.id());
331 + ObjectiveContext context = new ObjectiveContext() {
332 + @Override
333 + public void onSuccess(Objective objective) {
334 + log.debug("Previous FwdObj for {} removed", key);
335 + if (fwdFuture != null) {
336 + fwdFuture.complete(null);
337 + }
338 + }
339 +
340 + @Override
341 + public void onError(Objective objective, ObjectiveError error) {
342 + log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
343 + if (fwdFuture != null) {
344 + fwdFuture.complete(error);
345 + }
346 + }
347 + };
348 + srManager.flowObjectiveService
349 + .forward(key.deviceId(), fwdObjBuilder.remove(context));
350 + }
351 +
352 + /**
353 + * Updates XConnect groups and flows for given key.
354 + *
355 + * @param key XConnect key
356 + * @param prevPorts previous XConnect ports
357 + * @param ports new XConnect ports
358 + */
359 + private void updateXConnect(XConnectStoreKey key, Set<PortNumber> prevPorts,
360 + Set<PortNumber> ports) {
361 + // remove old filter
362 + prevPorts.stream().filter(port -> !ports.contains(port)).forEach(port -> {
363 + revokeFilter(key, ImmutableSet.of(port));
364 + });
365 + // install new filter
366 + ports.stream().filter(port -> !prevPorts.contains(port)).forEach(port -> {
367 + populateFilter(key, ImmutableSet.of(port));
368 + });
369 +
370 + CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
371 + CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
372 +
373 + if (xConnectNextObjStore.containsKey(key)) {
374 + NextObjective nextObj = xConnectNextObjStore.get(key).value();
375 + revokeFwd(key, nextObj, fwdFuture);
376 +
377 + fwdFuture.thenAcceptAsync(fwdStatus -> {
378 + if (fwdStatus == null) {
379 + log.debug("Fwd removed. Now remove group {}", key);
380 + revokeNext(key, nextObj, nextFuture);
381 + }
382 + });
383 +
384 + nextFuture.thenAcceptAsync(nextStatus -> {
385 + if (nextStatus == null) {
386 + log.debug("Installing new group and flow for {}", key);
387 + populateFwd(key, populateNext(key, ports));
388 + }
389 + });
390 + } else {
391 + log.warn("NextObj for {} does not exist in the store.", key);
392 + }
393 + }
394 +
395 + /**
396 + * Remove all groups on given device.
397 + *
398 + * @param deviceId device ID
399 + */
400 + protected void removeDevice(DeviceId deviceId) {
401 + Iterator<Map.Entry<XConnectStoreKey, Versioned<NextObjective>>> itNextObj =
402 + xConnectNextObjStore.entrySet().iterator();
403 + while (itNextObj.hasNext()) {
404 + Map.Entry<XConnectStoreKey, Versioned<NextObjective>> entry = itNextObj.next();
405 + if (entry.getKey().deviceId().equals(deviceId)) {
406 + itNextObj.remove();
407 + }
408 + }
409 + }
410 +
411 + /**
412 + * Creates a next objective builder for XConnect.
413 + *
414 + * @param key XConnect key
415 + * @param ports set of XConnect ports
416 + * @return next objective builder
417 + */
418 + private NextObjective.Builder nextObjBuilder(XConnectStoreKey key, Set<PortNumber> ports) {
419 + int nextId = srManager.flowObjectiveService.allocateNextId();
420 + TrafficSelector metadata =
421 + DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
422 + NextObjective.Builder nextObjBuilder = DefaultNextObjective
423 + .builder().withId(nextId)
424 + .withType(NextObjective.Type.BROADCAST).fromApp(srManager.appId)
425 + .withMeta(metadata);
426 + ports.forEach(port -> {
427 + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
428 + tBuilder.setOutput(port);
429 + nextObjBuilder.addTreatment(tBuilder.build());
430 + });
431 + return nextObjBuilder;
432 + }
433 +
434 + /**
435 + * Creates a forwarding objective builder for XConnect.
436 + *
437 + * @param key XConnect key
438 + * @param nextId next ID of the broadcast group for this XConnect key
439 + * @return next objective builder
440 + */
441 + private ForwardingObjective.Builder fwdObjBuilder(XConnectStoreKey key, int nextId) {
442 + /*
443 + * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
444 + * as the VLAN cross-connect broadcast rules
445 + */
446 + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
447 + sbuilder.matchVlanId(key.vlanId());
448 + sbuilder.matchEthDst(MacAddress.NONE);
449 +
450 + ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
451 + fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
452 + .withSelector(sbuilder.build())
453 + .nextStep(nextId)
454 + .withPriority(SegmentRoutingService.XCONNECT_PRIORITY)
455 + .fromApp(srManager.appId)
456 + .makePermanent();
457 + return fob;
458 + }
459 +
460 + /**
461 + * Creates a filtering objective builder for XConnect.
462 + *
463 + * @param key XConnect key
464 + * @param port XConnect ports
465 + * @return next objective builder
466 + */
467 + private FilteringObjective.Builder filterObjBuilder(XConnectStoreKey key, PortNumber port) {
468 + FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
469 + fob.withKey(Criteria.matchInPort(port))
470 + .addCondition(Criteria.matchVlanId(key.vlanId()))
471 + .addCondition(Criteria.matchEthDst(MacAddress.NONE))
472 + .withPriority(SegmentRoutingService.XCONNECT_PRIORITY);
473 + return fob.permit().fromApp(srManager.appId);
474 + }
475 +
476 + // TODO: Lambda closure in DefaultObjectiveContext cannot be serialized properly
477 + // with Kryo 3.0.3. It will be fixed in 3.0.4. By then we can use
478 + // DefaultObjectiveContext again.
479 + private final class NextObjContext implements ObjectiveContext {
480 + Objective.Operation op;
481 + XConnectStoreKey key;
482 +
483 + private NextObjContext(Objective.Operation op, XConnectStoreKey key) {
484 + this.op = op;
485 + this.key = key;
486 + }
487 +
488 + @Override
489 + public void onSuccess(Objective objective) {
490 + log.debug("XConnect NextObj for {} {}ED", key, op);
491 + }
492 +
493 + @Override
494 + public void onError(Objective objective, ObjectiveError error) {
495 + log.warn("Failed to {} XConnect NextObj for {}: {}", op, key, error);
496 + }
497 + }
498 +}
...@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory; ...@@ -39,7 +39,6 @@ import org.slf4j.LoggerFactory;
39 import java.util.ArrayList; 39 import java.util.ArrayList;
40 import java.util.HashMap; 40 import java.util.HashMap;
41 import java.util.HashSet; 41 import java.util.HashSet;
42 -import java.util.LinkedList;
43 import java.util.List; 42 import java.util.List;
44 import java.util.Map; 43 import java.util.Map;
45 import java.util.Optional; 44 import java.util.Optional;
...@@ -58,7 +57,6 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -58,7 +57,6 @@ public class DeviceConfiguration implements DeviceProperties {
58 private static final Logger log = LoggerFactory.getLogger(DeviceConfiguration.class); 57 private static final Logger log = LoggerFactory.getLogger(DeviceConfiguration.class);
59 private final List<Integer> allSegmentIds = new ArrayList<>(); 58 private final List<Integer> allSegmentIds = new ArrayList<>();
60 private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>(); 59 private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
61 - private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
62 private ApplicationId appId; 60 private ApplicationId appId;
63 private NetworkConfigService cfgService; 61 private NetworkConfigService cfgService;
64 62
...@@ -148,28 +146,6 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -148,28 +146,6 @@ public class DeviceConfiguration implements DeviceProperties {
148 } 146 }
149 info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix()); 147 info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
150 }); 148 });
151 -
152 - // Extract VLAN cross-connect information
153 - // Do not setup cross-connect if VLAN is NONE
154 - if (vlanId.equals(VlanId.NONE)) {
155 - return;
156 - }
157 - List<ConnectPoint> connectPoints = xConnects.get(vlanId);
158 - if (connectPoints != null) {
159 - if (connectPoints.size() != 1) {
160 - log.warn("Cross-connect should only have two endpoints. Aborting.");
161 - return;
162 - }
163 - if (!connectPoints.get(0).deviceId().equals(connectPoint.deviceId())) {
164 - log.warn("Cross-connect endpoints must be on the same switch. Aborting.");
165 - return;
166 - }
167 - connectPoints.add(connectPoint);
168 - } else {
169 - connectPoints = new LinkedList<>();
170 - connectPoints.add(connectPoint);
171 - xConnects.put(vlanId, connectPoints);
172 - }
173 } 149 }
174 }); 150 });
175 }); 151 });
...@@ -298,11 +274,6 @@ public class DeviceConfiguration implements DeviceProperties { ...@@ -298,11 +274,6 @@ public class DeviceConfiguration implements DeviceProperties {
298 return subnetPortMap; 274 return subnetPortMap;
299 } 275 }
300 276
301 - @Override
302 - public Map<VlanId, List<ConnectPoint>> getXConnects() {
303 - return xConnects;
304 - }
305 -
306 /** 277 /**
307 * Returns the device identifier or data plane identifier (dpid) 278 * Returns the device identifier or data plane identifier (dpid)
308 * of a segment router given its segment id. 279 * of a segment router given its segment id.
......
...@@ -21,8 +21,6 @@ import java.util.Map; ...@@ -21,8 +21,6 @@ import java.util.Map;
21 import org.onlab.packet.Ip4Address; 21 import org.onlab.packet.Ip4Address;
22 import org.onlab.packet.Ip4Prefix; 22 import org.onlab.packet.Ip4Prefix;
23 import org.onlab.packet.MacAddress; 23 import org.onlab.packet.MacAddress;
24 -import org.onlab.packet.VlanId;
25 -import org.onosproject.net.ConnectPoint;
26 import org.onosproject.net.DeviceId; 24 import org.onosproject.net.DeviceId;
27 import org.onosproject.net.PortNumber; 25 import org.onosproject.net.PortNumber;
28 26
...@@ -97,11 +95,4 @@ public interface DeviceProperties { ...@@ -97,11 +95,4 @@ public interface DeviceProperties {
97 */ 95 */
98 Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId) 96 Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId)
99 throws DeviceConfigNotFoundException; 97 throws DeviceConfigNotFoundException;
100 -
101 - /**
102 - * Returns the VLAN cross-connect configuration.
103 - *
104 - * @return A map of that maps VLAN ID to a list of cross-connect endpoints
105 - */
106 - Map<VlanId, List<ConnectPoint>> getXConnects();
107 } 98 }
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.segmentrouting.config;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.node.ObjectNode;
21 +import com.google.common.collect.ImmutableSet;
22 +import org.onlab.packet.VlanId;
23 +import org.onosproject.core.ApplicationId;
24 +import org.onosproject.net.DeviceId;
25 +import org.onosproject.net.PortNumber;
26 +import org.onosproject.net.config.Config;
27 +import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
28 +
29 +import java.util.Set;
30 +
31 +import static com.google.common.base.Preconditions.checkArgument;
32 +
33 +/**
34 + * Configuration object for cross-connect.
35 + */
36 +public class XConnectConfig extends Config<ApplicationId> {
37 + private static final String VLAN = "vlan";
38 + private static final String PORTS = "ports";
39 + private static final String NAME = "name"; // dummy field for naming
40 +
41 + private static final String UNEXPECTED_FIELD_NAME = "Unexpected field name";
42 +
43 + @Override
44 + public boolean isValid() {
45 + try {
46 + getXconnects().forEach(this::getPorts);
47 + } catch (IllegalArgumentException e) {
48 + return false;
49 + }
50 + return true;
51 + }
52 +
53 + /**
54 + * Returns all xconnect keys.
55 + *
56 + * @return all keys (device/vlan pairs)
57 + * @throws IllegalArgumentException if wrong format
58 + */
59 + public Set<XConnectStoreKey> getXconnects() {
60 + ImmutableSet.Builder<XConnectStoreKey> builder = ImmutableSet.builder();
61 + object.fields().forEachRemaining(entry -> {
62 + DeviceId deviceId = DeviceId.deviceId(entry.getKey());
63 + builder.addAll(getXconnects(deviceId));
64 + });
65 + return builder.build();
66 + }
67 +
68 + /**
69 + * Returns xconnect keys of given device.
70 + *
71 + * @param deviceId ID of the device from which we want to get XConnect info
72 + * @return xconnect keys (device/vlan pairs) of given device
73 + * @throws IllegalArgumentException if wrong format
74 + */
75 + public Set<XConnectStoreKey> getXconnects(DeviceId deviceId) {
76 + ImmutableSet.Builder<XConnectStoreKey> builder = ImmutableSet.builder();
77 + JsonNode vlanPortPair = object.get(deviceId.toString());
78 + if (vlanPortPair != null) {
79 + vlanPortPair.forEach(jsonNode -> {
80 + if (!hasOnlyFields((ObjectNode) jsonNode, VLAN, PORTS, NAME)) {
81 + throw new IllegalArgumentException(UNEXPECTED_FIELD_NAME);
82 + }
83 + VlanId vlanId = VlanId.vlanId((short) jsonNode.get(VLAN).asInt());
84 + builder.add(new XConnectStoreKey(deviceId, vlanId));
85 + });
86 + }
87 + return builder.build();
88 + }
89 +
90 + /**
91 + * Returns ports of given xconnect key.
92 + *
93 + * @param xconnect xconnect key
94 + * @return set of two ports associated with given xconnect key
95 + * @throws IllegalArgumentException if wrong format
96 + */
97 + public Set<PortNumber> getPorts(XConnectStoreKey xconnect) {
98 + ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
99 + object.get(xconnect.deviceId().toString()).forEach(vlanPortsPair -> {
100 + if (xconnect.vlanId().toShort() == vlanPortsPair.get(VLAN).asInt()) {
101 + int portCount = vlanPortsPair.get(PORTS).size();
102 + checkArgument(portCount == 2,
103 + "Expect 2 ports but found " + portCount + " on " + xconnect);
104 + vlanPortsPair.get(PORTS).forEach(portNode -> {
105 + builder.add(PortNumber.portNumber(portNode.asInt()));
106 + });
107 + }
108 + });
109 + return builder.build();
110 + }
111 +}
...@@ -35,7 +35,6 @@ import org.onlab.packet.MplsLabel; ...@@ -35,7 +35,6 @@ import org.onlab.packet.MplsLabel;
35 import org.onlab.packet.VlanId; 35 import org.onlab.packet.VlanId;
36 import org.onlab.util.KryoNamespace; 36 import org.onlab.util.KryoNamespace;
37 import org.onosproject.core.ApplicationId; 37 import org.onosproject.core.ApplicationId;
38 -import org.onosproject.net.ConnectPoint;
39 import org.onosproject.net.DeviceId; 38 import org.onosproject.net.DeviceId;
40 import org.onosproject.net.Link; 39 import org.onosproject.net.Link;
41 import org.onosproject.net.PortNumber; 40 import org.onosproject.net.PortNumber;
...@@ -55,7 +54,6 @@ import org.onosproject.segmentrouting.config.DeviceProperties; ...@@ -55,7 +54,6 @@ import org.onosproject.segmentrouting.config.DeviceProperties;
55 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey; 54 import org.onosproject.segmentrouting.storekey.NeighborSetNextObjectiveStoreKey;
56 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey; 55 import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
57 import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey; 56 import org.onosproject.segmentrouting.storekey.SubnetNextObjectiveStoreKey;
58 -import org.onosproject.segmentrouting.storekey.XConnectNextObjectiveStoreKey;
59 import org.onosproject.store.service.EventuallyConsistentMap; 57 import org.onosproject.store.service.EventuallyConsistentMap;
60 import org.slf4j.Logger; 58 import org.slf4j.Logger;
61 59
...@@ -89,8 +87,6 @@ public class DefaultGroupHandler { ...@@ -89,8 +87,6 @@ public class DefaultGroupHandler {
89 subnetNextObjStore = null; 87 subnetNextObjStore = null;
90 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer> 88 protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
91 portNextObjStore = null; 89 portNextObjStore = null;
92 - protected EventuallyConsistentMap<XConnectNextObjectiveStoreKey, Integer>
93 - xConnectNextObjStore = null;
94 private SegmentRoutingManager srManager; 90 private SegmentRoutingManager srManager;
95 91
96 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder() 92 protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
...@@ -123,7 +119,6 @@ public class DefaultGroupHandler { ...@@ -123,7 +119,6 @@ public class DefaultGroupHandler {
123 this.nsNextObjStore = srManager.nsNextObjStore; 119 this.nsNextObjStore = srManager.nsNextObjStore;
124 this.subnetNextObjStore = srManager.subnetNextObjStore; 120 this.subnetNextObjStore = srManager.subnetNextObjStore;
125 this.portNextObjStore = srManager.portNextObjStore; 121 this.portNextObjStore = srManager.portNextObjStore;
126 - this.xConnectNextObjStore = srManager.xConnectNextObjStore;
127 this.srManager = srManager; 122 this.srManager = srManager;
128 123
129 populateNeighborMaps(); 124 populateNeighborMaps();
...@@ -471,32 +466,6 @@ public class DefaultGroupHandler { ...@@ -471,32 +466,6 @@ public class DefaultGroupHandler {
471 } 466 }
472 467
473 /** 468 /**
474 - * Returns the next objective ID of type broadcast associated with the VLAN
475 - * cross-connection.
476 - *
477 - * @param vlanId VLAN ID for the cross-connection
478 - * @return int if found or created, -1 if there are errors during the
479 - * creation of the next objective
480 - */
481 - public int getXConnectNextObjectiveId(VlanId vlanId) {
482 - Integer nextId = xConnectNextObjStore
483 - .get(new XConnectNextObjectiveStoreKey(deviceId, vlanId));
484 - if (nextId == null) {
485 - log.trace("getXConnectNextObjectiveId: Next objective id "
486 - + "not found for device {} and vlan {}. Creating", deviceId, vlanId);
487 - createGroupsForXConnect(deviceId);
488 - nextId = xConnectNextObjStore.get(
489 - new XConnectNextObjectiveStoreKey(deviceId, vlanId));
490 - if (nextId == null) {
491 - log.warn("getXConnectNextObjectiveId: Next objective id "
492 - + "not found for device {} and vlan {}.", deviceId, vlanId);
493 - return -1;
494 - }
495 - }
496 - return nextId;
497 - }
498 -
499 - /**
500 * Checks if the next objective ID (group) for the neighbor set exists or not. 469 * Checks if the next objective ID (group) for the neighbor set exists or not.
501 * 470 *
502 * @param ns neighbor set to check 471 * @param ns neighbor set to check
...@@ -743,55 +712,6 @@ public class DefaultGroupHandler { ...@@ -743,55 +712,6 @@ public class DefaultGroupHandler {
743 } 712 }
744 713
745 /** 714 /**
746 - * Creates broadcast groups for VLAN cross-connect ports.
747 - *
748 - * @param deviceId the DPID of the switch
749 - */
750 - public void createGroupsForXConnect(DeviceId deviceId) {
751 - Map<VlanId, List<ConnectPoint>> xConnectsForDevice = deviceConfig.getXConnects();
752 -
753 - xConnectsForDevice.forEach((vlanId, connectPoints) -> {
754 - // Only proceed the xConnect for given device
755 - for (ConnectPoint connectPoint : connectPoints) {
756 - if (!connectPoint.deviceId().equals(deviceId)) {
757 - return;
758 - }
759 - }
760 -
761 - // Check if the next obj is already in the store
762 - XConnectNextObjectiveStoreKey key =
763 - new XConnectNextObjectiveStoreKey(deviceId, vlanId);
764 - if (xConnectNextObjStore.containsKey(key)) {
765 - log.debug("Cross-connect Broadcast group for device {} and vlanId {} exists",
766 - deviceId, vlanId);
767 - return;
768 - }
769 -
770 - TrafficSelector metadata =
771 - DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
772 - int nextId = flowObjectiveService.allocateNextId();
773 -
774 - NextObjective.Builder nextObjBuilder = DefaultNextObjective
775 - .builder().withId(nextId)
776 - .withType(NextObjective.Type.BROADCAST).fromApp(appId)
777 - .withMeta(metadata);
778 -
779 - connectPoints.forEach(connectPoint -> {
780 - TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
781 - tBuilder.setOutput(connectPoint.port());
782 - nextObjBuilder.addTreatment(tBuilder.build());
783 - });
784 -
785 - NextObjective nextObj = nextObjBuilder.add();
786 - flowObjectiveService.next(deviceId, nextObj);
787 - log.debug("createGroupsForXConnect: Submited next objective {} in device {}",
788 - nextId, deviceId);
789 - xConnectNextObjStore.put(key, nextId);
790 - });
791 - }
792 -
793 -
794 - /**
795 * Create simple next objective for a single port. The treatments can include 715 * Create simple next objective for a single port. The treatments can include
796 * all outgoing actions that need to happen on the packet. 716 * all outgoing actions that need to happen on the packet.
797 * 717 *
......
...@@ -24,7 +24,7 @@ import java.util.Objects; ...@@ -24,7 +24,7 @@ import java.util.Objects;
24 /** 24 /**
25 * Key of VLAN cross-connect next objective store. 25 * Key of VLAN cross-connect next objective store.
26 */ 26 */
27 -public class XConnectNextObjectiveStoreKey { 27 +public class XConnectStoreKey {
28 private final DeviceId deviceId; 28 private final DeviceId deviceId;
29 private final VlanId vlanId; 29 private final VlanId vlanId;
30 30
...@@ -34,7 +34,7 @@ public class XConnectNextObjectiveStoreKey { ...@@ -34,7 +34,7 @@ public class XConnectNextObjectiveStoreKey {
34 * @param deviceId device ID of the VLAN cross-connection 34 * @param deviceId device ID of the VLAN cross-connection
35 * @param vlanId VLAN ID of the VLAN cross-connection 35 * @param vlanId VLAN ID of the VLAN cross-connection
36 */ 36 */
37 - public XConnectNextObjectiveStoreKey(DeviceId deviceId, VlanId vlanId) { 37 + public XConnectStoreKey(DeviceId deviceId, VlanId vlanId) {
38 this.deviceId = deviceId; 38 this.deviceId = deviceId;
39 this.vlanId = vlanId; 39 this.vlanId = vlanId;
40 } 40 }
...@@ -62,11 +62,11 @@ public class XConnectNextObjectiveStoreKey { ...@@ -62,11 +62,11 @@ public class XConnectNextObjectiveStoreKey {
62 if (this == o) { 62 if (this == o) {
63 return true; 63 return true;
64 } 64 }
65 - if (!(o instanceof XConnectNextObjectiveStoreKey)) { 65 + if (!(o instanceof XConnectStoreKey)) {
66 return false; 66 return false;
67 } 67 }
68 - XConnectNextObjectiveStoreKey that = 68 + XConnectStoreKey that =
69 - (XConnectNextObjectiveStoreKey) o; 69 + (XConnectStoreKey) o;
70 return (Objects.equals(this.deviceId, that.deviceId) && 70 return (Objects.equals(this.deviceId, that.deviceId) &&
71 Objects.equals(this.vlanId, that.vlanId)); 71 Objects.equals(this.vlanId, that.vlanId));
72 } 72 }
......
...@@ -41,9 +41,6 @@ import static org.junit.Assert.*; ...@@ -41,9 +41,6 @@ import static org.junit.Assert.*;
41 * Tests for class {@link SegmentRoutingAppConfig}. 41 * Tests for class {@link SegmentRoutingAppConfig}.
42 */ 42 */
43 public class SegmentRoutingAppConfigTest { 43 public class SegmentRoutingAppConfigTest {
44 - private static final ApplicationId APP_ID =
45 - new TestApplicationId(SegmentRoutingManager.SR_APP_ID);
46 -
47 private SegmentRoutingAppConfig config; 44 private SegmentRoutingAppConfig config;
48 private SegmentRoutingAppConfig invalidConfig; 45 private SegmentRoutingAppConfig invalidConfig;
49 46
...@@ -67,12 +64,12 @@ public class SegmentRoutingAppConfigTest { ...@@ -67,12 +64,12 @@ public class SegmentRoutingAppConfigTest {
67 @Before 64 @Before
68 public void setUp() throws Exception { 65 public void setUp() throws Exception {
69 InputStream jsonStream = SegmentRoutingAppConfigTest.class 66 InputStream jsonStream = SegmentRoutingAppConfigTest.class
70 - .getResourceAsStream("/sr-app-config.json"); 67 + .getResourceAsStream("/app.json");
71 InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class 68 InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class
72 - .getResourceAsStream("/sr-app-config-invalid.json"); 69 + .getResourceAsStream("/app-invalid.json");
73 70
74 - ApplicationId subject = APP_ID;
75 String key = SegmentRoutingManager.SR_APP_ID; 71 String key = SegmentRoutingManager.SR_APP_ID;
72 + ApplicationId subject = new TestApplicationId(key);
76 ObjectMapper mapper = new ObjectMapper(); 73 ObjectMapper mapper = new ObjectMapper();
77 JsonNode jsonNode = mapper.readTree(jsonStream); 74 JsonNode jsonNode = mapper.readTree(jsonStream);
78 JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream); 75 JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
......
...@@ -47,7 +47,7 @@ public class SegmentRoutingDeviceConfigTest { ...@@ -47,7 +47,7 @@ public class SegmentRoutingDeviceConfigTest {
47 @Before 47 @Before
48 public void setUp() throws Exception { 48 public void setUp() throws Exception {
49 InputStream jsonStream = SegmentRoutingDeviceConfigTest.class 49 InputStream jsonStream = SegmentRoutingDeviceConfigTest.class
50 - .getResourceAsStream("/sr-device-config.json"); 50 + .getResourceAsStream("/device.json");
51 51
52 adjacencySids1 = new HashMap<>(); 52 adjacencySids1 = new HashMap<>();
53 Set<Integer> ports1 = new HashSet<>(); 53 Set<Integer> ports1 = new HashSet<>();
......
1 +/*
2 + * Copyright 2016-present Open Networking Laboratory
3 + *
4 + * Licensed under the Apache License, Version 2.0 (the "License");
5 + * you may not use this file except in compliance with the License.
6 + * You may obtain a copy of the License at
7 + *
8 + * http://www.apache.org/licenses/LICENSE-2.0
9 + *
10 + * Unless required by applicable law or agreed to in writing, software
11 + * distributed under the License is distributed on an "AS IS" BASIS,
12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 + * See the License for the specific language governing permissions and
14 + * limitations under the License.
15 + */
16 +
17 +package org.onosproject.segmentrouting.config;
18 +
19 +import com.fasterxml.jackson.databind.JsonNode;
20 +import com.fasterxml.jackson.databind.ObjectMapper;
21 +import org.junit.Before;
22 +import org.junit.Test;
23 +import org.onlab.packet.VlanId;
24 +import org.onosproject.TestApplicationId;
25 +import org.onosproject.core.ApplicationId;
26 +import org.onosproject.net.DeviceId;
27 +import org.onosproject.net.PortNumber;
28 +import org.onosproject.net.config.Config;
29 +import org.onosproject.net.config.ConfigApplyDelegate;
30 +import org.onosproject.segmentrouting.SegmentRoutingManager;
31 +import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
32 +import java.io.InputStream;
33 +import java.util.Set;
34 +import static org.junit.Assert.assertFalse;
35 +import static org.junit.Assert.assertTrue;
36 +import static org.junit.Assert.assertThat;
37 +import static org.hamcrest.Matchers.is;
38 +
39 +/**
40 + * Tests for class {@link XConnectConfig}.
41 + */
42 +public class XConnectConfigTest {
43 + private static final DeviceId DEV1 = DeviceId.deviceId("of:0000000000000001");
44 + private static final DeviceId DEV2 = DeviceId.deviceId("of:0000000000000002");
45 + private static final VlanId VLAN10 = VlanId.vlanId((short) 10);
46 + private static final VlanId VLAN20 = VlanId.vlanId((short) 20);
47 + private static final PortNumber PORT3 = PortNumber.portNumber(3);
48 + private static final PortNumber PORT4 = PortNumber.portNumber(4);
49 + private static final PortNumber PORT5 = PortNumber.portNumber(5);
50 + private static final XConnectStoreKey KEY1 = new XConnectStoreKey(DEV1, VLAN10);
51 + private static final XConnectStoreKey KEY2 = new XConnectStoreKey(DEV2, VLAN10);
52 + private static final XConnectStoreKey KEY3 = new XConnectStoreKey(DEV2, VLAN20);
53 + private static final XConnectStoreKey KEY4 = new XConnectStoreKey(DEV2, VlanId.NONE);
54 +
55 + private XConnectConfig config;
56 + private XConnectConfig invalidConfig;
57 +
58 + @Before
59 + public void setUp() throws Exception {
60 + InputStream jsonStream = SegmentRoutingAppConfigTest.class
61 + .getResourceAsStream("/xconnect.json");
62 + InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class
63 + .getResourceAsStream("/xconnect-invalid.json");
64 +
65 + String key = SegmentRoutingManager.SR_APP_ID;
66 + ApplicationId subject = new TestApplicationId(key);
67 + ObjectMapper mapper = new ObjectMapper();
68 + JsonNode jsonNode = mapper.readTree(jsonStream);
69 + JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream);
70 + ConfigApplyDelegate delegate = new XConnectConfigTest.MockDelegate();
71 +
72 + config = new XConnectConfig();
73 + config.init(subject, key, jsonNode, mapper, delegate);
74 + invalidConfig = new XConnectConfig();
75 + invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate);
76 + }
77 +
78 + /**
79 + * Tests config validity.
80 + */
81 + @Test
82 + public void testIsValid() {
83 + assertTrue(config.isValid());
84 + assertFalse(invalidConfig.isValid());
85 + }
86 +
87 + /**
88 + * Tests getXconnects.
89 + */
90 + @Test
91 + public void testGetXconnects() {
92 + Set<XConnectStoreKey> xconnects = config.getXconnects();
93 + assertThat(xconnects.size(), is(3));
94 + assertTrue(xconnects.contains(KEY1));
95 + assertTrue(xconnects.contains(KEY2));
96 + assertTrue(xconnects.contains(KEY3));
97 + assertFalse(xconnects.contains(KEY4));
98 + }
99 +
100 + /**
101 + * Tests getPorts.
102 + */
103 + @Test
104 + public void testGetPorts() {
105 + Set<PortNumber> ports;
106 +
107 + ports = config.getPorts(KEY1);
108 + assertThat(ports.size(), is(2));
109 + assertTrue(ports.contains(PORT3));
110 + assertTrue(ports.contains(PORT4));
111 +
112 + ports = config.getPorts(KEY2);
113 + assertThat(ports.size(), is(2));
114 + assertTrue(ports.contains(PORT3));
115 + assertTrue(ports.contains(PORT4));
116 +
117 + ports = config.getPorts(KEY3);
118 + assertThat(ports.size(), is(2));
119 + assertTrue(ports.contains(PORT4));
120 + assertTrue(ports.contains(PORT5));
121 + }
122 +
123 + private class MockDelegate implements ConfigApplyDelegate {
124 + @Override
125 + public void onApply(Config config) {
126 + }
127 + }
128 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "of:0000000000000001": [
3 + {
4 + "vlan": 10,
5 + "ports": [3, 4]
6 + }
7 + ],
8 + "of:0000000000000002": [
9 + {
10 + "vlan": 10,
11 + "ports": [3, 4]
12 + },
13 + {
14 + "vlan": 20,
15 + "ports": [4, 5, 6]
16 + }
17 + ]
18 +}
...\ No newline at end of file ...\ No newline at end of file
1 +{
2 + "of:0000000000000001": [
3 + {
4 + "vlan": 10,
5 + "ports": [3, 4],
6 + "name": "OLT1"
7 + }
8 + ],
9 + "of:0000000000000002": [
10 + {
11 + "vlan": 10,
12 + "ports": [3, 4]
13 + },
14 + {
15 + "vlan": 20,
16 + "ports": [4, 5]
17 + }
18 + ]
19 +}
...\ No newline at end of file ...\ No newline at end of file
...@@ -34,11 +34,16 @@ public enum ObjectiveError { ...@@ -34,11 +34,16 @@ public enum ObjectiveError {
34 FLOWINSTALLATIONFAILED, 34 FLOWINSTALLATIONFAILED,
35 35
36 /** 36 /**
37 - * THe group installation for this objective failed. 37 + * The group installation for this objective failed.
38 */ 38 */
39 GROUPINSTALLATIONFAILED, 39 GROUPINSTALLATIONFAILED,
40 40
41 /** 41 /**
42 + * The group removal for this objective failed.
43 + */
44 + GROUPREMOVALFAILED,
45 +
46 + /**
42 * The group was reported as installed but is missing. 47 * The group was reported as installed but is missing.
43 */ 48 */
44 GROUPMISSING, 49 GROUPMISSING,
......
...@@ -114,7 +114,8 @@ public class Ofdpa2GroupHandler { ...@@ -114,7 +114,8 @@ public class Ofdpa2GroupHandler {
114 114
115 protected DeviceId deviceId; 115 protected DeviceId deviceId;
116 private FlowObjectiveStore flowObjectiveStore; 116 private FlowObjectiveStore flowObjectiveStore;
117 - private Cache<GroupKey, List<OfdpaNextGroup>> pendingNextObjectives; 117 + private Cache<GroupKey, List<OfdpaNextGroup>> pendingAddNextObjectives;
118 + private Cache<NextObjective, List<GroupKey>> pendingRemoveNextObjectives;
118 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups; 119 private ConcurrentHashMap<GroupKey, Set<GroupChainElem>> pendingGroups;
119 private ScheduledExecutorService groupChecker = 120 private ScheduledExecutorService groupChecker =
120 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa2-%d", log)); 121 Executors.newScheduledThreadPool(2, groupedThreads("onos/pipeliner", "ofdpa2-%d", log));
...@@ -134,7 +135,7 @@ public class Ofdpa2GroupHandler { ...@@ -134,7 +135,7 @@ public class Ofdpa2GroupHandler {
134 this.storageService = serviceDirectory.get(StorageService.class); 135 this.storageService = serviceDirectory.get(StorageService.class);
135 this.nextIndex = storageService.getAtomicCounter("group-id-index-counter"); 136 this.nextIndex = storageService.getAtomicCounter("group-id-index-counter");
136 137
137 - pendingNextObjectives = CacheBuilder.newBuilder() 138 + pendingAddNextObjectives = CacheBuilder.newBuilder()
138 .expireAfterWrite(20, TimeUnit.SECONDS) 139 .expireAfterWrite(20, TimeUnit.SECONDS)
139 .removalListener(( 140 .removalListener((
140 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> { 141 RemovalNotification<GroupKey, List<OfdpaNextGroup>> notification) -> {
...@@ -142,7 +143,16 @@ public class Ofdpa2GroupHandler { ...@@ -142,7 +143,16 @@ public class Ofdpa2GroupHandler {
142 notification.getValue().forEach(ofdpaNextGrp -> 143 notification.getValue().forEach(ofdpaNextGrp ->
143 Ofdpa2Pipeline.fail(ofdpaNextGrp.nextObj, 144 Ofdpa2Pipeline.fail(ofdpaNextGrp.nextObj,
144 ObjectiveError.GROUPINSTALLATIONFAILED)); 145 ObjectiveError.GROUPINSTALLATIONFAILED));
146 + }
147 + }).build();
145 148
149 + pendingRemoveNextObjectives = CacheBuilder.newBuilder()
150 + .expireAfterWrite(20, TimeUnit.SECONDS)
151 + .removalListener((
152 + RemovalNotification<NextObjective, List<GroupKey>> notification) -> {
153 + if (notification.getCause() == RemovalCause.EXPIRED) {
154 + Ofdpa2Pipeline.fail(notification.getKey(),
155 + ObjectiveError.GROUPREMOVALFAILED);
146 } 156 }
147 }).build(); 157 }).build();
148 pendingGroups = new ConcurrentHashMap<>(); 158 pendingGroups = new ConcurrentHashMap<>();
...@@ -1012,6 +1022,11 @@ public class Ofdpa2GroupHandler { ...@@ -1012,6 +1022,11 @@ public class Ofdpa2GroupHandler {
1012 */ 1022 */
1013 protected void removeGroup(NextObjective nextObjective, NextGroup next) { 1023 protected void removeGroup(NextObjective nextObjective, NextGroup next) {
1014 List<Deque<GroupKey>> allgkeys = Ofdpa2Pipeline.appKryo.deserialize(next.data()); 1024 List<Deque<GroupKey>> allgkeys = Ofdpa2Pipeline.appKryo.deserialize(next.data());
1025 +
1026 + List<GroupKey> groupKeys = allgkeys.stream()
1027 + .map(Deque::getFirst).collect(Collectors.toList());
1028 + pendingRemoveNextObjectives.put(nextObjective, groupKeys);
1029 +
1015 allgkeys.forEach(groupChain -> groupChain.forEach(groupKey -> 1030 allgkeys.forEach(groupChain -> groupChain.forEach(groupKey ->
1016 groupService.removeGroup(deviceId, groupKey, nextObjective.appId()))); 1031 groupService.removeGroup(deviceId, groupKey, nextObjective.appId())));
1017 flowObjectiveStore.removeNextGroup(nextObjective.id()); 1032 flowObjectiveStore.removeNextGroup(nextObjective.id());
...@@ -1024,7 +1039,7 @@ public class Ofdpa2GroupHandler { ...@@ -1024,7 +1039,7 @@ public class Ofdpa2GroupHandler {
1024 private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) { 1039 private void updatePendingNextObjective(GroupKey key, OfdpaNextGroup value) {
1025 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>(); 1040 List<OfdpaNextGroup> nextList = new CopyOnWriteArrayList<OfdpaNextGroup>();
1026 nextList.add(value); 1041 nextList.add(value);
1027 - List<OfdpaNextGroup> ret = pendingNextObjectives.asMap() 1042 + List<OfdpaNextGroup> ret = pendingAddNextObjectives.asMap()
1028 .putIfAbsent(key, nextList); 1043 .putIfAbsent(key, nextList);
1029 if (ret != null) { 1044 if (ret != null) {
1030 ret.add(value); 1045 ret.add(value);
...@@ -1079,13 +1094,13 @@ public class Ofdpa2GroupHandler { ...@@ -1079,13 +1094,13 @@ public class Ofdpa2GroupHandler {
1079 Set<GroupKey> keys = pendingGroups.keySet().stream() 1094 Set<GroupKey> keys = pendingGroups.keySet().stream()
1080 .filter(key -> groupService.getGroup(deviceId, key) != null) 1095 .filter(key -> groupService.getGroup(deviceId, key) != null)
1081 .collect(Collectors.toSet()); 1096 .collect(Collectors.toSet());
1082 - Set<GroupKey> otherkeys = pendingNextObjectives.asMap().keySet().stream() 1097 + Set<GroupKey> otherkeys = pendingAddNextObjectives.asMap().keySet().stream()
1083 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null) 1098 .filter(otherkey -> groupService.getGroup(deviceId, otherkey) != null)
1084 .collect(Collectors.toSet()); 1099 .collect(Collectors.toSet());
1085 keys.addAll(otherkeys); 1100 keys.addAll(otherkeys);
1086 1101
1087 keys.stream().forEach(key -> 1102 keys.stream().forEach(key ->
1088 - processPendingGroupsOrNextObjectives(key, false)); 1103 + processPendingAddGroupsOrNextObjs(key, false));
1089 } 1104 }
1090 } 1105 }
1091 1106
...@@ -1093,14 +1108,20 @@ public class Ofdpa2GroupHandler { ...@@ -1093,14 +1108,20 @@ public class Ofdpa2GroupHandler {
1093 @Override 1108 @Override
1094 public void event(GroupEvent event) { 1109 public void event(GroupEvent event) {
1095 log.trace("received group event of type {}", event.type()); 1110 log.trace("received group event of type {}", event.type());
1096 - if (event.type() == GroupEvent.Type.GROUP_ADDED) { 1111 + switch (event.type()) {
1097 - GroupKey key = event.subject().appCookie(); 1112 + case GROUP_ADDED:
1098 - processPendingGroupsOrNextObjectives(key, true); 1113 + processPendingAddGroupsOrNextObjs(event.subject().appCookie(), true);
1114 + break;
1115 + case GROUP_REMOVED:
1116 + processPendingRemoveNextObjs(event.subject().appCookie());
1117 + break;
1118 + default:
1119 + break;
1099 } 1120 }
1100 } 1121 }
1101 } 1122 }
1102 1123
1103 - private void processPendingGroupsOrNextObjectives(GroupKey key, boolean added) { 1124 + private void processPendingAddGroupsOrNextObjs(GroupKey key, boolean added) {
1104 //first check for group chain 1125 //first check for group chain
1105 Set<GroupChainElem> gceSet = pendingGroups.remove(key); 1126 Set<GroupChainElem> gceSet = pendingGroups.remove(key);
1106 if (gceSet != null) { 1127 if (gceSet != null) {
...@@ -1114,9 +1135,9 @@ public class Ofdpa2GroupHandler { ...@@ -1114,9 +1135,9 @@ public class Ofdpa2GroupHandler {
1114 } 1135 }
1115 } else { 1136 } else {
1116 // otherwise chain complete - check for waiting nextObjectives 1137 // otherwise chain complete - check for waiting nextObjectives
1117 - List<OfdpaNextGroup> nextGrpList = pendingNextObjectives.getIfPresent(key); 1138 + List<OfdpaNextGroup> nextGrpList = pendingAddNextObjectives.getIfPresent(key);
1118 if (nextGrpList != null) { 1139 if (nextGrpList != null) {
1119 - pendingNextObjectives.invalidate(key); 1140 + pendingAddNextObjectives.invalidate(key);
1120 nextGrpList.forEach(nextGrp -> { 1141 nextGrpList.forEach(nextGrp -> {
1121 log.debug("Group service {} group key {} in device:{}. " 1142 log.debug("Group service {} group key {} in device:{}. "
1122 + "Done implementing next objective: {} <<-->> gid:0x{}", 1143 + "Done implementing next objective: {} <<-->> gid:0x{}",
...@@ -1137,6 +1158,17 @@ public class Ofdpa2GroupHandler { ...@@ -1137,6 +1158,17 @@ public class Ofdpa2GroupHandler {
1137 } 1158 }
1138 } 1159 }
1139 1160
1161 + private void processPendingRemoveNextObjs(GroupKey key) {
1162 + pendingRemoveNextObjectives.asMap().forEach((nextObjective, groupKeys) -> {
1163 + if (groupKeys.isEmpty()) {
1164 + pendingRemoveNextObjectives.invalidate(nextObjective);
1165 + Ofdpa2Pipeline.pass(nextObjective);
1166 + } else {
1167 + groupKeys.remove(key);
1168 + }
1169 + });
1170 + }
1171 +
1140 protected int getNextAvailableIndex() { 1172 protected int getNextAvailableIndex() {
1141 return (int) nextIndex.incrementAndGet(); 1173 return (int) nextIndex.incrementAndGet();
1142 } 1174 }
......