Committed by
Gerrit Code Review
CORD-348 multicast support in SegmentRouting and vRouter
In this submission:
* Setup/teardown multicast route according to SinkAdded/SinkRemoved event
- ingressVlan and egressVlan is configurable through network config
* Change behavior of OFDPA VLAN assignment
- Always use the VLAN in metadata if present
* Bugfix of writing immutable object
NOT in this submission (coming soon):
* Error handling (e.g. link/device failure recovery)
Change-Id: I9be11af04eb2d6456b865c7e59e96cc02370f846
Showing
17 changed files
with
1043 additions
and
96 deletions
| ... | @@ -29,11 +29,13 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; | ... | @@ -29,11 +29,13 @@ import org.apache.felix.scr.annotations.ReferenceCardinality; |
| 29 | import org.onlab.packet.Ethernet; | 29 | import org.onlab.packet.Ethernet; |
| 30 | import org.onlab.packet.IpAddress; | 30 | import org.onlab.packet.IpAddress; |
| 31 | import org.onlab.packet.IpPrefix; | 31 | import org.onlab.packet.IpPrefix; |
| 32 | +import org.onlab.packet.MacAddress; | ||
| 32 | import org.onlab.packet.VlanId; | 33 | import org.onlab.packet.VlanId; |
| 33 | import org.onlab.util.Tools; | 34 | import org.onlab.util.Tools; |
| 34 | import org.onosproject.cfg.ComponentConfigService; | 35 | import org.onosproject.cfg.ComponentConfigService; |
| 35 | import org.onosproject.core.ApplicationId; | 36 | import org.onosproject.core.ApplicationId; |
| 36 | import org.onosproject.core.CoreService; | 37 | import org.onosproject.core.CoreService; |
| 38 | +import org.onosproject.incubator.net.config.basics.McastConfig; | ||
| 37 | import org.onosproject.incubator.net.intf.Interface; | 39 | import org.onosproject.incubator.net.intf.Interface; |
| 38 | import org.onosproject.incubator.net.intf.InterfaceEvent; | 40 | import org.onosproject.incubator.net.intf.InterfaceEvent; |
| 39 | import org.onosproject.incubator.net.intf.InterfaceListener; | 41 | import org.onosproject.incubator.net.intf.InterfaceListener; |
| ... | @@ -44,9 +46,12 @@ import org.onosproject.incubator.net.routing.RouteListener; | ... | @@ -44,9 +46,12 @@ import org.onosproject.incubator.net.routing.RouteListener; |
| 44 | import org.onosproject.incubator.net.routing.RouteService; | 46 | import org.onosproject.incubator.net.routing.RouteService; |
| 45 | import org.onosproject.net.ConnectPoint; | 47 | import org.onosproject.net.ConnectPoint; |
| 46 | import org.onosproject.net.DeviceId; | 48 | import org.onosproject.net.DeviceId; |
| 49 | +import org.onosproject.net.config.ConfigFactory; | ||
| 47 | import org.onosproject.net.config.NetworkConfigEvent; | 50 | import org.onosproject.net.config.NetworkConfigEvent; |
| 48 | import org.onosproject.net.config.NetworkConfigListener; | 51 | import org.onosproject.net.config.NetworkConfigListener; |
| 52 | +import org.onosproject.net.config.NetworkConfigRegistry; | ||
| 49 | import org.onosproject.net.config.NetworkConfigService; | 53 | import org.onosproject.net.config.NetworkConfigService; |
| 54 | +import org.onosproject.net.config.basics.SubjectFactories; | ||
| 50 | import org.onosproject.net.device.DeviceEvent; | 55 | import org.onosproject.net.device.DeviceEvent; |
| 51 | import org.onosproject.net.device.DeviceListener; | 56 | import org.onosproject.net.device.DeviceListener; |
| 52 | import org.onosproject.net.device.DeviceService; | 57 | import org.onosproject.net.device.DeviceService; |
| ... | @@ -102,6 +107,9 @@ public class SingleSwitchFibInstaller { | ... | @@ -102,6 +107,9 @@ public class SingleSwitchFibInstaller { |
| 102 | protected NetworkConfigService networkConfigService; | 107 | protected NetworkConfigService networkConfigService; |
| 103 | 108 | ||
| 104 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 109 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 110 | + protected NetworkConfigRegistry networkConfigRegistry; | ||
| 111 | + | ||
| 112 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 105 | protected ComponentConfigService componentConfigService; | 113 | protected ComponentConfigService componentConfigService; |
| 106 | 114 | ||
| 107 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 115 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| ... | @@ -123,6 +131,7 @@ public class SingleSwitchFibInstaller { | ... | @@ -123,6 +131,7 @@ public class SingleSwitchFibInstaller { |
| 123 | 131 | ||
| 124 | private List<String> interfaces; | 132 | private List<String> interfaces; |
| 125 | 133 | ||
| 134 | + private ApplicationId coreAppId; | ||
| 126 | private ApplicationId routerAppId; | 135 | private ApplicationId routerAppId; |
| 127 | 136 | ||
| 128 | // Reference count for how many times a next hop is used by a route | 137 | // Reference count for how many times a next hop is used by a route |
| ... | @@ -138,13 +147,25 @@ public class SingleSwitchFibInstaller { | ... | @@ -138,13 +147,25 @@ public class SingleSwitchFibInstaller { |
| 138 | private InternalInterfaceListener internalInterfaceList = new InternalInterfaceListener(); | 147 | private InternalInterfaceListener internalInterfaceList = new InternalInterfaceListener(); |
| 139 | private InternalRouteListener routeListener = new InternalRouteListener(); | 148 | private InternalRouteListener routeListener = new InternalRouteListener(); |
| 140 | 149 | ||
| 150 | + private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory = | ||
| 151 | + new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY, | ||
| 152 | + McastConfig.class, "multicast") { | ||
| 153 | + @Override | ||
| 154 | + public McastConfig createConfig() { | ||
| 155 | + return new McastConfig(); | ||
| 156 | + } | ||
| 157 | + }; | ||
| 158 | + | ||
| 141 | @Activate | 159 | @Activate |
| 142 | protected void activate(ComponentContext context) { | 160 | protected void activate(ComponentContext context) { |
| 143 | componentConfigService.registerProperties(getClass()); | 161 | componentConfigService.registerProperties(getClass()); |
| 144 | modified(context); | 162 | modified(context); |
| 145 | 163 | ||
| 164 | + coreAppId = coreService.registerApplication(CoreService.CORE_APP_NAME); | ||
| 146 | routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID); | 165 | routerAppId = coreService.registerApplication(RoutingService.ROUTER_APP_ID); |
| 147 | 166 | ||
| 167 | + networkConfigRegistry.registerConfigFactory(mcastConfigFactory); | ||
| 168 | + | ||
| 148 | deviceListener = new InternalDeviceListener(); | 169 | deviceListener = new InternalDeviceListener(); |
| 149 | deviceService.addListener(deviceListener); | 170 | deviceService.addListener(deviceListener); |
| 150 | 171 | ||
| ... | @@ -368,6 +389,7 @@ public class SingleSwitchFibInstaller { | ... | @@ -368,6 +389,7 @@ public class SingleSwitchFibInstaller { |
| 368 | } | 389 | } |
| 369 | 390 | ||
| 370 | createFilteringObjective(install, intf); | 391 | createFilteringObjective(install, intf); |
| 392 | + createMcastFilteringObjective(install, intf); | ||
| 371 | } | 393 | } |
| 372 | } | 394 | } |
| 373 | 395 | ||
| ... | @@ -380,10 +402,14 @@ public class SingleSwitchFibInstaller { | ... | @@ -380,10 +402,14 @@ public class SingleSwitchFibInstaller { |
| 380 | } | 402 | } |
| 381 | 403 | ||
| 382 | createFilteringObjective(install, intf); | 404 | createFilteringObjective(install, intf); |
| 405 | + createMcastFilteringObjective(install, intf); | ||
| 383 | } | 406 | } |
| 384 | 407 | ||
| 385 | //create filtering objective for interface | 408 | //create filtering objective for interface |
| 386 | private void createFilteringObjective(boolean install, Interface intf) { | 409 | private void createFilteringObjective(boolean install, Interface intf) { |
| 410 | + VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ? | ||
| 411 | + VlanId.vlanId(ASSIGNED_VLAN) : | ||
| 412 | + egressVlan(); | ||
| 387 | 413 | ||
| 388 | FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); | 414 | FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); |
| 389 | // first add filter for the interface | 415 | // first add filter for the interface |
| ... | @@ -393,12 +419,12 @@ public class SingleSwitchFibInstaller { | ... | @@ -393,12 +419,12 @@ public class SingleSwitchFibInstaller { |
| 393 | fob.withPriority(PRIORITY_OFFSET); | 419 | fob.withPriority(PRIORITY_OFFSET); |
| 394 | if (intf.vlan() == VlanId.NONE) { | 420 | if (intf.vlan() == VlanId.NONE) { |
| 395 | TrafficTreatment tt = DefaultTrafficTreatment.builder() | 421 | TrafficTreatment tt = DefaultTrafficTreatment.builder() |
| 396 | - .pushVlan().setVlanId(VlanId.vlanId(ASSIGNED_VLAN)).build(); | 422 | + .pushVlan().setVlanId(assignedVlan).build(); |
| 397 | fob.withMeta(tt); | 423 | fob.withMeta(tt); |
| 398 | } | 424 | } |
| 399 | - | ||
| 400 | fob.permit().fromApp(routerAppId); | 425 | fob.permit().fromApp(routerAppId); |
| 401 | sendFilteringObjective(install, fob, intf); | 426 | sendFilteringObjective(install, fob, intf); |
| 427 | + | ||
| 402 | if (controlPlaneConnectPoint != null) { | 428 | if (controlPlaneConnectPoint != null) { |
| 403 | // then add the same mac/vlan filters for control-plane connect point | 429 | // then add the same mac/vlan filters for control-plane connect point |
| 404 | fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port())); | 430 | fob.withKey(Criteria.matchInPort(controlPlaneConnectPoint.port())); |
| ... | @@ -406,6 +432,27 @@ public class SingleSwitchFibInstaller { | ... | @@ -406,6 +432,27 @@ public class SingleSwitchFibInstaller { |
| 406 | } | 432 | } |
| 407 | } | 433 | } |
| 408 | 434 | ||
| 435 | + //create filtering objective for multicast traffic | ||
| 436 | + private void createMcastFilteringObjective(boolean install, Interface intf) { | ||
| 437 | + VlanId assignedVlan = (egressVlan().equals(VlanId.NONE)) ? | ||
| 438 | + VlanId.vlanId(ASSIGNED_VLAN) : | ||
| 439 | + egressVlan(); | ||
| 440 | + | ||
| 441 | + FilteringObjective.Builder fob = DefaultFilteringObjective.builder(); | ||
| 442 | + // first add filter for the interface | ||
| 443 | + fob.withKey(Criteria.matchInPort(intf.connectPoint().port())) | ||
| 444 | + .addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST, | ||
| 445 | + MacAddress.IPV4_MULTICAST_MASK)) | ||
| 446 | + .addCondition(Criteria.matchVlanId(ingressVlan())); | ||
| 447 | + fob.withPriority(PRIORITY_OFFSET); | ||
| 448 | + TrafficTreatment tt = DefaultTrafficTreatment.builder() | ||
| 449 | + .pushVlan().setVlanId(assignedVlan).build(); | ||
| 450 | + fob.withMeta(tt); | ||
| 451 | + | ||
| 452 | + fob.permit().fromApp(routerAppId); | ||
| 453 | + sendFilteringObjective(install, fob, intf); | ||
| 454 | + } | ||
| 455 | + | ||
| 409 | private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob, | 456 | private void sendFilteringObjective(boolean install, FilteringObjective.Builder fob, |
| 410 | Interface intf) { | 457 | Interface intf) { |
| 411 | 458 | ||
| ... | @@ -419,6 +466,18 @@ public class SingleSwitchFibInstaller { | ... | @@ -419,6 +466,18 @@ public class SingleSwitchFibInstaller { |
| 419 | flowObjectiveService.filter(deviceId, filter); | 466 | flowObjectiveService.filter(deviceId, filter); |
| 420 | } | 467 | } |
| 421 | 468 | ||
| 469 | + private VlanId ingressVlan() { | ||
| 470 | + McastConfig mcastConfig = | ||
| 471 | + networkConfigService.getConfig(coreAppId, McastConfig.class); | ||
| 472 | + return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE; | ||
| 473 | + } | ||
| 474 | + | ||
| 475 | + private VlanId egressVlan() { | ||
| 476 | + McastConfig mcastConfig = | ||
| 477 | + networkConfigService.getConfig(coreAppId, McastConfig.class); | ||
| 478 | + return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE; | ||
| 479 | + } | ||
| 480 | + | ||
| 422 | private class InternalRouteListener implements RouteListener { | 481 | private class InternalRouteListener implements RouteListener { |
| 423 | @Override | 482 | @Override |
| 424 | public void event(RouteEvent event) { | 483 | public void event(RouteEvent event) { |
| ... | @@ -490,7 +549,6 @@ public class SingleSwitchFibInstaller { | ... | @@ -490,7 +549,6 @@ public class SingleSwitchFibInstaller { |
| 490 | } | 549 | } |
| 491 | 550 | ||
| 492 | private class InternalInterfaceListener implements InterfaceListener { | 551 | private class InternalInterfaceListener implements InterfaceListener { |
| 493 | - | ||
| 494 | @Override | 552 | @Override |
| 495 | public void event(InterfaceEvent event) { | 553 | public void event(InterfaceEvent event) { |
| 496 | Interface intf = event.subject(); | 554 | Interface intf = event.subject(); | ... | ... |
| ... | @@ -40,6 +40,7 @@ import org.onosproject.incubator.net.routing.RouteServiceAdapter; | ... | @@ -40,6 +40,7 @@ import org.onosproject.incubator.net.routing.RouteServiceAdapter; |
| 40 | import org.onosproject.net.ConnectPoint; | 40 | import org.onosproject.net.ConnectPoint; |
| 41 | import org.onosproject.net.DeviceId; | 41 | import org.onosproject.net.DeviceId; |
| 42 | import org.onosproject.net.PortNumber; | 42 | import org.onosproject.net.PortNumber; |
| 43 | +import org.onosproject.net.config.NetworkConfigRegistry; | ||
| 43 | import org.onosproject.net.config.NetworkConfigService; | 44 | import org.onosproject.net.config.NetworkConfigService; |
| 44 | import org.onosproject.net.device.DeviceListener; | 45 | import org.onosproject.net.device.DeviceListener; |
| 45 | import org.onosproject.net.device.DeviceService; | 46 | import org.onosproject.net.device.DeviceService; |
| ... | @@ -105,6 +106,7 @@ public class SingleSwitchFibInstallerTest { | ... | @@ -105,6 +106,7 @@ public class SingleSwitchFibInstallerTest { |
| 105 | private final Set<Interface> interfaces = Sets.newHashSet(); | 106 | private final Set<Interface> interfaces = Sets.newHashSet(); |
| 106 | private InterfaceService interfaceService; | 107 | private InterfaceService interfaceService; |
| 107 | private NetworkConfigService networkConfigService; | 108 | private NetworkConfigService networkConfigService; |
| 109 | + private NetworkConfigRegistry networkConfigRegistry; | ||
| 108 | private FlowObjectiveService flowObjectiveService; | 110 | private FlowObjectiveService flowObjectiveService; |
| 109 | private DeviceService deviceService; | 111 | private DeviceService deviceService; |
| 110 | private static final ApplicationId APPID = TestApplicationId.create("foo"); | 112 | private static final ApplicationId APPID = TestApplicationId.create("foo"); |
| ... | @@ -128,13 +130,15 @@ public class SingleSwitchFibInstallerTest { | ... | @@ -128,13 +130,15 @@ public class SingleSwitchFibInstallerTest { |
| 128 | interfaceService = createMock(InterfaceService.class); | 130 | interfaceService = createMock(InterfaceService.class); |
| 129 | 131 | ||
| 130 | networkConfigService = createMock(NetworkConfigService.class); | 132 | networkConfigService = createMock(NetworkConfigService.class); |
| 133 | + networkConfigRegistry = createMock(NetworkConfigRegistry.class); | ||
| 131 | flowObjectiveService = createMock(FlowObjectiveService.class); | 134 | flowObjectiveService = createMock(FlowObjectiveService.class); |
| 132 | deviceService = new TestDeviceService(); | 135 | deviceService = new TestDeviceService(); |
| 133 | CoreService coreService = createNiceMock(CoreService.class); | 136 | CoreService coreService = createNiceMock(CoreService.class); |
| 134 | - expect(coreService.registerApplication(anyString())).andReturn(APPID); | 137 | + expect(coreService.registerApplication(anyString())).andReturn(APPID).anyTimes(); |
| 135 | replay(coreService); | 138 | replay(coreService); |
| 136 | 139 | ||
| 137 | sSfibInstaller.networkConfigService = networkConfigService; | 140 | sSfibInstaller.networkConfigService = networkConfigService; |
| 141 | + sSfibInstaller.networkConfigRegistry = networkConfigRegistry; | ||
| 138 | sSfibInstaller.interfaceService = interfaceService; | 142 | sSfibInstaller.interfaceService = interfaceService; |
| 139 | sSfibInstaller.flowObjectiveService = flowObjectiveService; | 143 | sSfibInstaller.flowObjectiveService = flowObjectiveService; |
| 140 | sSfibInstaller.coreService = coreService; | 144 | sSfibInstaller.coreService = coreService; | ... | ... |
| ... | @@ -3,9 +3,9 @@ COMPILE_DEPS = [ | ... | @@ -3,9 +3,9 @@ COMPILE_DEPS = [ |
| 3 | '//lib:org.apache.karaf.shell.console', | 3 | '//lib:org.apache.karaf.shell.console', |
| 4 | '//lib:javax.ws.rs-api', | 4 | '//lib:javax.ws.rs-api', |
| 5 | '//cli:onos-cli', | 5 | '//cli:onos-cli', |
| 6 | + '//core/store/serializers:onos-core-serializers', | ||
| 6 | '//incubator/api:onos-incubator-api', | 7 | '//incubator/api:onos-incubator-api', |
| 7 | '//utils/rest:onlab-rest', | 8 | '//utils/rest:onlab-rest', |
| 8 | - '//core/store/serializers:onos-core-serializers', | ||
| 9 | ] | 9 | ] |
| 10 | 10 | ||
| 11 | TEST_DEPS = [ | 11 | TEST_DEPS = [ | ... | ... |
| ... | @@ -51,7 +51,11 @@ | ... | @@ -51,7 +51,11 @@ |
| 51 | <artifactId>onos-cli</artifactId> | 51 | <artifactId>onos-cli</artifactId> |
| 52 | <version>${project.version}</version> | 52 | <version>${project.version}</version> |
| 53 | </dependency> | 53 | </dependency> |
| 54 | - | 54 | + <dependency> |
| 55 | + <groupId>org.onosproject</groupId> | ||
| 56 | + <artifactId>onos-core-serializers</artifactId> | ||
| 57 | + <version>${project.version}</version> | ||
| 58 | + </dependency> | ||
| 55 | <dependency> | 59 | <dependency> |
| 56 | <groupId>org.apache.karaf.shell</groupId> | 60 | <groupId>org.apache.karaf.shell</groupId> |
| 57 | <artifactId>org.apache.karaf.shell.console</artifactId> | 61 | <artifactId>org.apache.karaf.shell.console</artifactId> |
| ... | @@ -84,12 +88,10 @@ | ... | @@ -84,12 +88,10 @@ |
| 84 | <groupId>com.fasterxml.jackson.core</groupId> | 88 | <groupId>com.fasterxml.jackson.core</groupId> |
| 85 | <artifactId>jackson-databind</artifactId> | 89 | <artifactId>jackson-databind</artifactId> |
| 86 | </dependency> | 90 | </dependency> |
| 87 | - | ||
| 88 | <dependency> | 91 | <dependency> |
| 89 | <groupId>com.fasterxml.jackson.core</groupId> | 92 | <groupId>com.fasterxml.jackson.core</groupId> |
| 90 | <artifactId>jackson-annotations</artifactId> | 93 | <artifactId>jackson-annotations</artifactId> |
| 91 | </dependency> | 94 | </dependency> |
| 92 | - | ||
| 93 | <dependency> | 95 | <dependency> |
| 94 | <groupId>org.osgi</groupId> | 96 | <groupId>org.osgi</groupId> |
| 95 | <artifactId>org.osgi.compendium</artifactId> | 97 | <artifactId>org.osgi.compendium</artifactId> |
| ... | @@ -103,7 +105,6 @@ | ... | @@ -103,7 +105,6 @@ |
| 103 | <artifactId>onlab-junit</artifactId> | 105 | <artifactId>onlab-junit</artifactId> |
| 104 | <scope>test</scope> | 106 | <scope>test</scope> |
| 105 | </dependency> | 107 | </dependency> |
| 106 | - | ||
| 107 | <dependency> | 108 | <dependency> |
| 108 | <groupId>org.onosproject</groupId> | 109 | <groupId>org.onosproject</groupId> |
| 109 | <artifactId>onos-api</artifactId> | 110 | <artifactId>onos-api</artifactId> | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2016-present Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +package org.onosproject.segmentrouting; | ||
| 18 | + | ||
| 19 | +import com.google.common.collect.ImmutableSet; | ||
| 20 | +import com.google.common.collect.Lists; | ||
| 21 | +import com.google.common.collect.Sets; | ||
| 22 | +import org.onlab.packet.Ethernet; | ||
| 23 | +import org.onlab.packet.IpAddress; | ||
| 24 | +import org.onlab.packet.IpPrefix; | ||
| 25 | +import org.onlab.packet.MacAddress; | ||
| 26 | +import org.onlab.packet.VlanId; | ||
| 27 | +import org.onlab.util.KryoNamespace; | ||
| 28 | +import org.onosproject.core.ApplicationId; | ||
| 29 | +import org.onosproject.core.CoreService; | ||
| 30 | +import org.onosproject.incubator.net.config.basics.McastConfig; | ||
| 31 | +import org.onosproject.net.ConnectPoint; | ||
| 32 | +import org.onosproject.net.DeviceId; | ||
| 33 | +import org.onosproject.net.Link; | ||
| 34 | +import org.onosproject.net.Path; | ||
| 35 | +import org.onosproject.net.PortNumber; | ||
| 36 | +import org.onosproject.net.flow.DefaultTrafficSelector; | ||
| 37 | +import org.onosproject.net.flow.DefaultTrafficTreatment; | ||
| 38 | +import org.onosproject.net.flow.TrafficSelector; | ||
| 39 | +import org.onosproject.net.flow.TrafficTreatment; | ||
| 40 | +import org.onosproject.net.flow.criteria.Criteria; | ||
| 41 | +import org.onosproject.net.flow.instructions.Instructions.OutputInstruction; | ||
| 42 | +import org.onosproject.net.flowobjective.DefaultFilteringObjective; | ||
| 43 | +import org.onosproject.net.flowobjective.DefaultForwardingObjective; | ||
| 44 | +import org.onosproject.net.flowobjective.DefaultNextObjective; | ||
| 45 | +import org.onosproject.net.flowobjective.FilteringObjective; | ||
| 46 | +import org.onosproject.net.flowobjective.ForwardingObjective; | ||
| 47 | +import org.onosproject.net.flowobjective.NextObjective; | ||
| 48 | +import org.onosproject.net.mcast.McastEvent; | ||
| 49 | +import org.onosproject.net.mcast.McastRouteInfo; | ||
| 50 | +import org.onosproject.net.topology.TopologyService; | ||
| 51 | +import org.onosproject.segmentrouting.grouphandler.McastNextObjectiveStoreKey; | ||
| 52 | +import org.onosproject.store.serializers.KryoNamespaces; | ||
| 53 | +import org.onosproject.store.service.ConsistentMap; | ||
| 54 | +import org.onosproject.store.service.Serializer; | ||
| 55 | +import org.onosproject.store.service.StorageService; | ||
| 56 | +import org.slf4j.Logger; | ||
| 57 | +import org.slf4j.LoggerFactory; | ||
| 58 | + | ||
| 59 | +import java.util.Collection; | ||
| 60 | +import java.util.Collections; | ||
| 61 | +import java.util.List; | ||
| 62 | +import java.util.Optional; | ||
| 63 | +import java.util.Set; | ||
| 64 | + | ||
| 65 | +/** | ||
| 66 | + * Multicast event handler. | ||
| 67 | + */ | ||
| 68 | +public class McastEventHandler { | ||
| 69 | + private static final Logger log = LoggerFactory.getLogger(McastEventHandler.class); | ||
| 70 | + private final SegmentRoutingManager srManager; | ||
| 71 | + private final ApplicationId coreAppId; | ||
| 72 | + private StorageService storageService; | ||
| 73 | + private TopologyService topologyService; | ||
| 74 | + private final KryoNamespace.Builder kryoBuilder; | ||
| 75 | + private final ConsistentMap<McastNextObjectiveStoreKey, NextObjective> mcastNextObjStore; | ||
| 76 | + | ||
| 77 | + /** | ||
| 78 | + * Constructs the McastEventHandler. | ||
| 79 | + * | ||
| 80 | + * @param srManager Segment Routing manager | ||
| 81 | + */ | ||
| 82 | + public McastEventHandler(SegmentRoutingManager srManager) { | ||
| 83 | + coreAppId = srManager.coreService.getAppId(CoreService.CORE_APP_NAME); | ||
| 84 | + | ||
| 85 | + this.srManager = srManager; | ||
| 86 | + this.storageService = srManager.storageService; | ||
| 87 | + this.topologyService = srManager.topologyService; | ||
| 88 | + | ||
| 89 | + kryoBuilder = new KryoNamespace.Builder() | ||
| 90 | + .register(KryoNamespaces.API) | ||
| 91 | + .register(McastNextObjectiveStoreKey.class); | ||
| 92 | + mcastNextObjStore = storageService | ||
| 93 | + .<McastNextObjectiveStoreKey, NextObjective>consistentMapBuilder() | ||
| 94 | + .withName("onos-mcast-nextobj-store") | ||
| 95 | + .withSerializer(Serializer.using(kryoBuilder.build())) | ||
| 96 | + .build(); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * Processes the SOURCE_ADDED event. | ||
| 101 | + * | ||
| 102 | + * @param event McastEvent with SOURCE_ADDED type | ||
| 103 | + */ | ||
| 104 | + protected void processSourceAdded(McastEvent event) { | ||
| 105 | + log.info("processSourceAdded {}", event); | ||
| 106 | + McastRouteInfo mcastRouteInfo = event.subject(); | ||
| 107 | + if (!mcastRouteInfo.isComplete()) { | ||
| 108 | + log.info("Incompleted McastRouteInfo. Abort."); | ||
| 109 | + return; | ||
| 110 | + } | ||
| 111 | + ConnectPoint source = mcastRouteInfo.source().orElse(null); | ||
| 112 | + Set<ConnectPoint> sinks = mcastRouteInfo.sinks(); | ||
| 113 | + IpAddress mcastIp = mcastRouteInfo.route().group(); | ||
| 114 | + | ||
| 115 | + sinks.forEach(sink -> { | ||
| 116 | + processSinkAddedInternal(source, sink, mcastIp); | ||
| 117 | + }); | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + /** | ||
| 121 | + * Processes the SINK_ADDED event. | ||
| 122 | + * | ||
| 123 | + * @param event McastEvent with SINK_ADDED type | ||
| 124 | + */ | ||
| 125 | + protected void processSinkAdded(McastEvent event) { | ||
| 126 | + log.info("processSinkAdded {}", event); | ||
| 127 | + McastRouteInfo mcastRouteInfo = event.subject(); | ||
| 128 | + if (!mcastRouteInfo.isComplete()) { | ||
| 129 | + log.info("Incompleted McastRouteInfo. Abort."); | ||
| 130 | + return; | ||
| 131 | + } | ||
| 132 | + ConnectPoint source = mcastRouteInfo.source().orElse(null); | ||
| 133 | + ConnectPoint sink = mcastRouteInfo.sink().orElse(null); | ||
| 134 | + IpAddress mcastIp = mcastRouteInfo.route().group(); | ||
| 135 | + | ||
| 136 | + processSinkAddedInternal(source, sink, mcastIp); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + /** | ||
| 140 | + * Processes the SINK_REMOVED event. | ||
| 141 | + * | ||
| 142 | + * @param event McastEvent with SINK_REMOVED type | ||
| 143 | + */ | ||
| 144 | + protected void processSinkRemoved(McastEvent event) { | ||
| 145 | + log.info("processSinkRemoved {}", event); | ||
| 146 | + McastRouteInfo mcastRouteInfo = event.subject(); | ||
| 147 | + if (!mcastRouteInfo.isComplete()) { | ||
| 148 | + log.info("Incompleted McastRouteInfo. Abort."); | ||
| 149 | + return; | ||
| 150 | + } | ||
| 151 | + ConnectPoint source = mcastRouteInfo.source().orElse(null); | ||
| 152 | + ConnectPoint sink = mcastRouteInfo.sink().orElse(null); | ||
| 153 | + IpAddress mcastIp = mcastRouteInfo.route().group(); | ||
| 154 | + VlanId assignedVlan = assignedVlan(); | ||
| 155 | + | ||
| 156 | + // When source and sink are on the same device | ||
| 157 | + if (source.deviceId().equals(sink.deviceId())) { | ||
| 158 | + // Source and sink are on even the same port. There must be something wrong. | ||
| 159 | + if (source.port().equals(sink.port())) { | ||
| 160 | + log.warn("Sink is on the same port of source. Abort"); | ||
| 161 | + return; | ||
| 162 | + } | ||
| 163 | + removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan); | ||
| 164 | + return; | ||
| 165 | + } | ||
| 166 | + | ||
| 167 | + // Process the egress device | ||
| 168 | + boolean isLast = removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan); | ||
| 169 | + | ||
| 170 | + // If this is the last sink on the device, also update upstream | ||
| 171 | + Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp); | ||
| 172 | + if (mcastPath.isPresent()) { | ||
| 173 | + List<Link> links = Lists.newArrayList(mcastPath.get().links()); | ||
| 174 | + Collections.reverse(links); | ||
| 175 | + for (Link link : links) { | ||
| 176 | + if (isLast) { | ||
| 177 | + isLast = removePortFromDevice(link.src().deviceId(), link.src().port(), | ||
| 178 | + mcastIp, assignedVlan); | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + /** | ||
| 185 | + * Establishes a path from source to sink for given multicast group. | ||
| 186 | + * | ||
| 187 | + * @param source connect point of the multicast source | ||
| 188 | + * @param sink connection point of the multicast sink | ||
| 189 | + * @param mcastIp multicast group IP address | ||
| 190 | + */ | ||
| 191 | + private void processSinkAddedInternal(ConnectPoint source, ConnectPoint sink, | ||
| 192 | + IpAddress mcastIp) { | ||
| 193 | + VlanId assignedVlan = assignedVlan(); | ||
| 194 | + | ||
| 195 | + // When source and sink are on the same device | ||
| 196 | + if (source.deviceId().equals(sink.deviceId())) { | ||
| 197 | + // Source and sink are on even the same port. There must be something wrong. | ||
| 198 | + if (source.port().equals(sink.port())) { | ||
| 199 | + log.warn("Sink is on the same port of source. Abort"); | ||
| 200 | + return; | ||
| 201 | + } | ||
| 202 | + addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan); | ||
| 203 | + return; | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + // Process the ingress device | ||
| 207 | + addFilterToDevice(source.deviceId(), source.port(), assignedVlan); | ||
| 208 | + | ||
| 209 | + // Find a path. If present, create/update groups and flows for each hop | ||
| 210 | + Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp); | ||
| 211 | + if (mcastPath.isPresent()) { | ||
| 212 | + mcastPath.get().links().forEach(link -> { | ||
| 213 | + addFilterToDevice(link.dst().deviceId(), link.dst().port(), assignedVlan); | ||
| 214 | + addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp, assignedVlan); | ||
| 215 | + }); | ||
| 216 | + // Process the egress device | ||
| 217 | + addPortToDevice(sink.deviceId(), sink.port(), mcastIp, assignedVlan); | ||
| 218 | + } | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + /** | ||
| 222 | + * Adds filtering objective for given device and port. | ||
| 223 | + * | ||
| 224 | + * @param deviceId device ID | ||
| 225 | + * @param port ingress port number | ||
| 226 | + * @param assignedVlan assigned VLAN ID | ||
| 227 | + */ | ||
| 228 | + private void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan) { | ||
| 229 | + // Do nothing if the port is configured as suppressed | ||
| 230 | + ConnectPoint connectPt = new ConnectPoint(deviceId, port); | ||
| 231 | + if (srManager.deviceConfiguration.suppressSubnet().contains(connectPt) || | ||
| 232 | + srManager.deviceConfiguration.suppressHost().contains(connectPt)) { | ||
| 233 | + log.info("Ignore suppressed port {}", connectPt); | ||
| 234 | + return; | ||
| 235 | + } | ||
| 236 | + | ||
| 237 | + FilteringObjective.Builder filtObjBuilder = | ||
| 238 | + filterObjBuilder(deviceId, port, assignedVlan); | ||
| 239 | + srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add()); | ||
| 240 | + // TODO add objective context | ||
| 241 | + } | ||
| 242 | + | ||
| 243 | + /** | ||
| 244 | + * Adds a port to given multicast group on given device. This involves the | ||
| 245 | + * update of L3 multicast group and multicast routing table entry. | ||
| 246 | + * | ||
| 247 | + * @param deviceId device ID | ||
| 248 | + * @param port port to be added | ||
| 249 | + * @param mcastIp multicast group | ||
| 250 | + * @param assignedVlan assigned VLAN ID | ||
| 251 | + */ | ||
| 252 | + private void addPortToDevice(DeviceId deviceId, PortNumber port, | ||
| 253 | + IpAddress mcastIp, VlanId assignedVlan) { | ||
| 254 | + log.info("Add port {} to {}. mcastIp={}, assignedVlan={}", | ||
| 255 | + port, deviceId, mcastIp, assignedVlan); | ||
| 256 | + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey = | ||
| 257 | + new McastNextObjectiveStoreKey(mcastIp, deviceId); | ||
| 258 | + ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder(); | ||
| 259 | + if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) { | ||
| 260 | + // First time someone request this mcast group via this device | ||
| 261 | + portBuilder.add(port); | ||
| 262 | + } else { | ||
| 263 | + // This device already serves some subscribers of this mcast group | ||
| 264 | + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value(); | ||
| 265 | + // Stop if the port is already in the nextobj | ||
| 266 | + Set<PortNumber> existingPorts = getPorts(nextObj.next()); | ||
| 267 | + if (existingPorts.contains(port)) { | ||
| 268 | + log.info("NextObj for {}/{} already exists. Abort", deviceId, port); | ||
| 269 | + return; | ||
| 270 | + } | ||
| 271 | + portBuilder.addAll(existingPorts).add(port).build(); | ||
| 272 | + } | ||
| 273 | + // Create, store and apply the new nextObj and fwdObj | ||
| 274 | + NextObjective newNextObj = | ||
| 275 | + nextObjBuilder(mcastIp, assignedVlan, portBuilder.build()).add(); | ||
| 276 | + ForwardingObjective fwdObj = | ||
| 277 | + fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(); | ||
| 278 | + mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj); | ||
| 279 | + srManager.flowObjectiveService.next(deviceId, newNextObj); | ||
| 280 | + srManager.flowObjectiveService.forward(deviceId, fwdObj); | ||
| 281 | + // TODO add objective callback | ||
| 282 | + } | ||
| 283 | + | ||
| 284 | + /** | ||
| 285 | + * Removes a port from given multicast group on given device. | ||
| 286 | + * This involves the update of L3 multicast group and multicast routing | ||
| 287 | + * table entry. | ||
| 288 | + * | ||
| 289 | + * @param deviceId device ID | ||
| 290 | + * @param port port to be added | ||
| 291 | + * @param mcastIp multicast group | ||
| 292 | + * @param assignedVlan assigned VLAN ID | ||
| 293 | + * @return true if this is the last sink on this device | ||
| 294 | + */ | ||
| 295 | + private boolean removePortFromDevice(DeviceId deviceId, PortNumber port, | ||
| 296 | + IpAddress mcastIp, VlanId assignedVlan) { | ||
| 297 | + log.info("Remove port {} from {}. mcastIp={}, assignedVlan={}", | ||
| 298 | + port, deviceId, mcastIp, assignedVlan); | ||
| 299 | + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey = | ||
| 300 | + new McastNextObjectiveStoreKey(mcastIp, deviceId); | ||
| 301 | + // This device is not serving this multicast group | ||
| 302 | + if (!mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) { | ||
| 303 | + log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port); | ||
| 304 | + return false; | ||
| 305 | + } | ||
| 306 | + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value(); | ||
| 307 | + | ||
| 308 | + Set<PortNumber> existingPorts = getPorts(nextObj.next()); | ||
| 309 | + // This device does not serve this multicast group | ||
| 310 | + if (!existingPorts.contains(port)) { | ||
| 311 | + log.warn("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port); | ||
| 312 | + return false; | ||
| 313 | + } | ||
| 314 | + // Copy and modify the ImmutableSet | ||
| 315 | + existingPorts = Sets.newHashSet(existingPorts); | ||
| 316 | + existingPorts.remove(port); | ||
| 317 | + | ||
| 318 | + NextObjective newNextObj; | ||
| 319 | + ForwardingObjective fwdObj; | ||
| 320 | + if (existingPorts.isEmpty()) { | ||
| 321 | + // If this is the last sink, remove flows and groups | ||
| 322 | + // NOTE: Rely on GroupStore garbage collection rather than explicitly | ||
| 323 | + // remove L3MG since there might be other flows/groups refer to | ||
| 324 | + // the same L2IG | ||
| 325 | + fwdObj = fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove(); | ||
| 326 | + mcastNextObjStore.remove(mcastNextObjectiveStoreKey); | ||
| 327 | + srManager.flowObjectiveService.forward(deviceId, fwdObj); | ||
| 328 | + } else { | ||
| 329 | + // If this is not the last sink, update flows and groups | ||
| 330 | + newNextObj = nextObjBuilder(mcastIp, assignedVlan, existingPorts).add(); | ||
| 331 | + fwdObj = fwdObjBuilder(mcastIp, assignedVlan, newNextObj.id()).add(); | ||
| 332 | + mcastNextObjStore.put(mcastNextObjectiveStoreKey, newNextObj); | ||
| 333 | + srManager.flowObjectiveService.next(deviceId, newNextObj); | ||
| 334 | + srManager.flowObjectiveService.forward(deviceId, fwdObj); | ||
| 335 | + } | ||
| 336 | + // TODO add objective callback | ||
| 337 | + | ||
| 338 | + return existingPorts.isEmpty(); | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + /** | ||
| 342 | + * Creates a next objective builder for multicast. | ||
| 343 | + * | ||
| 344 | + * @param mcastIp multicast group | ||
| 345 | + * @param assignedVlan assigned VLAN ID | ||
| 346 | + * @param outPorts set of output port numbers | ||
| 347 | + * @return next objective builder | ||
| 348 | + */ | ||
| 349 | + private NextObjective.Builder nextObjBuilder(IpAddress mcastIp, | ||
| 350 | + VlanId assignedVlan, Set<PortNumber> outPorts) { | ||
| 351 | + int nextId = srManager.flowObjectiveService.allocateNextId(); | ||
| 352 | + | ||
| 353 | + TrafficSelector metadata = | ||
| 354 | + DefaultTrafficSelector.builder() | ||
| 355 | + .matchVlanId(assignedVlan) | ||
| 356 | + .matchIPDst(mcastIp.toIpPrefix()) | ||
| 357 | + .build(); | ||
| 358 | + | ||
| 359 | + NextObjective.Builder nextObjBuilder = DefaultNextObjective | ||
| 360 | + .builder().withId(nextId) | ||
| 361 | + .withType(NextObjective.Type.BROADCAST).fromApp(srManager.appId) | ||
| 362 | + .withMeta(metadata); | ||
| 363 | + | ||
| 364 | + outPorts.forEach(port -> { | ||
| 365 | + TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder(); | ||
| 366 | + if (egressVlan().equals(VlanId.NONE)) { | ||
| 367 | + tBuilder.popVlan(); | ||
| 368 | + } | ||
| 369 | + tBuilder.setOutput(port); | ||
| 370 | + nextObjBuilder.addTreatment(tBuilder.build()); | ||
| 371 | + }); | ||
| 372 | + | ||
| 373 | + return nextObjBuilder; | ||
| 374 | + } | ||
| 375 | + | ||
| 376 | + /** | ||
| 377 | + * Creates a forwarding objective builder for multicast. | ||
| 378 | + * | ||
| 379 | + * @param mcastIp multicast group | ||
| 380 | + * @param assignedVlan assigned VLAN ID | ||
| 381 | + * @param nextId next ID of the L3 multicast group | ||
| 382 | + * @return forwarding objective builder | ||
| 383 | + */ | ||
| 384 | + private ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp, | ||
| 385 | + VlanId assignedVlan, int nextId) { | ||
| 386 | + TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder(); | ||
| 387 | + IpPrefix mcastPrefix = IpPrefix.valueOf(mcastIp, IpPrefix.MAX_INET_MASK_LENGTH); | ||
| 388 | + sbuilder.matchEthType(Ethernet.TYPE_IPV4); | ||
| 389 | + sbuilder.matchIPDst(mcastPrefix); | ||
| 390 | + TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(); | ||
| 391 | + metabuilder.matchVlanId(assignedVlan); | ||
| 392 | + | ||
| 393 | + ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder(); | ||
| 394 | + fwdBuilder.withSelector(sbuilder.build()) | ||
| 395 | + .withMeta(metabuilder.build()) | ||
| 396 | + .nextStep(nextId) | ||
| 397 | + .withFlag(ForwardingObjective.Flag.SPECIFIC) | ||
| 398 | + .fromApp(srManager.appId) | ||
| 399 | + .withPriority(SegmentRoutingService.DEFAULT_PRIORITY); | ||
| 400 | + return fwdBuilder; | ||
| 401 | + } | ||
| 402 | + | ||
| 403 | + /** | ||
| 404 | + * Creates a filtering objective builder for multicast. | ||
| 405 | + * | ||
| 406 | + * @param deviceId Device ID | ||
| 407 | + * @param ingressPort ingress port of the multicast stream | ||
| 408 | + * @param assignedVlan assigned VLAN ID | ||
| 409 | + * @return filtering objective builder | ||
| 410 | + */ | ||
| 411 | + private FilteringObjective.Builder filterObjBuilder(DeviceId deviceId, PortNumber ingressPort, | ||
| 412 | + VlanId assignedVlan) { | ||
| 413 | + FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder(); | ||
| 414 | + filtBuilder.withKey(Criteria.matchInPort(ingressPort)) | ||
| 415 | + .addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST, | ||
| 416 | + MacAddress.IPV4_MULTICAST_MASK)) | ||
| 417 | + .addCondition(Criteria.matchVlanId(egressVlan())) | ||
| 418 | + .withPriority(SegmentRoutingService.DEFAULT_PRIORITY); | ||
| 419 | + // vlan assignment is valid only if this instance is master | ||
| 420 | + if (srManager.mastershipService.isLocalMaster(deviceId)) { | ||
| 421 | + TrafficTreatment tt = DefaultTrafficTreatment.builder() | ||
| 422 | + .pushVlan().setVlanId(assignedVlan).build(); | ||
| 423 | + filtBuilder.withMeta(tt); | ||
| 424 | + } | ||
| 425 | + return filtBuilder.permit().fromApp(srManager.appId); | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + /** | ||
| 429 | + * Gets output ports information from treatments. | ||
| 430 | + * | ||
| 431 | + * @param treatments collection of traffic treatments | ||
| 432 | + * @return set of output port numbers | ||
| 433 | + */ | ||
| 434 | + private Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) { | ||
| 435 | + ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder(); | ||
| 436 | + treatments.forEach(treatment -> { | ||
| 437 | + treatment.allInstructions().stream() | ||
| 438 | + .filter(instr -> instr instanceof OutputInstruction) | ||
| 439 | + .forEach(instr -> { | ||
| 440 | + builder.add(((OutputInstruction) instr).port()); | ||
| 441 | + }); | ||
| 442 | + }); | ||
| 443 | + return builder.build(); | ||
| 444 | + } | ||
| 445 | + | ||
| 446 | + /** | ||
| 447 | + * Gets a path from src to dst. | ||
| 448 | + * If a path was allocated before, returns the allocated path. | ||
| 449 | + * Otherwise, randomly pick one from available paths. | ||
| 450 | + * | ||
| 451 | + * @param src source device ID | ||
| 452 | + * @param dst destination device ID | ||
| 453 | + * @param mcastIp multicast group | ||
| 454 | + * @return an optional path from src to dst | ||
| 455 | + */ | ||
| 456 | + private Optional<Path> getPath(DeviceId src, DeviceId dst, IpAddress mcastIp) { | ||
| 457 | + List<Path> allPaths = Lists.newArrayList( | ||
| 458 | + topologyService.getPaths(topologyService.currentTopology(), src, dst)); | ||
| 459 | + if (allPaths.isEmpty()) { | ||
| 460 | + log.warn("Fail to find a path from {} to {}. Abort.", src, dst); | ||
| 461 | + return Optional.empty(); | ||
| 462 | + } | ||
| 463 | + | ||
| 464 | + // If one of the available path is used before, use the same path | ||
| 465 | + McastNextObjectiveStoreKey mcastNextObjectiveStoreKey = | ||
| 466 | + new McastNextObjectiveStoreKey(mcastIp, src); | ||
| 467 | + if (mcastNextObjStore.containsKey(mcastNextObjectiveStoreKey)) { | ||
| 468 | + NextObjective nextObj = mcastNextObjStore.get(mcastNextObjectiveStoreKey).value(); | ||
| 469 | + Set<PortNumber> existingPorts = getPorts(nextObj.next()); | ||
| 470 | + for (Path path : allPaths) { | ||
| 471 | + PortNumber srcPort = path.links().get(0).src().port(); | ||
| 472 | + if (existingPorts.contains(srcPort)) { | ||
| 473 | + return Optional.of(path); | ||
| 474 | + } | ||
| 475 | + } | ||
| 476 | + } | ||
| 477 | + // Otherwise, randomly pick a path | ||
| 478 | + Collections.shuffle(allPaths); | ||
| 479 | + return allPaths.stream().findFirst(); | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + /** | ||
| 483 | + * Gets egress VLAN from McastConfig. | ||
| 484 | + * | ||
| 485 | + * @return egress VLAN or VlanId.NONE if not configured | ||
| 486 | + */ | ||
| 487 | + private VlanId egressVlan() { | ||
| 488 | + McastConfig mcastConfig = | ||
| 489 | + srManager.cfgService.getConfig(coreAppId, McastConfig.class); | ||
| 490 | + return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE; | ||
| 491 | + } | ||
| 492 | + | ||
| 493 | + /** | ||
| 494 | + * Gets assigned VLAN according to the value of egress VLAN. | ||
| 495 | + * | ||
| 496 | + * @return assigned VLAN | ||
| 497 | + */ | ||
| 498 | + private VlanId assignedVlan() { | ||
| 499 | + return (egressVlan().equals(VlanId.NONE)) ? | ||
| 500 | + VlanId.vlanId(SegmentRoutingManager.ASSIGNED_VLAN_NO_SUBNET) : | ||
| 501 | + egressVlan(); | ||
| 502 | + } | ||
| 503 | +} |
| ... | @@ -73,8 +73,8 @@ public class NetworkConfigEventHandler { | ... | @@ -73,8 +73,8 @@ public class NetworkConfigEventHandler { |
| 73 | SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get(); | 73 | SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get(); |
| 74 | SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get(); | 74 | SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get(); |
| 75 | deviceService.getAvailableDevices().forEach(device -> { | 75 | deviceService.getAvailableDevices().forEach(device -> { |
| 76 | - Set<MacAddress> macAddresses = getMacAddresses(config); | 76 | + Set<MacAddress> macAddresses = new HashSet<>(getMacAddresses(config)); |
| 77 | - Set<MacAddress> prevMacAddresses = getMacAddresses(prevConfig); | 77 | + Set<MacAddress> prevMacAddresses = new HashSet<>(getMacAddresses(prevConfig)); |
| 78 | // Avoid removing and re-adding unchanged MAC addresses since | 78 | // Avoid removing and re-adding unchanged MAC addresses since |
| 79 | // FlowObjective does not guarantee the execution order. | 79 | // FlowObjective does not guarantee the execution order. |
| 80 | Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses); | 80 | Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses); | ... | ... |
| ... | @@ -33,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; | ... | @@ -33,6 +33,7 @@ import org.onosproject.cfg.ComponentConfigService; |
| 33 | import org.onosproject.core.ApplicationId; | 33 | import org.onosproject.core.ApplicationId; |
| 34 | import org.onosproject.core.CoreService; | 34 | import org.onosproject.core.CoreService; |
| 35 | import org.onosproject.event.Event; | 35 | import org.onosproject.event.Event; |
| 36 | +import org.onosproject.incubator.net.config.basics.McastConfig; | ||
| 36 | import org.onosproject.mastership.MastershipService; | 37 | import org.onosproject.mastership.MastershipService; |
| 37 | import org.onosproject.net.ConnectPoint; | 38 | import org.onosproject.net.ConnectPoint; |
| 38 | import org.onosproject.net.Device; | 39 | import org.onosproject.net.Device; |
| ... | @@ -60,23 +61,27 @@ import org.onosproject.net.flowobjective.ObjectiveContext; | ... | @@ -60,23 +61,27 @@ import org.onosproject.net.flowobjective.ObjectiveContext; |
| 60 | import org.onosproject.net.flowobjective.ObjectiveError; | 61 | import org.onosproject.net.flowobjective.ObjectiveError; |
| 61 | import org.onosproject.net.host.HostEvent; | 62 | import org.onosproject.net.host.HostEvent; |
| 62 | import org.onosproject.net.host.HostListener; | 63 | import org.onosproject.net.host.HostListener; |
| 63 | -import org.onosproject.net.host.HostService; | 64 | +import org.onosproject.net.mcast.McastEvent; |
| 64 | -import org.onosproject.net.link.LinkEvent; | 65 | +import org.onosproject.net.mcast.McastListener; |
| 65 | -import org.onosproject.net.link.LinkListener; | 66 | +import org.onosproject.net.mcast.MulticastRouteService; |
| 66 | -import org.onosproject.net.link.LinkService; | ||
| 67 | -import org.onosproject.net.packet.InboundPacket; | ||
| 68 | -import org.onosproject.net.packet.PacketContext; | ||
| 69 | import org.onosproject.net.packet.PacketPriority; | 67 | import org.onosproject.net.packet.PacketPriority; |
| 70 | -import org.onosproject.net.packet.PacketProcessor; | 68 | +import org.onosproject.net.topology.TopologyService; |
| 71 | -import org.onosproject.net.packet.PacketService; | ||
| 72 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; | 69 | import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException; |
| 73 | import org.onosproject.segmentrouting.config.DeviceConfiguration; | 70 | import org.onosproject.segmentrouting.config.DeviceConfiguration; |
| 74 | -import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig; | ||
| 75 | import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig; | 71 | import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig; |
| 72 | +import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig; | ||
| 76 | import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; | 73 | import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler; |
| 77 | import org.onosproject.segmentrouting.grouphandler.NeighborSet; | 74 | import org.onosproject.segmentrouting.grouphandler.NeighborSet; |
| 78 | import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey; | 75 | import org.onosproject.segmentrouting.grouphandler.NeighborSetNextObjectiveStoreKey; |
| 79 | import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey; | 76 | import org.onosproject.segmentrouting.grouphandler.PortNextObjectiveStoreKey; |
| 77 | +import org.onosproject.net.host.HostService; | ||
| 78 | +import org.onosproject.net.link.LinkEvent; | ||
| 79 | +import org.onosproject.net.link.LinkListener; | ||
| 80 | +import org.onosproject.net.link.LinkService; | ||
| 81 | +import org.onosproject.net.packet.InboundPacket; | ||
| 82 | +import org.onosproject.net.packet.PacketContext; | ||
| 83 | +import org.onosproject.net.packet.PacketProcessor; | ||
| 84 | +import org.onosproject.net.packet.PacketService; | ||
| 80 | import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey; | 85 | import org.onosproject.segmentrouting.grouphandler.SubnetNextObjectiveStoreKey; |
| 81 | import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey; | 86 | import org.onosproject.segmentrouting.grouphandler.XConnectNextObjectiveStoreKey; |
| 82 | import org.onosproject.store.serializers.KryoNamespaces; | 87 | import org.onosproject.store.serializers.KryoNamespaces; |
| ... | @@ -143,6 +148,12 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -143,6 +148,12 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 143 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | 148 | @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) |
| 144 | protected ComponentConfigService compCfgService; | 149 | protected ComponentConfigService compCfgService; |
| 145 | 150 | ||
| 151 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 152 | + protected MulticastRouteService multicastRouteService; | ||
| 153 | + | ||
| 154 | + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) | ||
| 155 | + protected TopologyService topologyService; | ||
| 156 | + | ||
| 146 | protected ArpHandler arpHandler = null; | 157 | protected ArpHandler arpHandler = null; |
| 147 | protected IcmpHandler icmpHandler = null; | 158 | protected IcmpHandler icmpHandler = null; |
| 148 | protected IpHandler ipHandler = null; | 159 | protected IpHandler ipHandler = null; |
| ... | @@ -157,8 +168,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -157,8 +168,11 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 157 | private InternalLinkListener linkListener = null; | 168 | private InternalLinkListener linkListener = null; |
| 158 | private InternalDeviceListener deviceListener = null; | 169 | private InternalDeviceListener deviceListener = null; |
| 159 | private NetworkConfigEventHandler netcfgHandler = null; | 170 | private NetworkConfigEventHandler netcfgHandler = null; |
| 171 | + private McastEventHandler mcastEventHandler = null; | ||
| 160 | private InternalEventHandler eventHandler = new InternalEventHandler(); | 172 | private InternalEventHandler eventHandler = new InternalEventHandler(); |
| 161 | private final InternalHostListener hostListener = new InternalHostListener(); | 173 | private final InternalHostListener hostListener = new InternalHostListener(); |
| 174 | + private final InternalConfigListener cfgListener = new InternalConfigListener(this); | ||
| 175 | + private final InternalMcastListener mcastListener = new InternalMcastListener(); | ||
| 162 | 176 | ||
| 163 | private ScheduledExecutorService executorService = Executors | 177 | private ScheduledExecutorService executorService = Executors |
| 164 | .newScheduledThreadPool(1); | 178 | .newScheduledThreadPool(1); |
| ... | @@ -196,29 +210,32 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -196,29 +210,32 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 196 | private EventuallyConsistentMap<String, Tunnel> tunnelStore = null; | 210 | private EventuallyConsistentMap<String, Tunnel> tunnelStore = null; |
| 197 | private EventuallyConsistentMap<String, Policy> policyStore = null; | 211 | private EventuallyConsistentMap<String, Policy> policyStore = null; |
| 198 | 212 | ||
| 199 | - private final InternalConfigListener cfgListener = | 213 | + private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> deviceConfigFactory = |
| 200 | - new InternalConfigListener(this); | ||
| 201 | - | ||
| 202 | - private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> cfgDeviceFactory = | ||
| 203 | new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY, | 214 | new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY, |
| 204 | - SegmentRoutingDeviceConfig.class, | 215 | + SegmentRoutingDeviceConfig.class, "segmentrouting") { |
| 205 | - "segmentrouting") { | ||
| 206 | @Override | 216 | @Override |
| 207 | public SegmentRoutingDeviceConfig createConfig() { | 217 | public SegmentRoutingDeviceConfig createConfig() { |
| 208 | return new SegmentRoutingDeviceConfig(); | 218 | return new SegmentRoutingDeviceConfig(); |
| 209 | } | 219 | } |
| 210 | }; | 220 | }; |
| 211 | - | 221 | + private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> appConfigFactory = |
| 212 | - private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> cfgAppFactory = | ||
| 213 | new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY, | 222 | new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(SubjectFactories.APP_SUBJECT_FACTORY, |
| 214 | - SegmentRoutingAppConfig.class, | 223 | + SegmentRoutingAppConfig.class, "segmentrouting") { |
| 215 | - "segmentrouting") { | ||
| 216 | @Override | 224 | @Override |
| 217 | public SegmentRoutingAppConfig createConfig() { | 225 | public SegmentRoutingAppConfig createConfig() { |
| 218 | return new SegmentRoutingAppConfig(); | 226 | return new SegmentRoutingAppConfig(); |
| 219 | } | 227 | } |
| 220 | }; | 228 | }; |
| 221 | 229 | ||
| 230 | + private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory = | ||
| 231 | + new ConfigFactory<ApplicationId, McastConfig>(SubjectFactories.APP_SUBJECT_FACTORY, | ||
| 232 | + McastConfig.class, "multicast") { | ||
| 233 | + @Override | ||
| 234 | + public McastConfig createConfig() { | ||
| 235 | + return new McastConfig(); | ||
| 236 | + } | ||
| 237 | + }; | ||
| 238 | + | ||
| 222 | private Object threadSchedulerLock = new Object(); | 239 | private Object threadSchedulerLock = new Object(); |
| 223 | private static int numOfEventsQueued = 0; | 240 | private static int numOfEventsQueued = 0; |
| 224 | private static int numOfEventsExecuted = 0; | 241 | private static int numOfEventsExecuted = 0; |
| ... | @@ -312,14 +329,17 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -312,14 +329,17 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 312 | linkListener = new InternalLinkListener(); | 329 | linkListener = new InternalLinkListener(); |
| 313 | deviceListener = new InternalDeviceListener(); | 330 | deviceListener = new InternalDeviceListener(); |
| 314 | netcfgHandler = new NetworkConfigEventHandler(this); | 331 | netcfgHandler = new NetworkConfigEventHandler(this); |
| 332 | + mcastEventHandler = new McastEventHandler(this); | ||
| 315 | 333 | ||
| 316 | cfgService.addListener(cfgListener); | 334 | cfgService.addListener(cfgListener); |
| 317 | - cfgService.registerConfigFactory(cfgDeviceFactory); | 335 | + cfgService.registerConfigFactory(deviceConfigFactory); |
| 318 | - cfgService.registerConfigFactory(cfgAppFactory); | 336 | + cfgService.registerConfigFactory(appConfigFactory); |
| 337 | + cfgService.registerConfigFactory(mcastConfigFactory); | ||
| 319 | hostService.addListener(hostListener); | 338 | hostService.addListener(hostListener); |
| 320 | packetService.addProcessor(processor, PacketProcessor.director(2)); | 339 | packetService.addProcessor(processor, PacketProcessor.director(2)); |
| 321 | linkService.addListener(linkListener); | 340 | linkService.addListener(linkListener); |
| 322 | deviceService.addListener(deviceListener); | 341 | deviceService.addListener(deviceListener); |
| 342 | + multicastRouteService.addListener(mcastListener); | ||
| 323 | 343 | ||
| 324 | // Request ARP packet-in | 344 | // Request ARP packet-in |
| 325 | TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); | 345 | TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| ... | @@ -351,8 +371,9 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -351,8 +371,9 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 351 | @Deactivate | 371 | @Deactivate |
| 352 | protected void deactivate() { | 372 | protected void deactivate() { |
| 353 | cfgService.removeListener(cfgListener); | 373 | cfgService.removeListener(cfgListener); |
| 354 | - cfgService.unregisterConfigFactory(cfgDeviceFactory); | 374 | + cfgService.unregisterConfigFactory(deviceConfigFactory); |
| 355 | - cfgService.unregisterConfigFactory(cfgAppFactory); | 375 | + cfgService.unregisterConfigFactory(appConfigFactory); |
| 376 | + cfgService.unregisterConfigFactory(mcastConfigFactory); | ||
| 356 | 377 | ||
| 357 | // Withdraw ARP packet-in | 378 | // Withdraw ARP packet-in |
| 358 | TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); | 379 | TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); |
| ... | @@ -362,12 +383,20 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -362,12 +383,20 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 362 | packetService.removeProcessor(processor); | 383 | packetService.removeProcessor(processor); |
| 363 | linkService.removeListener(linkListener); | 384 | linkService.removeListener(linkListener); |
| 364 | deviceService.removeListener(deviceListener); | 385 | deviceService.removeListener(deviceListener); |
| 386 | + multicastRouteService.removeListener(mcastListener); | ||
| 387 | + | ||
| 365 | processor = null; | 388 | processor = null; |
| 366 | linkListener = null; | 389 | linkListener = null; |
| 367 | - deviceService = null; | 390 | + deviceListener = null; |
| 368 | - | ||
| 369 | groupHandlerMap.clear(); | 391 | groupHandlerMap.clear(); |
| 370 | 392 | ||
| 393 | + nsNextObjStore.destroy(); | ||
| 394 | + subnetNextObjStore.destroy(); | ||
| 395 | + portNextObjStore.destroy(); | ||
| 396 | + xConnectNextObjStore.destroy(); | ||
| 397 | + tunnelStore.destroy(); | ||
| 398 | + policyStore.destroy(); | ||
| 399 | + subnetVidStore.destroy(); | ||
| 371 | log.info("Stopped"); | 400 | log.info("Stopped"); |
| 372 | } | 401 | } |
| 373 | 402 | ||
| ... | @@ -1186,6 +1215,27 @@ public class SegmentRoutingManager implements SegmentRoutingService { | ... | @@ -1186,6 +1215,27 @@ public class SegmentRoutingManager implements SegmentRoutingService { |
| 1186 | } | 1215 | } |
| 1187 | } | 1216 | } |
| 1188 | 1217 | ||
| 1218 | + private class InternalMcastListener implements McastListener { | ||
| 1219 | + @Override | ||
| 1220 | + public void event(McastEvent event) { | ||
| 1221 | + switch (event.type()) { | ||
| 1222 | + case SOURCE_ADDED: | ||
| 1223 | + mcastEventHandler.processSourceAdded(event); | ||
| 1224 | + break; | ||
| 1225 | + case SINK_ADDED: | ||
| 1226 | + mcastEventHandler.processSinkAdded(event); | ||
| 1227 | + break; | ||
| 1228 | + case SINK_REMOVED: | ||
| 1229 | + mcastEventHandler.processSinkRemoved(event); | ||
| 1230 | + break; | ||
| 1231 | + case ROUTE_ADDED: | ||
| 1232 | + case ROUTE_REMOVED: | ||
| 1233 | + default: | ||
| 1234 | + break; | ||
| 1235 | + } | ||
| 1236 | + } | ||
| 1237 | + } | ||
| 1238 | + | ||
| 1189 | private static class BridgingTableObjectiveContext implements ObjectiveContext { | 1239 | private static class BridgingTableObjectiveContext implements ObjectiveContext { |
| 1190 | final MacAddress mac; | 1240 | final MacAddress mac; |
| 1191 | final VlanId vlanId; | 1241 | final VlanId vlanId; | ... | ... |
| ... | @@ -496,12 +496,22 @@ public class DeviceConfiguration implements DeviceProperties { | ... | @@ -496,12 +496,22 @@ public class DeviceConfiguration implements DeviceProperties { |
| 496 | return srinfo != null && srinfo.adjacencySids.containsKey(sid); | 496 | return srinfo != null && srinfo.adjacencySids.containsKey(sid); |
| 497 | } | 497 | } |
| 498 | 498 | ||
| 499 | + /** | ||
| 500 | + * Gets connect points for which segment routing does not install subnet rules. | ||
| 501 | + * | ||
| 502 | + * @return set of connect points | ||
| 503 | + */ | ||
| 499 | public Set<ConnectPoint> suppressSubnet() { | 504 | public Set<ConnectPoint> suppressSubnet() { |
| 500 | SegmentRoutingAppConfig appConfig = | 505 | SegmentRoutingAppConfig appConfig = |
| 501 | cfgService.getConfig(appId, SegmentRoutingAppConfig.class); | 506 | cfgService.getConfig(appId, SegmentRoutingAppConfig.class); |
| 502 | return (appConfig != null) ? appConfig.suppressSubnet() : ImmutableSet.of(); | 507 | return (appConfig != null) ? appConfig.suppressSubnet() : ImmutableSet.of(); |
| 503 | } | 508 | } |
| 504 | 509 | ||
| 510 | + /** | ||
| 511 | + * Gets connect points for which segment routing does not install host rules. | ||
| 512 | + * | ||
| 513 | + * @return set of connect points | ||
| 514 | + */ | ||
| 505 | public Set<ConnectPoint> suppressHost() { | 515 | public Set<ConnectPoint> suppressHost() { |
| 506 | SegmentRoutingAppConfig appConfig = | 516 | SegmentRoutingAppConfig appConfig = |
| 507 | cfgService.getConfig(appId, SegmentRoutingAppConfig.class); | 517 | cfgService.getConfig(appId, SegmentRoutingAppConfig.class); | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2015-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.grouphandler; | ||
| 18 | + | ||
| 19 | +import org.onlab.packet.IpAddress; | ||
| 20 | +import org.onosproject.net.DeviceId; | ||
| 21 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
| 22 | +import static com.google.common.base.Preconditions.checkArgument; | ||
| 23 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
| 24 | +import java.util.Objects; | ||
| 25 | + | ||
| 26 | +/** | ||
| 27 | + * Key of multicast next objective store. | ||
| 28 | + */ | ||
| 29 | +public class McastNextObjectiveStoreKey { | ||
| 30 | + private final IpAddress mcastIp; | ||
| 31 | + private final DeviceId deviceId; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * Constructs the key of multicast next objective store. | ||
| 35 | + * | ||
| 36 | + * @param mcastIp multicast group IP address | ||
| 37 | + * @param deviceId device ID | ||
| 38 | + */ | ||
| 39 | + public McastNextObjectiveStoreKey(IpAddress mcastIp, DeviceId deviceId) { | ||
| 40 | + checkNotNull(mcastIp, "mcastIp cannot be null"); | ||
| 41 | + checkNotNull(deviceId, "deviceId cannot be null"); | ||
| 42 | + checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address"); | ||
| 43 | + this.mcastIp = mcastIp; | ||
| 44 | + this.deviceId = deviceId; | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * Returns the multicast IP address of this key. | ||
| 49 | + * | ||
| 50 | + * @return multicast IP | ||
| 51 | + */ | ||
| 52 | + public IpAddress mcastIp() { | ||
| 53 | + return this.mcastIp; | ||
| 54 | + } | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * Returns the device ID of this key. | ||
| 58 | + * | ||
| 59 | + * @return device ID | ||
| 60 | + */ | ||
| 61 | + public DeviceId deviceId() { | ||
| 62 | + return this.deviceId; | ||
| 63 | + } | ||
| 64 | + | ||
| 65 | + @Override | ||
| 66 | + public boolean equals(Object o) { | ||
| 67 | + if (this == o) { | ||
| 68 | + return true; | ||
| 69 | + } | ||
| 70 | + if (!(o instanceof McastNextObjectiveStoreKey)) { | ||
| 71 | + return false; | ||
| 72 | + } | ||
| 73 | + McastNextObjectiveStoreKey that = | ||
| 74 | + (McastNextObjectiveStoreKey) o; | ||
| 75 | + return (Objects.equals(this.mcastIp, that.mcastIp) && | ||
| 76 | + Objects.equals(this.deviceId, that.deviceId)); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Override | ||
| 80 | + public int hashCode() { | ||
| 81 | + return Objects.hash(mcastIp, deviceId); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Override | ||
| 85 | + public String toString() { | ||
| 86 | + return toStringHelper(getClass()) | ||
| 87 | + .add("mcastIp", mcastIp) | ||
| 88 | + .add("deviceId", deviceId) | ||
| 89 | + .toString(); | ||
| 90 | + } | ||
| 91 | +} |
| ... | @@ -22,6 +22,7 @@ import java.util.Collections; | ... | @@ -22,6 +22,7 @@ import java.util.Collections; |
| 22 | import java.util.Optional; | 22 | import java.util.Optional; |
| 23 | import java.util.Set; | 23 | import java.util.Set; |
| 24 | 24 | ||
| 25 | +import static com.google.common.base.MoreObjects.toStringHelper; | ||
| 25 | import static com.google.common.base.Preconditions.checkNotNull; | 26 | import static com.google.common.base.Preconditions.checkNotNull; |
| 26 | 27 | ||
| 27 | /** | 28 | /** |
| ... | @@ -102,4 +103,13 @@ public final class McastRouteInfo { | ... | @@ -102,4 +103,13 @@ public final class McastRouteInfo { |
| 102 | return sinks; | 103 | return sinks; |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 106 | + @Override | ||
| 107 | + public String toString() { | ||
| 108 | + return toStringHelper(this) | ||
| 109 | + .add("route", route()) | ||
| 110 | + .add("sink", sink()) | ||
| 111 | + .add("source", source()) | ||
| 112 | + .add("sinks", sinks()) | ||
| 113 | + .toString(); | ||
| 114 | + } | ||
| 105 | } | 115 | } | ... | ... |
| ... | @@ -22,8 +22,6 @@ import java.util.Collection; | ... | @@ -22,8 +22,6 @@ import java.util.Collection; |
| 22 | import java.util.Collections; | 22 | import java.util.Collections; |
| 23 | import java.util.Deque; | 23 | import java.util.Deque; |
| 24 | import java.util.List; | 24 | import java.util.List; |
| 25 | -import java.util.Set; | ||
| 26 | -import java.util.concurrent.ConcurrentHashMap; | ||
| 27 | 25 | ||
| 28 | import com.google.common.collect.ImmutableList; | 26 | import com.google.common.collect.ImmutableList; |
| 29 | import com.google.common.collect.ImmutableSet; | 27 | import com.google.common.collect.ImmutableSet; |
| ... | @@ -279,17 +277,6 @@ public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline { | ... | @@ -279,17 +277,6 @@ public class CpqdOfdpa2Pipeline extends Ofdpa2Pipeline { |
| 279 | } | 277 | } |
| 280 | 278 | ||
| 281 | for (PortNumber pnum : portnums) { | 279 | for (PortNumber pnum : portnums) { |
| 282 | - // update storage | ||
| 283 | - groupHandler.port2Vlan.put(pnum, storeVlan); | ||
| 284 | - Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan); | ||
| 285 | - if (vlanPorts == null) { | ||
| 286 | - vlanPorts = Collections.newSetFromMap( | ||
| 287 | - new ConcurrentHashMap<PortNumber, Boolean>()); | ||
| 288 | - vlanPorts.add(pnum); | ||
| 289 | - groupHandler.vlan2Port.put(storeVlan, vlanPorts); | ||
| 290 | - } else { | ||
| 291 | - vlanPorts.add(pnum); | ||
| 292 | - } | ||
| 293 | // create rest of flowrule | 280 | // create rest of flowrule |
| 294 | selector.matchInPort(pnum); | 281 | selector.matchInPort(pnum); |
| 295 | FlowRule rule = DefaultFlowRule.builder() | 282 | FlowRule rule = DefaultFlowRule.builder() | ... | ... |
| ... | @@ -65,7 +65,6 @@ import java.util.Collection; | ... | @@ -65,7 +65,6 @@ import java.util.Collection; |
| 65 | import java.util.Collections; | 65 | import java.util.Collections; |
| 66 | import java.util.Deque; | 66 | import java.util.Deque; |
| 67 | import java.util.List; | 67 | import java.util.List; |
| 68 | -import java.util.Map; | ||
| 69 | import java.util.Objects; | 68 | import java.util.Objects; |
| 70 | import java.util.Set; | 69 | import java.util.Set; |
| 71 | import java.util.concurrent.ConcurrentHashMap; | 70 | import java.util.concurrent.ConcurrentHashMap; |
| ... | @@ -123,10 +122,6 @@ public class Ofdpa2GroupHandler { | ... | @@ -123,10 +122,6 @@ public class Ofdpa2GroupHandler { |
| 123 | // index number for group creation | 122 | // index number for group creation |
| 124 | private AtomicCounter nextIndex; | 123 | private AtomicCounter nextIndex; |
| 125 | 124 | ||
| 126 | - // local stores for port-vlan mapping | ||
| 127 | - protected Map<PortNumber, VlanId> port2Vlan = new ConcurrentHashMap<>(); | ||
| 128 | - protected Map<VlanId, Set<PortNumber>> vlan2Port = new ConcurrentHashMap<>(); | ||
| 129 | - | ||
| 130 | // local store for pending bucketAdds - by design there can only be one | 125 | // local store for pending bucketAdds - by design there can only be one |
| 131 | // pending bucket for a group | 126 | // pending bucket for a group |
| 132 | protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>(); | 127 | protected ConcurrentHashMap<Integer, NextObjective> pendingBuckets = new ConcurrentHashMap<>(); | ... | ... |
| ... | @@ -313,31 +313,22 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -313,31 +313,22 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
| 313 | } | 313 | } |
| 314 | 314 | ||
| 315 | VlanId assignedVlan = null; | 315 | VlanId assignedVlan = null; |
| 316 | - // For VLAN cross-connect packets, use the configured VLAN | ||
| 317 | if (vidCriterion != null) { | 316 | if (vidCriterion != null) { |
| 318 | - if (vidCriterion.vlanId() != VlanId.NONE) { | 317 | + // Use the VLAN in metadata whenever a metadata is provided |
| 318 | + if (filt.meta() != null) { | ||
| 319 | + assignedVlan = readVlanFromTreatment(filt.meta()); | ||
| 320 | + // Use the VLAN in criterion if metadata is not present and the traffic is tagged | ||
| 321 | + } else if (!vidCriterion.vlanId().equals(VlanId.NONE)) { | ||
| 319 | assignedVlan = vidCriterion.vlanId(); | 322 | assignedVlan = vidCriterion.vlanId(); |
| 323 | + } | ||
| 320 | 324 | ||
| 321 | - // For untagged packets, assign a VLAN ID | 325 | + if (assignedVlan == null) { |
| 322 | - } else { | 326 | + log.error("Driver fails to extract VLAN information. " |
| 323 | - if (filt.meta() == null) { | 327 | + + "Not proccessing VLAN filters on device {}.", deviceId); |
| 324 | - log.error("Missing metadata in filtering objective required " + | 328 | + log.debug("VLAN ID in criterion={}, metadata={}", |
| 325 | - "for vlan assignment in dev {}", deviceId); | 329 | + readVlanFromTreatment(filt.meta()), vidCriterion.vlanId()); |
| 326 | - fail(filt, ObjectiveError.BADPARAMS); | 330 | + fail(filt, ObjectiveError.BADPARAMS); |
| 327 | - return; | 331 | + return; |
| 328 | - } | ||
| 329 | - for (Instruction i : filt.meta().allInstructions()) { | ||
| 330 | - if (i instanceof ModVlanIdInstruction) { | ||
| 331 | - assignedVlan = ((ModVlanIdInstruction) i).vlanId(); | ||
| 332 | - } | ||
| 333 | - } | ||
| 334 | - if (assignedVlan == null) { | ||
| 335 | - log.error("Driver requires an assigned vlan-id to tag incoming " | ||
| 336 | - + "untagged packets. Not processing vlan filters on " | ||
| 337 | - + "device {}", deviceId); | ||
| 338 | - fail(filt, ObjectiveError.BADPARAMS); | ||
| 339 | - return; | ||
| 340 | - } | ||
| 341 | } | 332 | } |
| 342 | } | 333 | } |
| 343 | 334 | ||
| ... | @@ -457,22 +448,14 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -457,22 +448,14 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
| 457 | TrafficSelector.Builder preSelector = null; | 448 | TrafficSelector.Builder preSelector = null; |
| 458 | TrafficTreatment.Builder preTreatment = null; | 449 | TrafficTreatment.Builder preTreatment = null; |
| 459 | 450 | ||
| 460 | - | ||
| 461 | treatment.transition(TMAC_TABLE); | 451 | treatment.transition(TMAC_TABLE); |
| 462 | 452 | ||
| 463 | - VlanId storeVlan = null; | ||
| 464 | if (vidCriterion.vlanId() == VlanId.NONE) { | 453 | if (vidCriterion.vlanId() == VlanId.NONE) { |
| 465 | // untagged packets are assigned vlans | 454 | // untagged packets are assigned vlans |
| 466 | OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE); | 455 | OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE); |
| 467 | selector.extension(ofdpaMatchVlanVid, deviceId); | 456 | selector.extension(ofdpaMatchVlanVid, deviceId); |
| 468 | OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan); | 457 | OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan); |
| 469 | treatment.extension(ofdpaSetVlanVid, deviceId); | 458 | treatment.extension(ofdpaSetVlanVid, deviceId); |
| 470 | - // ofdpa requires an additional vlan match rule for the assigned vlan | ||
| 471 | - // and it does not require the push when setting the assigned vlan. | ||
| 472 | - // It also requires the extra rule to be sent to the switch before we | ||
| 473 | - // send the untagged match rule. | ||
| 474 | - // None of this in compliance with OF standard. | ||
| 475 | - storeVlan = assignedVlan; | ||
| 476 | 459 | ||
| 477 | preSelector = DefaultTrafficSelector.builder(); | 460 | preSelector = DefaultTrafficSelector.builder(); |
| 478 | OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan); | 461 | OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan); |
| ... | @@ -482,7 +465,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -482,7 +465,11 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
| 482 | } else { | 465 | } else { |
| 483 | OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId()); | 466 | OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId()); |
| 484 | selector.extension(ofdpaMatchVlanVid, deviceId); | 467 | selector.extension(ofdpaMatchVlanVid, deviceId); |
| 485 | - storeVlan = vidCriterion.vlanId(); | 468 | + |
| 469 | + if (!assignedVlan.equals(vidCriterion.vlanId())) { | ||
| 470 | + OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan); | ||
| 471 | + treatment.extension(ofdpaSetVlanVid, deviceId); | ||
| 472 | + } | ||
| 486 | } | 473 | } |
| 487 | 474 | ||
| 488 | // ofdpa cannot match on ALL portnumber, so we need to use separate | 475 | // ofdpa cannot match on ALL portnumber, so we need to use separate |
| ... | @@ -499,17 +486,6 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -499,17 +486,6 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
| 499 | } | 486 | } |
| 500 | 487 | ||
| 501 | for (PortNumber pnum : portnums) { | 488 | for (PortNumber pnum : portnums) { |
| 502 | - // update storage | ||
| 503 | - groupHandler.port2Vlan.put(pnum, storeVlan); | ||
| 504 | - Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan); | ||
| 505 | - if (vlanPorts == null) { | ||
| 506 | - vlanPorts = Collections.newSetFromMap( | ||
| 507 | - new ConcurrentHashMap<PortNumber, Boolean>()); | ||
| 508 | - vlanPorts.add(pnum); | ||
| 509 | - groupHandler.vlan2Port.put(storeVlan, vlanPorts); | ||
| 510 | - } else { | ||
| 511 | - vlanPorts.add(pnum); | ||
| 512 | - } | ||
| 513 | // create rest of flowrule | 489 | // create rest of flowrule |
| 514 | selector.matchInPort(pnum); | 490 | selector.matchInPort(pnum); |
| 515 | FlowRule rule = DefaultFlowRule.builder() | 491 | FlowRule rule = DefaultFlowRule.builder() |
| ... | @@ -1112,4 +1088,13 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline | ... | @@ -1112,4 +1088,13 @@ public class Ofdpa2Pipeline extends AbstractHandlerBehaviour implements Pipeline |
| 1112 | Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST); | 1088 | Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST); |
| 1113 | return (criterion == null) ? null : ((IPCriterion) criterion).ip(); | 1089 | return (criterion == null) ? null : ((IPCriterion) criterion).ip(); |
| 1114 | } | 1090 | } |
| 1091 | + | ||
| 1092 | + private static VlanId readVlanFromTreatment(TrafficTreatment treatment) { | ||
| 1093 | + for (Instruction i : treatment.allInstructions()) { | ||
| 1094 | + if (i instanceof ModVlanIdInstruction) { | ||
| 1095 | + return ((ModVlanIdInstruction) i).vlanId(); | ||
| 1096 | + } | ||
| 1097 | + } | ||
| 1098 | + return null; | ||
| 1099 | + } | ||
| 1115 | } | 1100 | } | ... | ... |
| 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.incubator.net.config.basics; | ||
| 18 | + | ||
| 19 | +import com.google.common.annotations.Beta; | ||
| 20 | +import org.onlab.packet.VlanId; | ||
| 21 | +import org.onosproject.core.ApplicationId; | ||
| 22 | +import org.onosproject.net.config.Config; | ||
| 23 | + | ||
| 24 | +/** | ||
| 25 | + * Configuration for multicast. | ||
| 26 | + */ | ||
| 27 | +@Beta | ||
| 28 | +public class McastConfig extends Config<ApplicationId> { | ||
| 29 | + private static final String INGRESS_VLAN = "ingressVlan"; | ||
| 30 | + private static final String EGRESS_VLAN = "egressVlan"; | ||
| 31 | + | ||
| 32 | + @Override | ||
| 33 | + public boolean isValid() { | ||
| 34 | + return hasOnlyFields(INGRESS_VLAN, EGRESS_VLAN) && | ||
| 35 | + ingressVlan() != null && egressVlan() != null; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * Gets ingress VLAN of multicast traffic. | ||
| 40 | + * | ||
| 41 | + * @return Ingress VLAN ID | ||
| 42 | + */ | ||
| 43 | + public VlanId ingressVlan() { | ||
| 44 | + if (!object.has(INGRESS_VLAN)) { | ||
| 45 | + return VlanId.NONE; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + try { | ||
| 49 | + return VlanId.vlanId(object.path(INGRESS_VLAN).asText()); | ||
| 50 | + } catch (IllegalArgumentException e) { | ||
| 51 | + return null; | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * Sets ingress VLAN of multicast traffic. | ||
| 57 | + * | ||
| 58 | + * @param vlanId Ingress VLAN ID | ||
| 59 | + * @return this {@link McastConfig} | ||
| 60 | + */ | ||
| 61 | + public McastConfig setIngressVlan(VlanId vlanId) { | ||
| 62 | + if (vlanId == null) { | ||
| 63 | + object.remove(INGRESS_VLAN); | ||
| 64 | + } else { | ||
| 65 | + object.put(INGRESS_VLAN, vlanId.toString()); | ||
| 66 | + } | ||
| 67 | + return this; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + /** | ||
| 71 | + * Gets egress VLAN of multicast traffic. | ||
| 72 | + * | ||
| 73 | + * @return Egress VLAN ID | ||
| 74 | + */ | ||
| 75 | + public VlanId egressVlan() { | ||
| 76 | + if (!object.has(EGRESS_VLAN)) { | ||
| 77 | + return VlanId.NONE; | ||
| 78 | + } | ||
| 79 | + | ||
| 80 | + try { | ||
| 81 | + return VlanId.vlanId(object.path(EGRESS_VLAN).asText()); | ||
| 82 | + } catch (IllegalArgumentException e) { | ||
| 83 | + return null; | ||
| 84 | + } | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * Sets egress VLAN of multicast traffic. | ||
| 89 | + * | ||
| 90 | + * @param vlanId Egress VLAN ID | ||
| 91 | + * @return this {@link McastConfig} | ||
| 92 | + */ | ||
| 93 | + public McastConfig setEgressVlan(VlanId vlanId) { | ||
| 94 | + if (vlanId == null) { | ||
| 95 | + object.remove(EGRESS_VLAN); | ||
| 96 | + } else { | ||
| 97 | + object.put(EGRESS_VLAN, vlanId.toString()); | ||
| 98 | + } | ||
| 99 | + return this; | ||
| 100 | + } | ||
| 101 | +} |
incubator/api/src/test/java/org/onosproject/incubator/net/config/basics/McastConfigTest.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.incubator.net.config.basics; | ||
| 18 | + | ||
| 19 | +import com.fasterxml.jackson.databind.JsonNode; | ||
| 20 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 21 | +import com.google.common.annotations.Beta; | ||
| 22 | +import org.junit.Before; | ||
| 23 | +import org.junit.Test; | ||
| 24 | +import org.onlab.packet.VlanId; | ||
| 25 | +import org.onosproject.TestApplicationId; | ||
| 26 | +import org.onosproject.core.ApplicationId; | ||
| 27 | +import org.onosproject.core.CoreService; | ||
| 28 | +import org.onosproject.net.config.Config; | ||
| 29 | +import org.onosproject.net.config.ConfigApplyDelegate; | ||
| 30 | + | ||
| 31 | +import java.io.InputStream; | ||
| 32 | + | ||
| 33 | +import static org.hamcrest.Matchers.is; | ||
| 34 | +import static org.junit.Assert.*; | ||
| 35 | + | ||
| 36 | +/** | ||
| 37 | + * Tests for class {@link McastConfig}. | ||
| 38 | + */ | ||
| 39 | +@Beta | ||
| 40 | +public class McastConfigTest { | ||
| 41 | + private static final TestApplicationId APP_ID = | ||
| 42 | + new TestApplicationId(CoreService.CORE_APP_NAME); | ||
| 43 | + private McastConfig config; | ||
| 44 | + private McastConfig invalidConfig; | ||
| 45 | + | ||
| 46 | + private static final VlanId INGRESS_VLAN_1 = VlanId.NONE; | ||
| 47 | + private static final VlanId EGRESS_VLAN_1 = VlanId.NONE; | ||
| 48 | + private static final VlanId INGRESS_VLAN_2 = VlanId.vlanId((short) 100); | ||
| 49 | + private static final VlanId EGRESS_VLAN_2 = VlanId.vlanId((short) 100); | ||
| 50 | + | ||
| 51 | + /** | ||
| 52 | + * Initialize test related variables. | ||
| 53 | + * | ||
| 54 | + * @throws Exception | ||
| 55 | + */ | ||
| 56 | + @Before | ||
| 57 | + public void setUp() throws Exception { | ||
| 58 | + InputStream jsonStream = McastConfigTest.class | ||
| 59 | + .getResourceAsStream("/mcast-config.json"); | ||
| 60 | + InputStream invalidJsonStream = McastConfigTest.class | ||
| 61 | + .getResourceAsStream("/mcast-config-invalid.json"); | ||
| 62 | + | ||
| 63 | + ApplicationId subject = APP_ID; | ||
| 64 | + String key = CoreService.CORE_APP_NAME; | ||
| 65 | + ObjectMapper mapper = new ObjectMapper(); | ||
| 66 | + JsonNode jsonNode = mapper.readTree(jsonStream); | ||
| 67 | + JsonNode invalidJsonNode = mapper.readTree(invalidJsonStream); | ||
| 68 | + ConfigApplyDelegate delegate = new MockDelegate(); | ||
| 69 | + | ||
| 70 | + config = new McastConfig(); | ||
| 71 | + config.init(subject, key, jsonNode, mapper, delegate); | ||
| 72 | + invalidConfig = new McastConfig(); | ||
| 73 | + invalidConfig.init(subject, key, invalidJsonNode, mapper, delegate); | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + /** | ||
| 77 | + * Tests config validity. | ||
| 78 | + * | ||
| 79 | + * @throws Exception | ||
| 80 | + */ | ||
| 81 | + @Test | ||
| 82 | + public void isValid() throws Exception { | ||
| 83 | + assertTrue(config.isValid()); | ||
| 84 | + assertFalse(invalidConfig.isValid()); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + /** | ||
| 88 | + * Tests ingress VLAN getter. | ||
| 89 | + * | ||
| 90 | + * @throws Exception | ||
| 91 | + */ | ||
| 92 | + @Test | ||
| 93 | + public void ingressVlan() throws Exception { | ||
| 94 | + VlanId ingressVlan = config.ingressVlan(); | ||
| 95 | + assertNotNull("ingressVlan should not be null", ingressVlan); | ||
| 96 | + assertThat(ingressVlan, is(INGRESS_VLAN_1)); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + /** | ||
| 100 | + * Tests ingress VLAN setter. | ||
| 101 | + * | ||
| 102 | + * @throws Exception | ||
| 103 | + */ | ||
| 104 | + @Test | ||
| 105 | + public void setIngressVlan() throws Exception { | ||
| 106 | + config.setIngressVlan(INGRESS_VLAN_2); | ||
| 107 | + | ||
| 108 | + VlanId ingressVlan = config.ingressVlan(); | ||
| 109 | + assertNotNull("ingressVlan should not be null", ingressVlan); | ||
| 110 | + assertThat(ingressVlan, is(INGRESS_VLAN_2)); | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + /** | ||
| 114 | + * Tests egress VLAN getter. | ||
| 115 | + * | ||
| 116 | + * @throws Exception | ||
| 117 | + */ | ||
| 118 | + @Test | ||
| 119 | + public void egressVlan() throws Exception { | ||
| 120 | + VlanId egressVlan = config.egressVlan(); | ||
| 121 | + assertNotNull("egressVlan should not be null", egressVlan); | ||
| 122 | + assertThat(egressVlan, is(EGRESS_VLAN_1)); | ||
| 123 | + } | ||
| 124 | + | ||
| 125 | + /** | ||
| 126 | + * Tests egress VLAN setter. | ||
| 127 | + * | ||
| 128 | + * @throws Exception | ||
| 129 | + */ | ||
| 130 | + @Test | ||
| 131 | + public void setEgressVlan() throws Exception { | ||
| 132 | + config.setEgressVlan(EGRESS_VLAN_2); | ||
| 133 | + | ||
| 134 | + VlanId egressVlan = config.egressVlan(); | ||
| 135 | + assertNotNull("egressVlan should not be null", egressVlan); | ||
| 136 | + assertThat(egressVlan, is(EGRESS_VLAN_2)); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + private class MockDelegate implements ConfigApplyDelegate { | ||
| 140 | + @Override | ||
| 141 | + public void onApply(Config config) { | ||
| 142 | + } | ||
| 143 | + } | ||
| 144 | +} | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
-
Please register or login to post a comment