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
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 : | ... | ... |
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
| ... | @@ -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 | } | ... | ... |
apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/XConnectConfig.java
0 → 100644
| 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<>(); | ... | ... |
apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/XConnectConfigTest.java
0 → 100644
| 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 | + "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 | } | ... | ... |
-
Please register or login to post a comment