Charles Chan

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 350 additions and 243 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 :
......
...@@ -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 }
......