diff options
Diffstat (limited to 'framework/src/onos/providers')
33 files changed, 2929 insertions, 317 deletions
diff --git a/framework/src/onos/providers/bgp/app/app.xml b/framework/src/onos/providers/bgp/app/app.xml new file mode 100755 index 00000000..072003b9 --- /dev/null +++ b/framework/src/onos/providers/bgp/app/app.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<app name="org.onosproject.bgp" origin="ON.Lab" version="${project.version}" + featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" + features="${project.artifactId}"> + <description>${project.description}</description> + <artifact>mvn:${project.groupId}/onos-bgpio/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-bgp-api/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-bgp-ctl/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-bgp-provider-topology/${project.version}</artifact> +</app> diff --git a/framework/src/onos/providers/bgp/app/features.xml b/framework/src/onos/providers/bgp/app/features.xml new file mode 100755 index 00000000..b24d3ffe --- /dev/null +++ b/framework/src/onos/providers/bgp/app/features.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> + <feature name="${project.artifactId}" version="${project.version}" + description="${project.description}"> + <feature>onos-api</feature> + <bundle>mvn:${project.groupId}/onos-bgpio/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-bgp-api/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-bgp-ctl/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-bgp-provider-topology/${project.version}</bundle> + </feature> +</features> diff --git a/framework/src/onos/providers/bgp/app/pom.xml b/framework/src/onos/providers/bgp/app/pom.xml new file mode 100755 index 00000000..4ac3acdc --- /dev/null +++ b/framework/src/onos/providers/bgp/app/pom.xml @@ -0,0 +1,52 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-providers</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-bgp-app</artifactId> + <packaging>pom</packaging> + <description>BGP protocol southbound providers</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgpio</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-api</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-ctl</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-provider-topology</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/framework/src/onos/providers/bgp/pom.xml b/framework/src/onos/providers/bgp/pom.xml new file mode 100755 index 00000000..b2e3a51d --- /dev/null +++ b/framework/src/onos/providers/bgp/pom.xml @@ -0,0 +1,58 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-providers</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>onos-bgp-providers</artifactId> + <packaging>pom</packaging> + <description>BGP-LS protocol providers root</description> + <modules> + <module>topology</module> + <module>app</module> + </modules> + <dependencies> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgpio</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-api</artifactId> + <classifier>tests</classifier> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>io.netty</groupId> + <artifactId>netty</artifactId> + </dependency> + + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onlab-junit</artifactId> + </dependency> + </dependencies> +</project> diff --git a/framework/src/onos/providers/bgp/topology/pom.xml b/framework/src/onos/providers/bgp/topology/pom.xml new file mode 100755 index 00000000..b033742d --- /dev/null +++ b/framework/src/onos/providers/bgp/topology/pom.xml @@ -0,0 +1,33 @@ +<!-- + ~ Copyright 2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-providers</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + <artifactId>onos-bgp-provider-topology</artifactId> + <packaging>bundle</packaging> + <description>BGP topology provider</description> + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-bgp-api</artifactId> + </dependency> + </dependencies> +</project> diff --git a/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java b/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java new file mode 100755 index 00000000..34803663 --- /dev/null +++ b/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProvider.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ + +package org.onosproject.provider.bgp.topology.impl; + +import static org.onosproject.bgp.controller.BgpDpid.uri; +import static org.onosproject.net.DeviceId.deviceId; + +import org.onlab.packet.ChassisId; +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onosproject.bgp.controller.BgpController; +import org.onosproject.bgp.controller.BgpDpid; +import org.onosproject.bgp.controller.BgpNodeListener; +import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSNlriVer4; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.device.DefaultDeviceDescription; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Provider which uses an BGP controller to detect network infrastructure topology. + */ +@Component(immediate = true) +public class BgpTopologyProvider extends AbstractProvider implements DeviceProvider { + + public BgpTopologyProvider() { + super(new ProviderId("bgp", "org.onosproject.provider.bgp")); + } + + private static final Logger log = LoggerFactory.getLogger(BgpTopologyProvider.class); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceProviderRegistry deviceProviderRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected BgpController controller; + + private DeviceProviderService deviceProviderService; + + private InternalBgpProvider listener = new InternalBgpProvider(); + private static final String UNKNOWN = "unknown"; + + @Activate + public void activate() { + deviceProviderService = deviceProviderRegistry.register(this); + controller.addListener(listener); + } + + @Deactivate + public void deactivate() { + controller.removeListener(listener); + } + + /* + * Implements device and link update. + */ + private class InternalBgpProvider implements BgpNodeListener { + + @Override + public void addNode(BgpNodeLSNlriVer4 nodeNlri) { + log.debug("Add node {}", nodeNlri.toString()); + + if (deviceProviderService == null) { + return; + } + BgpDpid nodeUri = new BgpDpid(nodeNlri); + DeviceId deviceId = deviceId(uri(nodeUri.toString())); + ChassisId cId = new ChassisId(); + + DeviceDescription description = new DefaultDeviceDescription(uri(nodeUri.toString()), Device.Type.ROUTER, + UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN, cId); + deviceProviderService.deviceConnected(deviceId, description); + + } + + @Override + public void deleteNode(BgpNodeLSNlriVer4 nodeNlri) { + log.debug("Delete node {}", nodeNlri.toString()); + + if (deviceProviderService == null) { + return; + } + + BgpDpid nodeUri = new BgpDpid(nodeNlri); + deviceProviderService.deviceDisconnected(deviceId(uri(nodeUri.toString()))); + } + } + + @Override + public void triggerProbe(DeviceId deviceId) { + // TODO Auto-generated method stub + } + + @Override + public void roleChanged(DeviceId deviceId, MastershipRole newRole) { + } + + @Override + public boolean isReachable(DeviceId deviceId) { + // TODO Auto-generated method stub + return true; + } +} diff --git a/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/package-info.java b/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/package-info.java new file mode 100755 index 00000000..0287ec7e --- /dev/null +++ b/framework/src/onos/providers/bgp/topology/src/main/java/org/onosproject/provider/bgp/topology/impl/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + *Provider that uses BGP controller as a means of infrastructure topology discovery. + */ +package org.onosproject.provider.bgp.topology.impl;
\ No newline at end of file diff --git a/framework/src/onos/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProviderTest.java b/framework/src/onos/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProviderTest.java new file mode 100755 index 00000000..30bb4470 --- /dev/null +++ b/framework/src/onos/providers/bgp/topology/src/test/java/org/onosproject/provider/bgp/topology/impl/BgpTopologyProviderTest.java @@ -0,0 +1,301 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + */ +package org.onosproject.provider.bgp.topology.impl; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.bgp.controller.BgpCfg; +import org.onosproject.bgp.controller.BgpController; +import org.onosproject.bgp.controller.BgpId; +import org.onosproject.bgp.controller.BgpPeer; +import org.onosproject.bgp.controller.BgpNodeListener; +import org.onosproject.bgp.controller.BgpPeerManager; +import org.onosproject.bgpio.exceptions.BgpParseException; +import org.onosproject.bgpio.protocol.BgpMessage; +import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSIdentifier; +import org.onosproject.bgpio.protocol.linkstate.BgpNodeLSNlriVer4; +import org.onosproject.bgpio.protocol.linkstate.NodeDescriptors; +import org.onosproject.bgpio.types.AutonomousSystemTlv; +import org.onosproject.bgpio.types.BgpValueType; +import org.onosproject.bgpio.types.RouteDistinguisher; +import org.onosproject.bgpio.util.Constants; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.device.DeviceDescription; +import org.onosproject.net.device.DeviceProvider; +import org.onosproject.net.device.DeviceProviderRegistry; +import org.onosproject.net.device.DeviceProviderService; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.device.PortStatistics; +import org.onosproject.net.provider.ProviderId; + +public class BgpTopologyProviderTest { + + private static final DeviceId DID1 = DeviceId + .deviceId("bgp:bgpls://0:direct:0/&=bgpnodelsidentifier%7bnodedescriptors=nodedescriptors%7bdestype=512," + + "%20deslength=4,%20subtlvs=[autonomoussystemtlv%7btype=512,%20length=4,%20asnum=100%7d]%7d%7d"); + private static final DeviceId DID2 = DeviceId + .deviceId("bgp:bgpls://0:direct:0/&=bgpnodelsidentifier%7bnodedescriptors=nodedescriptors%7bdestype=512," + + "%20deslength=4,%20subtlvs=[autonomoussystemtlv%7btype=512,%20length=4,%20asnum=10%7d]%7d%7d"); + private static final DeviceId DID3 = DeviceId + .deviceId("bgp:bgpls://direct:0/&=nodedescriptors%7bdestype=512,%20deslength=4," + + "%20subtlvs=[autonomoussystemtlv%7btype=512,%20length=4,%20asnum=100%7d]%7d"); + private final BgpTopologyProvider provider = new BgpTopologyProvider(); + private final TestDeviceRegistry nodeRegistry = new TestDeviceRegistry(); + private final TestController controller = new TestController(); + + @Before + public void startUp() { + provider.deviceProviderRegistry = nodeRegistry; + provider.controller = controller; + provider.activate(); + assertNotNull("provider should be registered", nodeRegistry.provider); + assertNotNull("listener should be registered", controller.nodeListener); + } + + @After + public void tearDown() { + provider.deactivate(); + assertNull("listener should be removed", controller.nodeListener); + provider.controller = null; + provider.deviceProviderRegistry = null; + } + + /* Class implement device test registry */ + private class TestDeviceRegistry implements DeviceProviderRegistry { + DeviceProvider provider; + + Set<DeviceId> connected = new HashSet<>(); + + @Override + public DeviceProviderService register(DeviceProvider provider) { + this.provider = provider; + return new TestProviderService(); + } + + @Override + public void unregister(DeviceProvider provider) { + } + + @Override + public Set<ProviderId> getProviders() { + return null; + } + + private class TestProviderService implements DeviceProviderService { + + @Override + public DeviceProvider provider() { + return null; + } + + @Override + public void deviceConnected(DeviceId deviceId, DeviceDescription deviceDescription) { + if (deviceId.equals(DID1)) { + connected.add(deviceId); + } + } + + @Override + public void deviceDisconnected(DeviceId deviceId) { + if (deviceId.equals(DID1)) { + connected.remove(deviceId); + } + } + + @Override + public void updatePorts(DeviceId deviceId, List<PortDescription> portDescriptions) { + // TODO Auto-generated method stub + + } + + @Override + public void portStatusChanged(DeviceId deviceId, PortDescription portDescription) { + // TODO Auto-generated method stub + + } + + @Override + public void receivedRoleReply(DeviceId deviceId, MastershipRole requested, MastershipRole response) { + // TODO Auto-generated method stub + + } + + @Override + public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) { + // TODO Auto-generated method stub + + } + } + } + + /* class implement test controller */ + private class TestController implements BgpController { + protected Set<BgpNodeListener> nodeListener = new CopyOnWriteArraySet<>(); + + @Override + public void addListener(BgpNodeListener nodeListener) { + this.nodeListener.add(nodeListener); + } + + @Override + public void removeListener(BgpNodeListener nodeListener) { + this.nodeListener = null; + } + + @Override + public Iterable<BgpPeer> getPeers() { + // TODO Auto-generated method stub + return null; + } + + @Override + public BgpPeer getPeer(BgpId bgpId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void writeMsg(BgpId bgpId, BgpMessage msg) { + // TODO Auto-generated method stub + + } + + @Override + public void processBGPPacket(BgpId bgpId, BgpMessage msg) throws BgpParseException { + // TODO Auto-generated method stub + + } + + @Override + public void closeConnectedPeers() { + // TODO Auto-generated method stub + + } + + @Override + public BgpCfg getConfig() { + // TODO Auto-generated method stub + return null; + } + + @Override + public int connectedPeerCount() { + // TODO Auto-generated method stub + return 0; + } + + + @Override + public BgpPeerManager peerManager() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map<BgpId, BgpPeer> connectedPeers() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set<BgpNodeListener> listener() { + // TODO Auto-generated method stub + return null; + } + } + + /* Validate node is added to the device validating URI, RIB should get updated properly */ + @Test + public void bgpTopologyProviderTestAddDevice1() { + int deviceAddCount = 0; + LinkedList<BgpValueType> subTlvs; + subTlvs = new LinkedList<>(); + BgpValueType tlv = new AutonomousSystemTlv(100); + short deslength = AutonomousSystemTlv.LENGTH; + short desType = AutonomousSystemTlv.TYPE; + + subTlvs.add(tlv); + BgpNodeLSIdentifier localNodeDescriptors = new BgpNodeLSIdentifier(new NodeDescriptors(subTlvs, deslength, + desType)); + BgpNodeLSNlriVer4 nodeNlri = new BgpNodeLSNlriVer4(0, (byte) Constants.DIRECT, localNodeDescriptors, false, + new RouteDistinguisher()); + + nodeNlri.setNodeLSIdentifier(localNodeDescriptors); + for (BgpNodeListener l : controller.nodeListener) { + l.addNode(nodeNlri); + deviceAddCount = nodeRegistry.connected.size(); + assertTrue(deviceAddCount == 1); + l.deleteNode(nodeNlri); + deviceAddCount = nodeRegistry.connected.size(); + assertTrue(deviceAddCount == 0); + } + } + + /* Validate node is not added to the device for invalid URI, RIB count should be zero */ + @Test + public void bgpTopologyProviderTestAddDevice2() { + LinkedList<BgpValueType> subTlvs; + BgpValueType tlv = new AutonomousSystemTlv(10); + short deslength = AutonomousSystemTlv.LENGTH; + short desType = AutonomousSystemTlv.TYPE; + + subTlvs = new LinkedList<>(); + subTlvs.add(tlv); + BgpNodeLSIdentifier localNodeDescriptors = new BgpNodeLSIdentifier(new NodeDescriptors(subTlvs, deslength, + desType)); + BgpNodeLSNlriVer4 nodeNlri = new BgpNodeLSNlriVer4(0, (byte) Constants.DIRECT, localNodeDescriptors, false, + new RouteDistinguisher()); + + nodeNlri.setNodeLSIdentifier(localNodeDescriptors); + for (BgpNodeListener l : controller.nodeListener) { + l.addNode(nodeNlri); + assertTrue("Failed to add device", (nodeRegistry.connected.size() == 0)); + } + } + + /* Delete node when node does not exist, RIB count should be zero */ + @Test + public void bgpTopologyProviderTestAddDevice3() { + LinkedList<BgpValueType> subTlvs; + BgpValueType tlv = new AutonomousSystemTlv(10); + short deslength = AutonomousSystemTlv.LENGTH; + short desType = AutonomousSystemTlv.TYPE; + + subTlvs = new LinkedList<>(); + subTlvs.add(tlv); + BgpNodeLSIdentifier localNodeDescriptors = new BgpNodeLSIdentifier(new NodeDescriptors(subTlvs, deslength, + desType)); + BgpNodeLSNlriVer4 nodeNlri = new BgpNodeLSNlriVer4(0, (byte) Constants.DIRECT, localNodeDescriptors, false, + new RouteDistinguisher()); + + nodeNlri.setNodeLSIdentifier(localNodeDescriptors); + for (BgpNodeListener l : controller.nodeListener) { + l.deleteNode(nodeNlri); + assertTrue("Failed to add device", (nodeRegistry.connected.size() == 0)); + } + } +} diff --git a/framework/src/onos/providers/lldp/pom.xml b/framework/src/onos/providers/lldp/pom.xml index 7bf92ed2..f9052760 100644 --- a/framework/src/onos/providers/lldp/pom.xml +++ b/framework/src/onos/providers/lldp/pom.xml @@ -55,5 +55,9 @@ <scope>test</scope> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.core</groupId> + <artifactId>jackson-databind</artifactId> + </dependency> </dependencies> </project> diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java new file mode 100644 index 00000000..32b12d47 --- /dev/null +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromDevice.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.lldp.impl; + +import org.onosproject.net.DeviceId; +import org.onosproject.net.config.basics.BasicFeatureConfig; + +/** + * Configuration to see LinkDiscovery should be enabled on Device. + */ +public class LinkDiscoveryFromDevice extends BasicFeatureConfig<DeviceId> { + + public LinkDiscoveryFromDevice() { + // default: enabled + super(true); + } + +} diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java new file mode 100644 index 00000000..b9a502f8 --- /dev/null +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscoveryFromPort.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.lldp.impl; + +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.config.basics.BasicFeatureConfig; + +/** + * Configuration to see LinkDiscovery should be enabled on a Port. + */ +public class LinkDiscoveryFromPort extends BasicFeatureConfig<ConnectPoint> { + + public LinkDiscoveryFromPort() { + // default: enabled + super(true); + } + +} diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java new file mode 100644 index 00000000..94abebaa --- /dev/null +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LldpLinkProvider.java @@ -0,0 +1,760 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.lldp.impl; + +import static com.google.common.base.Strings.isNullOrEmpty; +import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.onlab.packet.Ethernet.TYPE_BSN; +import static org.onlab.packet.Ethernet.TYPE_LLDP; +import static org.onlab.util.Tools.get; +import static org.onlab.util.Tools.groupedThreads; +import static org.onosproject.net.Link.Type.DIRECT; +import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY; +import static org.onosproject.net.config.basics.SubjectFactories.CONNECT_POINT_SUBJECT_FACTORY; +import static org.onosproject.net.config.basics.SubjectFactories.DEVICE_SUBJECT_FACTORY; +import static org.slf4j.LoggerFactory.getLogger; + +import java.util.Dictionary; +import java.util.EnumSet; +import java.util.Map; +import java.util.Optional; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Modified; +import org.apache.felix.scr.annotations.Property; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.onlab.packet.Ethernet; +import org.onosproject.cfg.ComponentConfigService; +import org.onosproject.cluster.ClusterService; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.mastership.MastershipEvent; +import org.onosproject.mastership.MastershipListener; +import org.onosproject.mastership.MastershipService; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.LinkKey; +import org.onosproject.net.Port; +import org.onosproject.net.config.ConfigFactory; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigRegistry; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.flow.DefaultTrafficSelector; +import org.onosproject.net.flow.TrafficSelector; +import org.onosproject.net.link.DefaultLinkDescription; +import org.onosproject.net.link.LinkProvider; +import org.onosproject.net.link.LinkProviderRegistry; +import org.onosproject.net.link.LinkProviderService; +import org.onosproject.net.link.LinkService; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketPriority; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketService; +import org.onosproject.net.provider.AbstractProvider; +import org.onosproject.net.provider.ProviderId; +import org.osgi.service.component.ComponentContext; +import org.slf4j.Logger; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +/** + * Provider which uses LLDP and BDDP packets to detect network infrastructure links. + */ +@Component(immediate = true) +public class LldpLinkProvider extends AbstractProvider implements LinkProvider { + + private static final String PROVIDER_NAME = "org.onosproject.provider.lldp"; + + private static final String FORMAT = + "Settings: enabled={}, useBDDP={}, probeRate={}, " + + "staleLinkAge={}"; + + // When a Device/Port has this annotation, do not send out LLDP/BDDP + public static final String NO_LLDP = "no-lldp"; + + private final Logger log = getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected CoreService coreService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected LinkProviderRegistry providerRegistry; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected DeviceService deviceService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected LinkService linkService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected PacketService packetService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MastershipService masterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ComponentConfigService cfgService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected ClusterService clusterService; + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected NetworkConfigRegistry cfgRegistry; + + private LinkProviderService providerService; + + private ScheduledExecutorService executor; + + // TODO: Add sanity checking for the configurable params based on the delays + private static final long DEVICE_SYNC_DELAY = 5; + private static final long LINK_PRUNER_DELAY = 3; + + private static final String PROP_ENABLED = "enabled"; + @Property(name = PROP_ENABLED, boolValue = true, + label = "If false, link discovery is disabled") + private boolean enabled = false; + + private static final String PROP_USE_BDDP = "useBDDP"; + @Property(name = PROP_USE_BDDP, boolValue = true, + label = "Use BDDP for link discovery") + private boolean useBddp = true; + + private static final String PROP_PROBE_RATE = "probeRate"; + private static final int DEFAULT_PROBE_RATE = 3_000; + @Property(name = PROP_PROBE_RATE, intValue = DEFAULT_PROBE_RATE, + label = "LLDP and BDDP probe rate specified in millis") + private int probeRate = DEFAULT_PROBE_RATE; + + private static final String PROP_STALE_LINK_AGE = "staleLinkAge"; + private static final int DEFAULT_STALE_LINK_AGE = 10_000; + @Property(name = PROP_STALE_LINK_AGE, intValue = DEFAULT_STALE_LINK_AGE, + label = "Number of millis beyond which links will be considered stale") + private int staleLinkAge = DEFAULT_STALE_LINK_AGE; + + private final DiscoveryContext context = new InternalDiscoveryContext(); + private final InternalRoleListener roleListener = new InternalRoleListener(); + private final InternalDeviceListener deviceListener = new InternalDeviceListener(); + private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor(); + + // Device link discovery helpers. + protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>(); + + // Most recent time a tracked link was seen; links are tracked if their + // destination connection point is mastered by this controller instance. + private final Map<LinkKey, Long> linkTimes = Maps.newConcurrentMap(); + + private ApplicationId appId; + + static final SuppressionRules DEFAULT_RULES + = new SuppressionRules(EnumSet.of(Device.Type.ROADM), + ImmutableMap.of(NO_LLDP, SuppressionRules.ANY_VALUE)); + + private SuppressionRules rules = LldpLinkProvider.DEFAULT_RULES; + + public static final String CONFIG_KEY = "suppression"; + public static final String FEATURE_NAME = "linkDiscovery"; + + private final Set<ConfigFactory<?, ?>> factories = ImmutableSet.of( + new ConfigFactory<ApplicationId, SuppressionConfig>(APP_SUBJECT_FACTORY, + SuppressionConfig.class, + CONFIG_KEY) { + @Override + public SuppressionConfig createConfig() { + return new SuppressionConfig(); + } + }, + new ConfigFactory<DeviceId, LinkDiscoveryFromDevice>(DEVICE_SUBJECT_FACTORY, + LinkDiscoveryFromDevice.class, FEATURE_NAME) { + @Override + public LinkDiscoveryFromDevice createConfig() { + return new LinkDiscoveryFromDevice(); + } + }, + new ConfigFactory<ConnectPoint, LinkDiscoveryFromPort>(CONNECT_POINT_SUBJECT_FACTORY, + LinkDiscoveryFromPort.class, FEATURE_NAME) { + @Override + public LinkDiscoveryFromPort createConfig() { + return new LinkDiscoveryFromPort(); + } + } + ); + + private final InternalConfigListener cfgListener = new InternalConfigListener(); + + + /** + * Creates an OpenFlow link provider. + */ + public LldpLinkProvider() { + super(new ProviderId("lldp", PROVIDER_NAME)); + } + + @Activate + public void activate(ComponentContext context) { + cfgService.registerProperties(getClass()); + appId = coreService.registerApplication(PROVIDER_NAME); + + cfgRegistry.addListener(cfgListener); + factories.forEach(cfgRegistry::registerConfigFactory); + + SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class); + if (cfg == null) { + // If no configuration is found, register default. + cfg = cfgRegistry.addConfig(appId, SuppressionConfig.class); + cfg.deviceTypes(DEFAULT_RULES.getSuppressedDeviceType()) + .annotation(DEFAULT_RULES.getSuppressedAnnotation()) + .apply(); + } + cfgListener.reconfigureSuppressionRules(cfg); + + modified(context); + log.info("Started"); + } + + @Deactivate + public void deactivate() { + cfgRegistry.removeListener(cfgListener); + factories.forEach(cfgRegistry::unregisterConfigFactory); + + cfgService.unregisterProperties(getClass(), false); + disable(); + log.info("Stopped"); + } + + @Modified + public void modified(ComponentContext context) { + Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties(); + + boolean newEnabled, newUseBddp; + int newProbeRate, newStaleLinkAge; + try { + String s = get(properties, PROP_ENABLED); + newEnabled = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim()); + + s = get(properties, PROP_USE_BDDP); + newUseBddp = isNullOrEmpty(s) || Boolean.parseBoolean(s.trim()); + + s = get(properties, PROP_PROBE_RATE); + newProbeRate = isNullOrEmpty(s) ? probeRate : Integer.parseInt(s.trim()); + + s = get(properties, PROP_STALE_LINK_AGE); + newStaleLinkAge = isNullOrEmpty(s) ? staleLinkAge : Integer.parseInt(s.trim()); + + } catch (NumberFormatException e) { + log.warn("Component configuration had invalid values", e); + newEnabled = enabled; + newUseBddp = useBddp; + newProbeRate = probeRate; + newStaleLinkAge = staleLinkAge; + } + + boolean wasEnabled = enabled; + + enabled = newEnabled; + useBddp = newUseBddp; + probeRate = newProbeRate; + staleLinkAge = newStaleLinkAge; + + if (!wasEnabled && enabled) { + enable(); + } else if (wasEnabled && !enabled) { + disable(); + } else { + if (enabled) { + // update all discovery helper state + loadDevices(); + } + } + + log.info(FORMAT, enabled, useBddp, probeRate, staleLinkAge); + } + + /** + * Enables link discovery processing. + */ + private void enable() { + providerService = providerRegistry.register(this); + masterService.addListener(roleListener); + deviceService.addListener(deviceListener); + packetService.addProcessor(packetProcessor, PacketProcessor.advisor(0)); + + loadDevices(); + + executor = newSingleThreadScheduledExecutor(groupedThreads("onos/link", "discovery-%d")); + executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), + DEVICE_SYNC_DELAY, DEVICE_SYNC_DELAY, SECONDS); + executor.scheduleAtFixedRate(new LinkPrunerTask(), + LINK_PRUNER_DELAY, LINK_PRUNER_DELAY, SECONDS); + + requestIntercepts(); + } + + /** + * Disables link discovery processing. + */ + private void disable() { + withdrawIntercepts(); + + providerRegistry.unregister(this); + masterService.removeListener(roleListener); + deviceService.removeListener(deviceListener); + packetService.removeProcessor(packetProcessor); + + + if (executor != null) { + executor.shutdownNow(); + } + discoverers.values().forEach(LinkDiscovery::stop); + discoverers.clear(); + + providerService = null; + } + + /** + * Loads available devices and registers their ports to be probed. + */ + private void loadDevices() { + if (!enabled) { + return; + } + deviceService.getAvailableDevices() + .forEach(d -> updateDevice(d) + .ifPresent(ld -> updatePorts(ld, d.id()))); + } + + private boolean isBlacklisted(DeviceId did) { + LinkDiscoveryFromDevice cfg = cfgRegistry.getConfig(did, LinkDiscoveryFromDevice.class); + if (cfg == null) { + return false; + } + return !cfg.enabled(); + } + + private boolean isBlacklisted(ConnectPoint cp) { + // if parent device is blacklisted, so is the port + if (isBlacklisted(cp.deviceId())) { + return true; + } + LinkDiscoveryFromPort cfg = cfgRegistry.getConfig(cp, LinkDiscoveryFromPort.class); + if (cfg == null) { + return false; + } + return !cfg.enabled(); + } + + private boolean isBlacklisted(Port port) { + return isBlacklisted(new ConnectPoint(port.element().id(), port.number())); + } + + /** + * Updates discovery helper for specified device. + * + * Adds and starts a discovery helper for specified device if enabled, + * calls {@link #removeDevice(DeviceId)} otherwise. + * + * @param device device to add + * @return discovery helper if discovery is enabled for the device + */ + private Optional<LinkDiscovery> updateDevice(Device device) { + if (device == null) { + return Optional.empty(); + } + if (rules.isSuppressed(device) || isBlacklisted(device.id())) { + log.trace("LinkDiscovery from {} disabled by configuration", device.id()); + removeDevice(device.id()); + return Optional.empty(); + } + LinkDiscovery ld = discoverers.computeIfAbsent(device.id(), + did -> new LinkDiscovery(device, context)); + if (ld.isStopped()) { + ld.start(); + } + return Optional.of(ld); + } + + /** + * Removes after stopping discovery helper for specified device. + * @param deviceId device to remove + */ + private void removeDevice(final DeviceId deviceId) { + discoverers.computeIfPresent(deviceId, (did, ld) -> { + ld.stop(); + return null; + }); + + } + + /** + * Updates ports of the specified device to the specified discovery helper. + */ + private void updatePorts(LinkDiscovery discoverer, DeviceId deviceId) { + deviceService.getPorts(deviceId).forEach(p -> updatePort(discoverer, p)); + } + + /** + * Updates discovery helper state of the specified port. + * + * Adds a port to the discovery helper if up and discovery is enabled, + * or calls {@link #removePort(Port)} otherwise. + */ + private void updatePort(LinkDiscovery discoverer, Port port) { + if (port == null) { + return; + } + if (port.number().isLogical()) { + // silently ignore logical ports + return; + } + + if (rules.isSuppressed(port) || isBlacklisted(port)) { + log.trace("LinkDiscovery from {} disabled by configuration", port); + removePort(port); + return; + } + + // check if enabled and turn off discovery? + if (!port.isEnabled()) { + removePort(port); + return; + } + + discoverer.addPort(port); + } + + /** + * Removes a port from the specified discovery helper. + * @param port the port + */ + private void removePort(Port port) { + if (port.element() instanceof Device) { + Device d = (Device) port.element(); + LinkDiscovery ld = discoverers.get(d.id()); + if (ld != null) { + ld.removePort(port.number()); + } + } else { + log.warn("Attempted to remove non-Device port", port); + } + } + + /** + * Requests packet intercepts. + */ + private void requestIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(TYPE_LLDP); + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); + + selector.matchEthType(TYPE_BSN); + if (useBddp) { + packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId); + } else { + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + } + } + + /** + * Withdraws packet intercepts. + */ + private void withdrawIntercepts() { + TrafficSelector.Builder selector = DefaultTrafficSelector.builder(); + selector.matchEthType(TYPE_LLDP); + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + selector.matchEthType(TYPE_BSN); + packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId); + } + + protected SuppressionRules rules() { + return rules; + } + + protected void updateRules(SuppressionRules newRules) { + if (!rules.equals(newRules)) { + rules = newRules; + loadDevices(); + } + } + + /** + * Processes device mastership role changes. + */ + private class InternalRoleListener implements MastershipListener { + @Override + public void event(MastershipEvent event) { + if (MastershipEvent.Type.BACKUPS_CHANGED.equals(event.type())) { + // only need new master events + return; + } + + DeviceId deviceId = event.subject(); + Device device = deviceService.getDevice(deviceId); + if (device == null) { + log.debug("Device {} doesn't exist, or isn't there yet", deviceId); + return; + } + if (clusterService.getLocalNode().id().equals(event.roleInfo().master())) { + updateDevice(device).ifPresent(ld -> updatePorts(ld, device.id())); + } + } + } + + /** + * Processes device events. + */ + private class InternalDeviceListener implements DeviceListener { + @Override + public void event(DeviceEvent event) { + Device device = event.subject(); + Port port = event.port(); + if (device == null) { + log.error("Device is null."); + return; + } + log.trace("{} {} {}", event.type(), event.subject(), event); + final DeviceId deviceId = device.id(); + switch (event.type()) { + case DEVICE_ADDED: + case DEVICE_UPDATED: + updateDevice(device).ifPresent(ld -> updatePorts(ld, deviceId)); + break; + case PORT_ADDED: + case PORT_UPDATED: + if (port.isEnabled()) { + updateDevice(device).ifPresent(ld -> updatePort(ld, port)); + } else { + log.debug("Port down {}", port); + removePort(port); + providerService.linksVanished(new ConnectPoint(port.element().id(), + port.number())); + } + break; + case PORT_REMOVED: + log.debug("Port removed {}", port); + removePort(port); + providerService.linksVanished(new ConnectPoint(port.element().id(), + port.number())); + break; + case DEVICE_REMOVED: + case DEVICE_SUSPENDED: + log.debug("Device removed {}", deviceId); + removeDevice(deviceId); + providerService.linksVanished(deviceId); + break; + case DEVICE_AVAILABILITY_CHANGED: + if (deviceService.isAvailable(deviceId)) { + log.debug("Device up {}", deviceId); + updateDevice(device); + } else { + log.debug("Device down {}", deviceId); + removeDevice(deviceId); + providerService.linksVanished(deviceId); + } + break; + case PORT_STATS_UPDATED: + break; + default: + log.debug("Unknown event {}", event); + } + } + } + + /** + * Processes incoming packets. + */ + private class InternalPacketProcessor implements PacketProcessor { + @Override + public void process(PacketContext context) { + if (context == null || context.isHandled()) { + return; + } + + Ethernet eth = context.inPacket().parsed(); + if (eth == null || (eth.getEtherType() != TYPE_LLDP && eth.getEtherType() != TYPE_BSN)) { + return; + } + + LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId()); + if (ld == null) { + return; + } + + if (ld.handleLldp(context)) { + context.block(); + } + } + } + + /** + * Auxiliary task to keep device ports up to date. + */ + private final class SyncDeviceInfoTask implements Runnable { + @Override + public void run() { + if (Thread.currentThread().isInterrupted()) { + log.info("Interrupted, quitting"); + return; + } + // check what deviceService sees, to see if we are missing anything + try { + loadDevices(); + } catch (Exception e) { + // Catch all exceptions to avoid task being suppressed + log.error("Exception thrown during synchronization process", e); + } + } + } + + /** + * Auxiliary task for pruning stale links. + */ + private class LinkPrunerTask implements Runnable { + @Override + public void run() { + if (Thread.currentThread().isInterrupted()) { + log.info("Interrupted, quitting"); + return; + } + + try { + // TODO: There is still a slight possibility of mastership + // change occurring right with link going stale. This will + // result in the stale link not being pruned. + Maps.filterEntries(linkTimes, e -> { + if (!masterService.isLocalMaster(e.getKey().dst().deviceId())) { + return true; + } + if (isStale(e.getValue())) { + providerService.linkVanished(new DefaultLinkDescription(e.getKey().src(), + e.getKey().dst(), + DIRECT)); + return true; + } + return false; + }).clear(); + + } catch (Exception e) { + // Catch all exceptions to avoid task being suppressed + log.error("Exception thrown during link pruning process", e); + } + } + + private boolean isStale(long lastSeen) { + return lastSeen < System.currentTimeMillis() - staleLinkAge; + } + } + + /** + * Provides processing context for the device link discovery helpers. + */ + private class InternalDiscoveryContext implements DiscoveryContext { + @Override + public MastershipService mastershipService() { + return masterService; + } + + @Override + public LinkProviderService providerService() { + return providerService; + } + + @Override + public PacketService packetService() { + return packetService; + } + + @Override + public long probeRate() { + return probeRate; + } + + @Override + public boolean useBddp() { + return useBddp; + } + + @Override + public void touchLink(LinkKey key) { + linkTimes.put(key, System.currentTimeMillis()); + } + } + + static final EnumSet<NetworkConfigEvent.Type> CONFIG_CHANGED + = EnumSet.of(NetworkConfigEvent.Type.CONFIG_ADDED, + NetworkConfigEvent.Type.CONFIG_UPDATED, + NetworkConfigEvent.Type.CONFIG_REMOVED); + + private class InternalConfigListener implements NetworkConfigListener { + + private synchronized void reconfigureSuppressionRules(SuppressionConfig cfg) { + if (cfg == null) { + log.error("Suppression Config is null."); + return; + } + + SuppressionRules newRules = new SuppressionRules(cfg.deviceTypes(), + cfg.annotation()); + + updateRules(newRules); + } + + @Override + public void event(NetworkConfigEvent event) { + if (event.configClass() == LinkDiscoveryFromDevice.class && + CONFIG_CHANGED.contains(event.type())) { + + if (event.subject() instanceof DeviceId) { + final DeviceId did = (DeviceId) event.subject(); + Device device = deviceService.getDevice(did); + updateDevice(device).ifPresent(ld -> updatePorts(ld, did)); + } + + } else if (event.configClass() == LinkDiscoveryFromPort.class && + CONFIG_CHANGED.contains(event.type())) { + + if (event.subject() instanceof ConnectPoint) { + ConnectPoint cp = (ConnectPoint) event.subject(); + if (cp.elementId() instanceof DeviceId) { + final DeviceId did = (DeviceId) cp.elementId(); + Device device = deviceService.getDevice(did); + Port port = deviceService.getPort(did, cp.port()); + updateDevice(device).ifPresent(ld -> updatePort(ld, port)); + } + } + + } else if (event.configClass().equals(SuppressionConfig.class) && + (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED || + event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED)) { + SuppressionConfig cfg = cfgRegistry.getConfig(appId, SuppressionConfig.class); + reconfigureSuppressionRules(cfg); + log.trace("Network config reconfigured"); + } + } + } +} diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java new file mode 100644 index 00000000..5b10f6d2 --- /dev/null +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionConfig.java @@ -0,0 +1,144 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.lldp.impl; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; + +import org.onosproject.core.ApplicationId; +import org.onosproject.net.Device; +import org.onosproject.net.config.Config; +import org.slf4j.Logger; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import static org.onosproject.provider.lldp.impl.LldpLinkProvider.DEFAULT_RULES; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * LinkDiscovery suppression config class. + */ +public class SuppressionConfig extends Config<ApplicationId> { + + private static final String DEVICE_TYPES = "deviceTypes"; + private static final String ANNOTATION = "annotation"; + + private static final ObjectMapper MAPPER = new ObjectMapper(); + + private static final List<Device.Type> DEFAULT_DEVICE_TYPES + = ImmutableList.copyOf(DEFAULT_RULES.getSuppressedDeviceType()); + + private final Logger log = getLogger(getClass()); + + /** + * Returns types of devices on which LinkDiscovery is suppressed. + * + * @return set of device types + */ + public Set<Device.Type> deviceTypes() { + return ImmutableSet.copyOf(getList(DEVICE_TYPES, Device.Type::valueOf, DEFAULT_DEVICE_TYPES)); + } + + /** + * Sets types of devices on which LinkDiscovery is suppressed. + * + * @param deviceTypes new set of device types; null to clear + * @return self + */ + public SuppressionConfig deviceTypes(Set<Device.Type> deviceTypes) { + return (SuppressionConfig) setOrClear(DEVICE_TYPES, deviceTypes); + } + + /** + * Returns annotation of Ports on which LinkDiscovery is suppressed. + * + * @return key-value pairs of annotation + */ + public Map<String, String> annotation() { + ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); + + String jsonAnnotation = get(ANNOTATION, null); + if (jsonAnnotation == null || jsonAnnotation.isEmpty()) { + return ImmutableMap.of(); + } + + JsonNode annotationNode; + try { + annotationNode = MAPPER.readTree(jsonAnnotation); + } catch (IOException e) { + log.error("Failed to read JSON tree from: {}", jsonAnnotation); + return ImmutableMap.of(); + } + + if (annotationNode.isObject()) { + ObjectNode obj = (ObjectNode) annotationNode; + Iterator<Map.Entry<String, JsonNode>> it = obj.fields(); + while (it.hasNext()) { + Map.Entry<String, JsonNode> entry = it.next(); + final String key = entry.getKey(); + final JsonNode value = entry.getValue(); + + if (value.isValueNode()) { + if (value.isNull()) { + builder.put(key, SuppressionRules.ANY_VALUE); + } else { + builder.put(key, value.asText()); + } + } else { + log.warn("Encountered unexpected JSON field {} for annotation", entry); + } + } + } else { + log.error("Encountered unexpected JSONNode {} for annotation", annotationNode); + return ImmutableMap.of(); + } + + return builder.build(); + } + + /** + * Sets annotation of Ports on which LinkDiscovery is suppressed. + * + * @param annotation new key-value pair of annotation; null to clear + * @return self + */ + public SuppressionConfig annotation(Map<String, String> annotation) { + + // ANY_VALUE should be null in JSON + Map<String, String> config = Maps.transformValues(annotation, + v -> (v == SuppressionRules.ANY_VALUE) ? null : v); + + String jsonAnnotation = null; + + try { + // TODO Store annotation as a Map instead of a String (which needs NetworkConfigRegistry modification) + jsonAnnotation = MAPPER.writeValueAsString(config); + } catch (JsonProcessingException e) { + log.error("Failed to write JSON from: {}", annotation); + } + + return (SuppressionConfig) setOrClear(ANNOTATION, jsonAnnotation); + } +} diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java index 27c75ebd..14bc2200 100644 --- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java +++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java @@ -18,38 +18,33 @@ package org.onosproject.provider.lldp.impl; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import org.onosproject.net.Annotations; import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; import org.onosproject.net.Element; import org.onosproject.net.Port; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.google.common.base.MoreObjects; public class SuppressionRules { public static final String ANY_VALUE = "(any)"; - private final Set<DeviceId> suppressedDevice; private final Set<Device.Type> suppressedDeviceType; private final Map<String, String> suppressedAnnotation; - public SuppressionRules(Set<DeviceId> suppressedDevice, - Set<Device.Type> suppressedType, - Map<String, String> suppressedAnnotation) { + public SuppressionRules(Set<Device.Type> suppressedType, + Map<String, String> suppressedAnnotation) { - this.suppressedDevice = ImmutableSet.copyOf(suppressedDevice); this.suppressedDeviceType = ImmutableSet.copyOf(suppressedType); this.suppressedAnnotation = ImmutableMap.copyOf(suppressedAnnotation); } public boolean isSuppressed(Device device) { - if (suppressedDevice.contains(device.id())) { - return true; - } if (suppressedDeviceType.contains(device.type())) { return true; } @@ -92,10 +87,6 @@ public class SuppressionRules { return false; } - Set<DeviceId> getSuppressedDevice() { - return suppressedDevice; - } - Set<Device.Type> getSuppressedDeviceType() { return suppressedDeviceType; } @@ -103,4 +94,30 @@ public class SuppressionRules { Map<String, String> getSuppressedAnnotation() { return suppressedAnnotation; } + + @Override + public int hashCode() { + return Objects.hash(suppressedDeviceType, + suppressedAnnotation); + } + + @Override + public boolean equals(Object object) { + if (object != null && getClass() == object.getClass()) { + SuppressionRules that = (SuppressionRules) object; + return Objects.equals(this.suppressedDeviceType, + that.suppressedDeviceType) + && Objects.equals(this.suppressedAnnotation, + that.suppressedAnnotation); + } + return false; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(this) + .add("suppressedDeviceType", suppressedDeviceType) + .add("suppressedAnnotation", suppressedAnnotation) + .toString(); + } } diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java deleted file mode 100644 index 360bebd2..00000000 --- a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2014-2015 Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.onosproject.provider.lldp.impl; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.slf4j.LoggerFactory.getLogger; - -import com.fasterxml.jackson.core.JsonEncoding; -import com.fasterxml.jackson.core.JsonFactory; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; -import com.fasterxml.jackson.databind.node.ObjectNode; - -import org.onosproject.net.Device; -import org.onosproject.net.DeviceId; -import org.slf4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -/* - * JSON file example - * - -{ - "deviceId" : [ "of:2222000000000000" ], - "deviceType" : [ "ROADM" ], - "annotation" : { "no-lldp" : null, "sendLLDP" : "false" } -} - */ - -/** - * Allows for reading and writing LLDP suppression definition as a JSON file. - */ -public class SuppressionRulesStore { - - private static final String DEVICE_ID = "deviceId"; - private static final String DEVICE_TYPE = "deviceType"; - private static final String ANNOTATION = "annotation"; - - private final Logger log = getLogger(getClass()); - - private final File file; - - /** - * Creates a reader/writer of the LLDP suppression definition file. - * - * @param filePath location of the definition file - */ - public SuppressionRulesStore(String filePath) { - file = new File(filePath); - } - - /** - * Creates a reader/writer of the LLDP suppression definition file. - * - * @param file definition file - */ - public SuppressionRulesStore(File file) { - this.file = checkNotNull(file); - } - - /** - * Returns SuppressionRules. - * - * @return SuppressionRules - * @throws IOException if error occurred while reading the data - */ - public SuppressionRules read() throws IOException { - final Set<DeviceId> suppressedDevice = new HashSet<>(); - final EnumSet<Device.Type> suppressedDeviceType = EnumSet.noneOf(Device.Type.class); - final Map<String, String> suppressedAnnotation = new HashMap<>(); - - ObjectMapper mapper = new ObjectMapper(); - ObjectNode root = (ObjectNode) mapper.readTree(file); - - for (JsonNode deviceId : root.get(DEVICE_ID)) { - if (deviceId.isTextual()) { - suppressedDevice.add(DeviceId.deviceId(deviceId.asText())); - } else { - log.warn("Encountered unexpected JSONNode {} for deviceId", deviceId); - } - } - - for (JsonNode deviceType : root.get(DEVICE_TYPE)) { - if (deviceType.isTextual()) { - suppressedDeviceType.add(Device.Type.valueOf(deviceType.asText())); - } else { - log.warn("Encountered unexpected JSONNode {} for deviceType", deviceType); - } - } - - JsonNode annotation = root.get(ANNOTATION); - if (annotation.isObject()) { - ObjectNode obj = (ObjectNode) annotation; - Iterator<Entry<String, JsonNode>> it = obj.fields(); - while (it.hasNext()) { - Entry<String, JsonNode> entry = it.next(); - final String key = entry.getKey(); - final JsonNode value = entry.getValue(); - - if (value.isValueNode()) { - if (value.isNull()) { - suppressedAnnotation.put(key, SuppressionRules.ANY_VALUE); - } else { - suppressedAnnotation.put(key, value.asText()); - } - } else { - log.warn("Encountered unexpected JSON field {} for annotation", entry); - } - } - } else { - log.warn("Encountered unexpected JSONNode {} for annotation", annotation); - } - - return new SuppressionRules(suppressedDevice, - suppressedDeviceType, - suppressedAnnotation); - } - - /** - * Writes the given SuppressionRules. - * - * @param rules SuppressionRules - * @throws IOException if error occurred while writing the data - */ - public void write(SuppressionRules rules) throws IOException { - ObjectMapper mapper = new ObjectMapper(); - ObjectNode root = mapper.createObjectNode(); - ArrayNode deviceIds = mapper.createArrayNode(); - ArrayNode deviceTypes = mapper.createArrayNode(); - ObjectNode annotations = mapper.createObjectNode(); - root.set(DEVICE_ID, deviceIds); - root.set(DEVICE_TYPE, deviceTypes); - root.set(ANNOTATION, annotations); - - rules.getSuppressedDevice() - .forEach(deviceId -> deviceIds.add(deviceId.toString())); - - rules.getSuppressedDeviceType() - .forEach(type -> deviceTypes.add(type.toString())); - - rules.getSuppressedAnnotation().forEach((key, value) -> { - if (value == SuppressionRules.ANY_VALUE) { - annotations.putNull(key); - } else { - annotations.put(key, value); - } - }); - mapper.writeTree(new JsonFactory().createGenerator(file, JsonEncoding.UTF8), - root); - } -} diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java new file mode 100644 index 00000000..758c34e6 --- /dev/null +++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LldpLinkProviderTest.java @@ -0,0 +1,944 @@ +/* + * Copyright 2014-2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.onosproject.provider.lldp.impl; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.onlab.packet.ChassisId; +import org.onlab.packet.Ethernet; +import org.onlab.packet.ONOSLLDP; +import org.onosproject.cfg.ComponentConfigAdapter; +import org.onosproject.cluster.NodeId; +import org.onosproject.cluster.RoleInfo; +import org.onosproject.core.ApplicationId; +import org.onosproject.core.CoreService; +import org.onosproject.core.DefaultApplicationId; +import org.onosproject.mastership.MastershipListener; +import org.onosproject.mastership.MastershipService; +import org.onosproject.net.Annotations; +import org.onosproject.net.ConnectPoint; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.DefaultPort; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.MastershipRole; +import org.onosproject.net.Port; +import org.onosproject.net.PortNumber; +import org.onosproject.net.config.Config; +import org.onosproject.net.config.NetworkConfigEvent; +import org.onosproject.net.config.NetworkConfigEvent.Type; +import org.onosproject.net.config.NetworkConfigListener; +import org.onosproject.net.config.NetworkConfigRegistryAdapter; +import org.onosproject.net.device.DeviceEvent; +import org.onosproject.net.device.DeviceListener; +import org.onosproject.net.device.DeviceServiceAdapter; +import org.onosproject.net.flow.TrafficTreatment; +import org.onosproject.net.link.LinkDescription; +import org.onosproject.net.link.LinkProvider; +import org.onosproject.net.link.LinkProviderRegistry; +import org.onosproject.net.link.LinkProviderService; +import org.onosproject.net.link.LinkServiceAdapter; +import org.onosproject.net.packet.DefaultInboundPacket; +import org.onosproject.net.packet.InboundPacket; +import org.onosproject.net.packet.OutboundPacket; +import org.onosproject.net.packet.PacketContext; +import org.onosproject.net.packet.PacketProcessor; +import org.onosproject.net.packet.PacketServiceAdapter; +import org.onosproject.net.provider.AbstractProviderService; +import org.onosproject.net.provider.ProviderId; + +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Collections; +import java.util.concurrent.CompletableFuture; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.onosproject.provider.lldp.impl.LldpLinkProvider.DEFAULT_RULES; +import static org.junit.Assert.assertFalse; + + +public class LldpLinkProviderTest { + + private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001"); + private static final DeviceId DID2 = DeviceId.deviceId("of:0000000000000002"); + private static final DeviceId DID3 = DeviceId.deviceId("of:0000000000000003"); + + private static Port pd1; + private static Port pd2; + private static Port pd3; + private static Port pd4; + + private final LldpLinkProvider provider = new LldpLinkProvider(); + private final TestLinkRegistry linkRegistry = new TestLinkRegistry(); + private final TestLinkService linkService = new TestLinkService(); + private final TestPacketService packetService = new TestPacketService(); + private final TestDeviceService deviceService = new TestDeviceService(); + private final TestMasterShipService masterService = new TestMasterShipService(); + private final TestNetworkConfigRegistry configRegistry = new TestNetworkConfigRegistry(); + + private CoreService coreService; + private TestLinkProviderService providerService; + + private PacketProcessor testProcessor; + private DeviceListener deviceListener; + private NetworkConfigListener configListener; + + private ApplicationId appId = + new DefaultApplicationId(100, "org.onosproject.provider.lldp"); + + private TestSuppressionConfig cfg; + + private Set<DeviceId> deviceBlacklist; + + private Set<ConnectPoint> portBlacklist; + + @Before + public void setUp() { + deviceBlacklist = new HashSet<>(); + portBlacklist = new HashSet<>(); + cfg = new TestSuppressionConfig(); + coreService = createMock(CoreService.class); + expect(coreService.registerApplication(appId.name())) + .andReturn(appId).anyTimes(); + replay(coreService); + + provider.cfgService = new ComponentConfigAdapter(); + provider.coreService = coreService; + provider.cfgRegistry = configRegistry; + + provider.deviceService = deviceService; + provider.linkService = linkService; + provider.packetService = packetService; + provider.providerRegistry = linkRegistry; + provider.masterService = masterService; + + provider.activate(null); + } + + @Test + public void basics() { + assertNotNull("registration expected", providerService); + assertEquals("incorrect provider", provider, providerService.provider()); + } + + @Test + public void switchAdd() { + DeviceEvent de = deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1); + deviceListener.event(de); + + assertFalse("Device not added", provider.discoverers.isEmpty()); + } + + @Test + public void switchRemove() { + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_REMOVED, DID1)); + + final LinkDiscovery linkDiscovery = provider.discoverers.get(DID1); + if (linkDiscovery != null) { + // If LinkDiscovery helper is there after DEVICE_REMOVED, + // it should be stopped + assertTrue("Discoverer is not stopped", linkDiscovery.isStopped()); + } + assertTrue("Device is not gone.", vanishedDpid(DID1)); + } + + /** + * Checks that links on a reconfigured switch are properly removed. + */ + @Test + public void switchSuppressedByAnnotation() { + + // add device to stub DeviceService + deviceService.putDevice(device(DID3)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + assertFalse("Device not added", provider.discoverers.isEmpty()); + + // update device in stub DeviceService with suppression config + deviceService.putDevice(device(DID3, DefaultAnnotations.builder() + .set(LldpLinkProvider.NO_LLDP, "true") + .build())); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, DID3)); + + // discovery on device is expected to be gone or stopped + LinkDiscovery linkDiscovery = provider.discoverers.get(DID3); + if (linkDiscovery != null) { + assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped()); + } + } + + @Test + public void switchSuppressByBlacklist() { + // add device in stub DeviceService + deviceService.putDevice(device(DID3)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + // add deviveId to device blacklist + deviceBlacklist.add(DID3); + configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED, + DID3, + LinkDiscoveryFromDevice.class)); + + // discovery helper for device is expected to be gone or stopped + LinkDiscovery linkDiscovery = provider.discoverers.get(DID3); + if (linkDiscovery != null) { + assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped()); + } + + } + + @Test + public void portUp() { + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 3, true))); + + assertTrue("Port not added to discoverer", + provider.discoverers.get(DID1).containsPort(3L)); + } + + @Test + public void portDown() { + + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 1, false))); + + assertFalse("Port added to discoverer", + provider.discoverers.get(DID1).containsPort(1L)); + assertTrue("Port is not gone.", vanishedPort(1L)); + } + + @Test + public void portRemoved() { + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, 3, true))); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_REMOVED, DID1, port(DID1, 3, true))); + + assertTrue("Port is not gone.", vanishedPort(3L)); + assertFalse("Port was not removed from discoverer", + provider.discoverers.get(DID1).containsPort(3L)); + } + + /** + * Checks that discovery on reconfigured switch are properly restarted. + */ + @Test + public void portSuppressedByDeviceAnnotationConfig() { + + /// When Device is configured with suppression:ON, Port also is same + + // add device in stub DeviceService with suppression configured + deviceService.putDevice(device(DID3, DefaultAnnotations.builder() + .set(LldpLinkProvider.NO_LLDP, "true") + .build())); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + // non-suppressed port added to suppressed device + final long portno3 = 3L; + deviceService.putPorts(DID3, port(DID3, portno3, true)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port(DID3, portno3, true))); + + // discovery on device is expected to be stopped + LinkDiscovery linkDiscovery = provider.discoverers.get(DID3); + if (linkDiscovery != null) { + assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped()); + } + + /// When Device is reconfigured without suppression:OFF, + /// Port should be included for discovery + + // update device in stub DeviceService without suppression configured + deviceService.putDevice(device(DID3)); + // update the Port in stub DeviceService. (Port has reference to Device) + deviceService.putPorts(DID3, port(DID3, portno3, true)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, DID3)); + + // discovery should come back on + assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped()); + assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID3).containsPort(portno3)); + } + + /** + * Checks that discovery on reconfigured switch are properly restarted. + */ + @Test + public void portSuppressedByParentDeviceIdBlacklist() { + + /// When Device is configured without suppression:OFF, + /// Port should be included for discovery + + // add device in stub DeviceService without suppression configured + deviceService.putDevice(device(DID3)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + // non-suppressed port added to suppressed device + final long portno3 = 3L; + deviceService.putPorts(DID3, port(DID3, portno3, true)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port(DID3, portno3, true))); + + // discovery should succeed + assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped()); + assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID3).containsPort(portno3)); + + // add suppression rule for "deviceId: "of:0000000000000003"" + deviceBlacklist.add(DID3); + configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED, + DID3, + LinkDiscoveryFromDevice.class)); + + + /// When Device is reconfigured with suppression:ON, Port also is same + + // update device in stub DeviceService with suppression configured + deviceService.putDevice(device(DID3)); + // update the Port in stub DeviceService. (Port has reference to Device) + deviceService.putPorts(DID3, port(DID3, portno3, true)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_UPDATED, DID3)); + + // discovery helper for device is expected to be gone or stopped + LinkDiscovery linkDiscovery = provider.discoverers.get(DID3); + if (linkDiscovery != null) { + assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped()); + } + } + + /** + * Checks that discovery on reconfigured switch are properly restarted. + */ + @Test + public void portSuppressedByDeviceTypeConfig() { + + /// When Device is configured without suppression:OFF, + /// Port should be included for discovery + + // add device in stub DeviceService without suppression configured + deviceService.putDevice(device(DID1, Device.Type.SWITCH)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + + // non-suppressed port added to suppressed device + final long portno3 = 3L; + deviceService.putPorts(DID1, port(DID1, portno3, true)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID1, port(DID1, portno3, true))); + + // add device in stub DeviceService with suppression configured + deviceService.putDevice(device(DID2, Device.Type.ROADM)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID2)); + + // non-suppressed port added to suppressed device + final long portno4 = 4L; + deviceService.putPorts(DID2, port(DID2, portno4, true)); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID2, port(DID2, portno4, true))); + + // discovery should succeed for this device + assertFalse("Discoverer is expected to start", provider.discoverers.get(DID1).isStopped()); + assertTrue("Discoverer should contain the port there", provider.discoverers.get(DID1).containsPort(portno3)); + + // discovery on device is expected to be stopped for this device + LinkDiscovery linkDiscovery = provider.discoverers.get(DID2); + if (linkDiscovery != null) { + assertTrue("Discovery expected to be stopped", linkDiscovery.isStopped()); + } + } + + /** + * Checks that discovery on reconfigured port are properly restarted. + */ + @Test + public void portSuppressedByPortConfig() { + + // add device in stub DeviceService without suppression configured + deviceService.putDevice(device(DID3)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + // suppressed port added to non-suppressed device + final long portno3 = 3L; + final Port port3 = port(DID3, portno3, true, + DefaultAnnotations.builder() + .set(LldpLinkProvider.NO_LLDP, "true") + .build()); + deviceService.putPorts(DID3, port3); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port3)); + + // discovery helper should be there turned on + assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped()); + assertFalse("Discoverer should not contain the port there", + provider.discoverers.get(DID3).containsPort(portno3)); + } + + @Test + public void portSuppressedByPortBlacklist() { + + // add device in stub DeviceService without suppression configured + deviceService.putDevice(device(DID3)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID3)); + + final long portno3 = 3L; + final Port port3 = port(DID3, portno3, true); + + final ConnectPoint cpDid3no3 = new ConnectPoint(DID3, PortNumber.portNumber(portno3)); + portBlacklist.add(cpDid3no3); + + // suppressed port added to non-suppressed device + deviceService.putPorts(DID3, port3); + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port3)); + + configListener.event(new NetworkConfigEvent(Type.CONFIG_ADDED, + cpDid3no3, + LinkDiscoveryFromPort.class)); + + // discovery helper should be there turned on + assertFalse("Discoverer is expected to start", provider.discoverers.get(DID3).isStopped()); + // but port is not a discovery target + assertFalse("Discoverer should not contain the port there", + provider.discoverers.get(DID3).containsPort(portno3)); + } + + @Test + public void portUnknown() { + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + // Note: DID3 hasn't been added to TestDeviceService, but only port is added + deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID3, port(DID3, 1, false))); + + + assertNull("DeviceId exists", + provider.discoverers.get(DID3)); + } + + @Test + public void unknownPktCtx() { + + // Note: DID3 hasn't been added to TestDeviceService + PacketContext pktCtx = new TestPacketContext(device(DID3)); + + testProcessor.process(pktCtx); + assertFalse("Context should still be free", pktCtx.isHandled()); + } + + @Test + public void knownPktCtx() { + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1)); + deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID2)); + PacketContext pktCtx = new TestPacketContext(deviceService.getDevice(DID2)); + + + testProcessor.process(pktCtx); + + assertTrue("Link not detected", detectedLink(DID1, DID2)); + + } + + + @After + public void tearDown() { + provider.deactivate(); + provider.coreService = null; + provider.providerRegistry = null; + provider.deviceService = null; + provider.packetService = null; + } + + private DeviceEvent deviceEvent(DeviceEvent.Type type, DeviceId did) { + return new DeviceEvent(type, deviceService.getDevice(did)); + + } + + private DefaultDevice device(DeviceId did) { + return new DefaultDevice(ProviderId.NONE, did, Device.Type.SWITCH, + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId()); + } + + private DefaultDevice device(DeviceId did, Device.Type type) { + return new DefaultDevice(ProviderId.NONE, did, type, + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId()); + } + + private DefaultDevice device(DeviceId did, Annotations annotations) { + return new DefaultDevice(ProviderId.NONE, did, Device.Type.SWITCH, + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId(), annotations); + } + + @SuppressWarnings(value = { "unused" }) + private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, PortNumber port) { + return new DeviceEvent(type, deviceService.getDevice(did), + deviceService.getPort(did, port)); + } + + private DeviceEvent portEvent(DeviceEvent.Type type, DeviceId did, Port port) { + return new DeviceEvent(type, deviceService.getDevice(did), port); + } + + private Port port(DeviceId did, long port, boolean enabled) { + return new DefaultPort(deviceService.getDevice(did), + PortNumber.portNumber(port), enabled); + } + + private Port port(DeviceId did, long port, boolean enabled, Annotations annotations) { + return new DefaultPort(deviceService.getDevice(did), + PortNumber.portNumber(port), enabled, annotations); + } + + private boolean vanishedDpid(DeviceId... dids) { + for (int i = 0; i < dids.length; i++) { + if (!providerService.vanishedDpid.contains(dids[i])) { + return false; + } + } + return true; + } + + private boolean vanishedPort(Long... ports) { + for (int i = 0; i < ports.length; i++) { + if (!providerService.vanishedPort.contains(ports[i])) { + return false; + } + } + return true; + } + + private boolean detectedLink(DeviceId src, DeviceId dst) { + for (DeviceId key : providerService.discoveredLinks.keySet()) { + if (key.equals(src)) { + return providerService.discoveredLinks.get(src).equals(dst); + } + } + return false; + } + + @Test + public void addDeviceTypeRule() { + Device.Type deviceType1 = Device.Type.ROADM; + Device.Type deviceType2 = Device.Type.SWITCH; + + Set<Device.Type> deviceTypes = new HashSet<>(); + deviceTypes.add(deviceType1); + + cfg.deviceTypes(deviceTypes); + + configEvent(NetworkConfigEvent.Type.CONFIG_ADDED); + + assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType1)); + assertFalse(provider.rules().getSuppressedDeviceType().contains(deviceType2)); + } + + @Test + public void updateDeviceTypeRule() { + Device.Type deviceType1 = Device.Type.ROADM; + Device.Type deviceType2 = Device.Type.SWITCH; + Set<Device.Type> deviceTypes = new HashSet<>(); + + deviceTypes.add(deviceType1); + cfg.deviceTypes(deviceTypes); + + configEvent(NetworkConfigEvent.Type.CONFIG_ADDED); + + deviceTypes.add(deviceType2); + cfg.deviceTypes(deviceTypes); + + configEvent(NetworkConfigEvent.Type.CONFIG_UPDATED); + + assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType1)); + assertTrue(provider.rules().getSuppressedDeviceType().contains(deviceType2)); + } + + @Test + public void addAnnotationRule() { + final String key1 = "key1", key2 = "key2"; + final String value1 = "value1"; + + Map<String, String> annotation = new HashMap<>(); + annotation.put(key1, value1); + + cfg.annotation(annotation); + + configEvent(NetworkConfigEvent.Type.CONFIG_ADDED); + + assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1)); + assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1)); + assertFalse(provider.rules().getSuppressedAnnotation().containsKey(key2)); + } + + @Test + public void updateAnnotationRule() { + final String key1 = "key1", key2 = "key2"; + final String value1 = "value1", value2 = "value2"; + Map<String, String> annotation = new HashMap<>(); + + annotation.put(key1, value1); + cfg.annotation(annotation); + + configEvent(NetworkConfigEvent.Type.CONFIG_ADDED); + + assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1)); + assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1)); + assertFalse(provider.rules().getSuppressedAnnotation().containsKey(key2)); + + annotation.put(key2, value2); + cfg.annotation(annotation); + + configEvent(NetworkConfigEvent.Type.CONFIG_UPDATED); + + assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key1)); + assertEquals(value1, provider.rules().getSuppressedAnnotation().get(key1)); + assertTrue(provider.rules().getSuppressedAnnotation().containsKey(key2)); + assertEquals(value2, provider.rules().getSuppressedAnnotation().get(key2)); + } + + private void configEvent(NetworkConfigEvent.Type evType) { + configListener.event(new NetworkConfigEvent(evType, + appId, + SuppressionConfig.class)); + } + + + private class TestLinkRegistry implements LinkProviderRegistry { + + @Override + public LinkProviderService register(LinkProvider provider) { + providerService = new TestLinkProviderService(provider); + return providerService; + } + + @Override + public void unregister(LinkProvider provider) { + } + + @Override + public Set<ProviderId> getProviders() { + return null; + } + + } + + private class TestLinkProviderService + extends AbstractProviderService<LinkProvider> + implements LinkProviderService { + + List<DeviceId> vanishedDpid = Lists.newLinkedList(); + List<Long> vanishedPort = Lists.newLinkedList(); + Map<DeviceId, DeviceId> discoveredLinks = Maps.newHashMap(); + + protected TestLinkProviderService(LinkProvider provider) { + super(provider); + } + + @Override + public void linkDetected(LinkDescription linkDescription) { + DeviceId sDid = linkDescription.src().deviceId(); + DeviceId dDid = linkDescription.dst().deviceId(); + discoveredLinks.put(sDid, dDid); + } + + @Override + public void linkVanished(LinkDescription linkDescription) { + } + + @Override + public void linksVanished(ConnectPoint connectPoint) { + vanishedPort.add(connectPoint.port().toLong()); + + } + + @Override + public void linksVanished(DeviceId deviceId) { + vanishedDpid.add(deviceId); + } + + + } + + + + private class TestPacketContext implements PacketContext { + + protected Device device; + protected boolean blocked = false; + + public TestPacketContext(Device dev) { + device = dev; + } + + @Override + public long time() { + return 0; + } + + @Override + public InboundPacket inPacket() { + ONOSLLDP lldp = new ONOSLLDP(); + lldp.setChassisId(device.chassisId()); + lldp.setPortId((int) pd1.number().toLong()); + lldp.setDevice(deviceService.getDevice(DID1).id().toString()); + + + Ethernet ethPacket = new Ethernet(); + ethPacket.setEtherType(Ethernet.TYPE_LLDP); + ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA); + ethPacket.setPayload(lldp); + ethPacket.setPad(true); + + + + ethPacket.setSourceMACAddress("DE:AD:BE:EF:BA:11"); + + ConnectPoint cp = new ConnectPoint(device.id(), pd3.number()); + + return new DefaultInboundPacket(cp, ethPacket, + ByteBuffer.wrap(ethPacket.serialize())); + + } + + @Override + public OutboundPacket outPacket() { + return null; + } + + @Override + public TrafficTreatment.Builder treatmentBuilder() { + return null; + } + + @Override + public void send() { + + } + + @Override + public boolean block() { + blocked = true; + return blocked; + } + + @Override + public boolean isHandled() { + return blocked; + } + + } + + private class TestPacketService extends PacketServiceAdapter { + @Override + public void addProcessor(PacketProcessor processor, int priority) { + testProcessor = processor; + } + } + + private class TestDeviceService extends DeviceServiceAdapter { + + private final Map<DeviceId, Device> devices = new HashMap<>(); + private final ArrayListMultimap<DeviceId, Port> ports = + ArrayListMultimap.create(); + public TestDeviceService() { + Device d1 = new DefaultDevice(ProviderId.NONE, DID1, Device.Type.SWITCH, + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId()); + Device d2 = new DefaultDevice(ProviderId.NONE, DID2, Device.Type.SWITCH, + "TESTMF", "TESTHW", "TESTSW", "TESTSN", new ChassisId()); + devices.put(DID1, d1); + devices.put(DID2, d2); + pd1 = new DefaultPort(d1, PortNumber.portNumber(1), true); + pd2 = new DefaultPort(d1, PortNumber.portNumber(2), true); + pd3 = new DefaultPort(d2, PortNumber.portNumber(1), true); + pd4 = new DefaultPort(d2, PortNumber.portNumber(2), true); + + ports.putAll(DID1, Lists.newArrayList(pd1, pd2)); + ports.putAll(DID2, Lists.newArrayList(pd3, pd4)); + } + + private void putDevice(Device device) { + DeviceId deviceId = device.id(); + devices.put(deviceId, device); + } + + private void putPorts(DeviceId did, Port...ports) { + this.ports.putAll(did, Lists.newArrayList(ports)); + } + + @Override + public int getDeviceCount() { + return devices.values().size(); + } + + @Override + public Iterable<Device> getDevices() { + return ImmutableList.copyOf(devices.values()); + } + + @Override + public Device getDevice(DeviceId deviceId) { + return devices.get(deviceId); + } + + @Override + public MastershipRole getRole(DeviceId deviceId) { + return MastershipRole.MASTER; + } + + @Override + public List<Port> getPorts(DeviceId deviceId) { + return ports.get(deviceId); + } + + @Override + public Port getPort(DeviceId deviceId, PortNumber portNumber) { + for (Port p : ports.get(deviceId)) { + if (p.number().equals(portNumber)) { + return p; + } + } + return null; + } + + @Override + public boolean isAvailable(DeviceId deviceId) { + return true; + } + + @Override + public void addListener(DeviceListener listener) { + deviceListener = listener; + + } + + @Override + public void removeListener(DeviceListener listener) { + + } + } + + private final class TestMasterShipService implements MastershipService { + + @Override + public MastershipRole getLocalRole(DeviceId deviceId) { + return MastershipRole.MASTER; + } + + @Override + public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) { + return CompletableFuture.completedFuture(null); + } + + @Override + public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) { + return null; + } + + @Override + public NodeId getMasterFor(DeviceId deviceId) { + return null; + } + + @Override + public Set<DeviceId> getDevicesOf(NodeId nodeId) { + return null; + } + + @Override + public void addListener(MastershipListener listener) { + + } + + @Override + public void removeListener(MastershipListener listener) { + + } + + @Override + public RoleInfo getNodesFor(DeviceId deviceId) { + return new RoleInfo(new NodeId("foo"), Collections.<NodeId>emptyList()); + } + } + + + private class TestLinkService extends LinkServiceAdapter { + } + + private final class TestNetworkConfigRegistry + extends NetworkConfigRegistryAdapter { + @SuppressWarnings("unchecked") + @Override + public <S, C extends Config<S>> C getConfig(S subj, Class<C> configClass) { + if (configClass == SuppressionConfig.class) { + return (C) cfg; + } else if (configClass == LinkDiscoveryFromDevice.class) { + return (C) new LinkDiscoveryFromDevice() { + @Override + public boolean enabled() { + return !deviceBlacklist.contains(subj); + } + }; + } else if (configClass == LinkDiscoveryFromPort.class) { + return (C) new LinkDiscoveryFromPort() { + @Override + public boolean enabled() { + return !portBlacklist.contains(subj); + } + }; + } else { + return null; + } + } + + @Override + public void addListener(NetworkConfigListener listener) { + configListener = listener; + } + } + + private final class TestSuppressionConfig extends SuppressionConfig { + private Set<Device.Type> deviceTypes = new HashSet<>(DEFAULT_RULES.getSuppressedDeviceType()); + private Map<String, String> annotation = new HashMap<>(DEFAULT_RULES.getSuppressedAnnotation()); + + @Override + public Set<Device.Type> deviceTypes() { + return ImmutableSet.copyOf(deviceTypes); + } + + @Override + public SuppressionConfig deviceTypes(Set<Device.Type> deviceTypes) { + this.deviceTypes = ImmutableSet.copyOf(deviceTypes); + return this; + } + + @Override + public Map<String, String> annotation() { + return ImmutableMap.copyOf(annotation); + } + + @Override + public SuppressionConfig annotation(Map<String, String> annotation) { + this.annotation = ImmutableMap.copyOf(annotation); + return this; + } + } +} diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java new file mode 100644 index 00000000..85061ca0 --- /dev/null +++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionConfigTest.java @@ -0,0 +1,71 @@ +package org.onosproject.provider.lldp.impl; + +import static org.junit.Assert.*; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import org.junit.Before; +import org.junit.Test; +import org.onosproject.TestApplicationId; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.config.ConfigApplyDelegate; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public class SuppressionConfigTest { + private static final String APP_NAME = "SuppressionConfigTest"; + private static final TestApplicationId APP_ID = new TestApplicationId(APP_NAME); + private static final DeviceId DEVICE_ID_1 = DeviceId.deviceId("of:1111000000000000"); + private static final DeviceId DEVICE_ID_2 = DeviceId.deviceId("of:2222000000000000"); + private static final Device.Type DEVICE_TYPE_1 = Device.Type.ROADM; + private static final Device.Type DEVICE_TYPE_2 = Device.Type.FIBER_SWITCH; + private static final String ANNOTATION_KEY_1 = "no_lldp"; + private static final String ANNOTATION_VALUE_1 = "true"; + private static final String ANNOTATION_KEY_2 = "sendLLDP"; + private static final String ANNOTATION_VALUE_2 = "false"; + + private SuppressionConfig cfg; + + @Before + public void setUp() throws Exception { + ConfigApplyDelegate delegate = config -> { }; + ObjectMapper mapper = new ObjectMapper(); + cfg = new SuppressionConfig(); + cfg.init(APP_ID, LldpLinkProvider.CONFIG_KEY, JsonNodeFactory.instance.objectNode(), mapper, delegate); + } + + @Test + public void testDeviceTypes() { + Set<Device.Type> inputTypes = new HashSet<Device.Type>() { { + add(DEVICE_TYPE_1); + add(DEVICE_TYPE_2); + } }; + + assertNotNull(cfg.deviceTypes(inputTypes)); + + Set<Device.Type> outputTypes = cfg.deviceTypes(); + assertTrue(outputTypes.contains(DEVICE_TYPE_1)); + assertTrue(outputTypes.contains(DEVICE_TYPE_2)); + assertEquals(outputTypes.size(), 2); + } + + @Test + public void testDeviceAnnotation() { + Map<String, String> inputMap = new HashMap<String, String>() { { + put(ANNOTATION_KEY_1, ANNOTATION_VALUE_1); + put(ANNOTATION_KEY_2, ANNOTATION_VALUE_2); + } }; + + assertNotNull(cfg.annotation(inputMap)); + + Map<String, String> outputMap = cfg.annotation(); + assertEquals(outputMap.get(ANNOTATION_KEY_1), ANNOTATION_VALUE_1); + assertEquals(outputMap.get(ANNOTATION_KEY_2), ANNOTATION_VALUE_2); + assertEquals(outputMap.size(), 2); + } + +} diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java deleted file mode 100644 index 0ac31123..00000000 --- a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014-2015 Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onosproject.provider.lldp.impl; - -import static org.junit.Assert.*; -import static org.onosproject.net.DeviceId.deviceId; - -import java.io.File; -import java.io.IOException; -import java.net.URISyntaxException; -import java.nio.file.Path; -import java.nio.file.Paths; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import org.onosproject.net.Device; - -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.ImmutableSet; -import com.google.common.io.Resources; - -public class SuppressionRulesStoreTest { - - @Rule - public TemporaryFolder tempFolder = new TemporaryFolder(); - - // "lldp_suppression.json" - SuppressionRules testData - = new SuppressionRules(ImmutableSet.of(deviceId("of:2222000000000000")), - ImmutableSet.of(Device.Type.ROADM), - ImmutableMap.of("no-lldp", SuppressionRules.ANY_VALUE, - "sendLLDP", "false")); - - private static void assertRulesEqual(SuppressionRules expected, SuppressionRules actual) { - assertEquals(expected.getSuppressedDevice(), - actual.getSuppressedDevice()); - assertEquals(expected.getSuppressedDeviceType(), - actual.getSuppressedDeviceType()); - assertEquals(expected.getSuppressedAnnotation(), - actual.getSuppressedAnnotation()); - } - - @Test - public void testRead() throws URISyntaxException, IOException { - Path path = Paths.get(Resources.getResource("lldp_suppression.json").toURI()); - - SuppressionRulesStore store = new SuppressionRulesStore(path.toString()); - - SuppressionRules rules = store.read(); - - assertRulesEqual(testData, rules); - } - - @Test - public void testWrite() throws IOException { - File newFile = tempFolder.newFile(); - SuppressionRulesStore store = new SuppressionRulesStore(newFile); - store.write(testData); - - SuppressionRulesStore reload = new SuppressionRulesStore(newFile); - SuppressionRules rules = reload.read(); - - assertRulesEqual(testData, rules); - } -} diff --git a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java index 03d431af..c18c248e 100644 --- a/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java +++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java @@ -51,22 +51,12 @@ public class SuppressionRulesTest { @Before public void setUp() throws Exception { - rules = new SuppressionRules(ImmutableSet.of(SUPPRESSED_DID), - ImmutableSet.of(Device.Type.ROADM), + rules = new SuppressionRules(ImmutableSet.of(Device.Type.ROADM), ImmutableMap.of("no-lldp", SuppressionRules.ANY_VALUE, "sendLLDP", "false")); } @Test - public void testSuppressedDeviceId() { - Device device = new DefaultDevice(PID, - SUPPRESSED_DID, - Device.Type.SWITCH, - MFR, HW, SW1, SN, CID); - assertTrue(rules.isSuppressed(device)); - } - - @Test public void testSuppressedDeviceType() { Device device = new DefaultDevice(PID, NON_SUPPRESSED_DID, @@ -111,17 +101,6 @@ public class SuppressionRulesTest { } @Test - public void testSuppressedPortOnSuppressedDevice() { - Device device = new DefaultDevice(PID, - SUPPRESSED_DID, - Device.Type.SWITCH, - MFR, HW, SW1, SN, CID); - Port port = new DefaultPort(device, P1, true); - - assertTrue(rules.isSuppressed(port)); - } - - @Test public void testSuppressedPortAnnotation() { Annotations annotation = DefaultAnnotations.builder() .set("no-lldp", "random") diff --git a/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json b/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json deleted file mode 100644 index 062e73aa..00000000 --- a/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "deviceId" : [ "of:2222000000000000" ], - "deviceType" : [ "ROADM" ], - "annotation" : { "no-lldp" : null, "sendLLDP" : "false" } -} - diff --git a/framework/src/onos/providers/netconf/app/features.xml b/framework/src/onos/providers/netconf/app/features.xml index ef0fb738..e032f4da 100644 --- a/framework/src/onos/providers/netconf/app/features.xml +++ b/framework/src/onos/providers/netconf/app/features.xml @@ -15,7 +15,6 @@ ~ limitations under the License. --> <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> - <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository> <feature name="${project.artifactId}" version="${project.version}" description="${project.description}"> <feature>onos-api</feature> diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfProviderConfig.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfProviderConfig.java new file mode 100644 index 00000000..7ae116eb --- /dev/null +++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfProviderConfig.java @@ -0,0 +1,93 @@ +/* + * Copyright 2015 Open Networking Laboratory + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.onosproject.provider.netconf.device.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.annotations.Beta; +import com.google.common.collect.Sets; +import org.onlab.packet.IpAddress; +import org.onosproject.core.ApplicationId; +import org.onosproject.incubator.net.config.basics.ConfigException; +import org.onosproject.net.config.Config; + +import java.util.Set; + +/** + * Configuration for Netconf provider. + */ +@Beta +public class NetconfProviderConfig extends Config<ApplicationId> { + + public static final String CONFIG_VALUE_ERROR = "Error parsing config value"; + private static final String IP = "ip"; + private static final int DEFAULT_TCP_PORT = 830; + private static final String PORT = "port"; + private static final String NAME = "name"; + private static final String PASSWORD = "password"; + + public Set<NetconfDeviceAddress> getDevicesAddresses() throws ConfigException { + Set<NetconfDeviceAddress> devicesAddresses = Sets.newHashSet(); + + try { + for (JsonNode node : array) { + String ip = node.path(IP).asText(); + IpAddress ipAddr = ip.isEmpty() ? null : IpAddress.valueOf(ip); + int port = node.path(PORT).asInt(DEFAULT_TCP_PORT); + String name = node.path(NAME).asText(); + String password = node.path(PASSWORD).asText(); + devicesAddresses.add(new NetconfDeviceAddress(ipAddr, port, name, password)); + + } + } catch (IllegalArgumentException e) { + throw new ConfigException(CONFIG_VALUE_ERROR, e); + } + + return devicesAddresses; + } + + public class NetconfDeviceAddress { + private final IpAddress ip; + private final int port; + private final String name; + private final String password; + + public NetconfDeviceAddress(IpAddress ip, int port, String name, String password) { + this.ip = ip; + this.port = port; + this.name = name; + this.password = password; + } + + public IpAddress ip() { + return ip; + } + + public int port() { + return port; + } + + public String name() { + return name; + } + + public String password() { + return password; + } + } + + +} diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java index d03e75ac..4e5a2752 100644 --- a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java +++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java @@ -72,7 +72,9 @@ public class NetconfOperation { log.error("Unable to send Hello Message to the device: ", e); } finally { log.debug("Closing the session after successful execution"); - ssh.close(); + if (ssh != null) { + ssh.close(); + } } } diff --git a/framework/src/onos/providers/openflow/base/app.xml b/framework/src/onos/providers/openflow/base/app.xml new file mode 100644 index 00000000..34e6b151 --- /dev/null +++ b/framework/src/onos/providers/openflow/base/app.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2014-2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<app name="org.onosproject.openflow-base" origin="ON.Lab" version="${project.version}" + featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features" + features="${project.artifactId}"> + <description>${project.description}</description> + + <artifact>mvn:${project.groupId}/onos-of-api/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-of-ctl/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-drivers/${project.version}</artifact> + + <artifact>mvn:${project.groupId}/onos-of-provider-device/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-of-provider-packet/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-of-provider-flow/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-of-provider-group/${project.version}</artifact> + <artifact>mvn:${project.groupId}/onos-of-provider-meter/${project.version}</artifact> +</app> diff --git a/framework/src/onos/providers/openflow/base/features.xml b/framework/src/onos/providers/openflow/base/features.xml new file mode 100644 index 00000000..54e9cacb --- /dev/null +++ b/framework/src/onos/providers/openflow/base/features.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + ~ Copyright 2014-2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> + <feature name="${project.artifactId}" version="${project.version}" + description="${project.description}"> + <feature>onos-api</feature> + <bundle>mvn:io.netty/netty/3.9.2.Final</bundle> + <bundle>mvn:${project.groupId}/onos-of-api/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-of-ctl/${project.version}</bundle> + + <bundle>mvn:${project.groupId}/onos-of-provider-device/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-of-provider-packet/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-of-provider-flow/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-of-provider-group/${project.version}</bundle> + <bundle>mvn:${project.groupId}/onos-of-provider-meter/${project.version}</bundle> + </feature> +</features> diff --git a/framework/src/onos/providers/openflow/base/pom.xml b/framework/src/onos/providers/openflow/base/pom.xml new file mode 100644 index 00000000..9eb170c3 --- /dev/null +++ b/framework/src/onos/providers/openflow/base/pom.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ Copyright 2014-2015 Open Networking Laboratory + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-providers</artifactId> + <version>1.4.0-SNAPSHOT</version> + <relativePath>../pom.xml</relativePath> + </parent> + + <artifactId>onos-openflow-base</artifactId> + <packaging>pom</packaging> + + <description>OpenFlow protocol southbound providers</description> + + <dependencies> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-api</artifactId> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-ctl</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-drivers</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-provider-device</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-provider-packet</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-provider-flow</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.onosproject</groupId> + <artifactId>onos-of-provider-group</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + +</project> diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java index 1354240f..d4494f18 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java @@ -44,7 +44,7 @@ import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.openflow.controller.Dpid; -import org.onosproject.openflow.controller.ExtensionInterpreter; +import org.onosproject.openflow.controller.ExtensionTreatmentInterpreter; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.OFFlowRemoved; import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry; @@ -449,7 +449,7 @@ public class FlowEntryBuilder { break; case TUNNEL_IPV4_DST: DriverHandler driver = getDriver(dpid); - ExtensionInterpreter interpreter = driver.behaviour(ExtensionInterpreter.class); + ExtensionTreatmentInterpreter interpreter = driver.behaviour(ExtensionTreatmentInterpreter.class); if (interpreter != null) { builder.extension(interpreter.mapAction(action), DeviceId.deviceId(Dpid.uri(dpid))); } @@ -513,6 +513,7 @@ public class FlowEntryBuilder { Ip4Prefix ip4Prefix; Ip6Address ip6Address; Ip6Prefix ip6Prefix; + Ip4Address ip; TrafficSelector.Builder builder = DefaultTrafficSelector.builder(); for (MatchField<?> field : match.getMatchFields()) { @@ -707,17 +708,26 @@ public class FlowEntryBuilder { long tunnelId = match.get(MatchField.TUNNEL_ID).getValue(); builder.matchTunnelId(tunnelId); break; + case ARP_OP: + int arpOp = match.get(MatchField.ARP_OP).getOpcode(); + builder.matchArpOp(arpOp); + break; case ARP_SHA: mac = MacAddress.valueOf(match.get(MatchField.ARP_SHA).getLong()); builder.matchArpSha(mac); break; + case ARP_SPA: + ip = Ip4Address.valueOf(match.get(MatchField.ARP_SPA).getInt()); + builder.matchArpSpa(ip); + break; case ARP_THA: mac = MacAddress.valueOf(match.get(MatchField.ARP_THA).getLong()); builder.matchArpTha(mac); break; - case ARP_OP: - case ARP_SPA: case ARP_TPA: + ip = Ip4Address.valueOf(match.get(MatchField.ARP_TPA).getInt()); + builder.matchArpTpa(ip); + break; case MPLS_TC: default: log.warn("Match type {} not yet implemented.", field.id); diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java index c5de72a8..2a8d2010 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java @@ -25,6 +25,8 @@ import org.onosproject.net.driver.DriverService; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.criteria.ArpHaCriterion; +import org.onosproject.net.flow.criteria.ArpOpCriterion; +import org.onosproject.net.flow.criteria.ArpPaCriterion; import org.onosproject.net.flow.criteria.Criterion; import org.onosproject.net.flow.criteria.EthCriterion; import org.onosproject.net.flow.criteria.EthTypeCriterion; @@ -58,6 +60,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowDelete; import org.projectfloodlight.openflow.protocol.OFFlowMod; import org.projectfloodlight.openflow.protocol.match.Match; import org.projectfloodlight.openflow.protocol.match.MatchField; +import org.projectfloodlight.openflow.types.ArpOpcode; import org.projectfloodlight.openflow.types.CircuitSignalID; import org.projectfloodlight.openflow.types.EthType; import org.projectfloodlight.openflow.types.ICMPv4Code; @@ -180,6 +183,7 @@ public abstract class FlowModBuilder { SctpPortCriterion sctpPortCriterion; IPv6NDLinkLayerAddressCriterion llAddressCriterion; ArpHaCriterion arpHaCriterion; + ArpPaCriterion arpPaCriterion; for (Criterion c : selector.criteria()) { switch (c.type()) { @@ -417,19 +421,31 @@ public abstract class FlowModBuilder { mplsBos.mplsBos() ? OFBooleanValue.TRUE : OFBooleanValue.FALSE); break; + case ARP_OP: + ArpOpCriterion arpOp = (ArpOpCriterion) c; + mBuilder.setExact(MatchField.ARP_OP, + ArpOpcode.of(arpOp.arpOp())); + break; case ARP_SHA: arpHaCriterion = (ArpHaCriterion) c; mBuilder.setExact(MatchField.ARP_SHA, MacAddress.of(arpHaCriterion.mac().toLong())); break; + case ARP_SPA: + arpPaCriterion = (ArpPaCriterion) c; + mBuilder.setExact(MatchField.ARP_SPA, + IPv4Address.of(arpPaCriterion.ip().toInt())); + break; case ARP_THA: arpHaCriterion = (ArpHaCriterion) c; mBuilder.setExact(MatchField.ARP_THA, MacAddress.of(arpHaCriterion.mac().toLong())); break; - case ARP_OP: - case ARP_SPA: case ARP_TPA: + arpPaCriterion = (ArpPaCriterion) c; + mBuilder.setExact(MatchField.ARP_TPA, + IPv4Address.of(arpPaCriterion.ip().toInt())); + break; case MPLS_TC: case PBB_ISID: default: diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java index e2fc30da..90def432 100644 --- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java +++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java @@ -27,7 +27,7 @@ import org.onosproject.net.driver.Driver; import org.onosproject.net.driver.DriverService; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.instructions.ExtensionInstruction; +import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.Instructions.GroupInstruction; @@ -49,7 +49,7 @@ import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInst import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPv6FlowLabelInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction; import org.onosproject.net.flow.instructions.L4ModificationInstruction.ModTransportPortInstruction; -import org.onosproject.openflow.controller.ExtensionInterpreter; +import org.onosproject.openflow.controller.ExtensionTreatmentInterpreter; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFFlowAdd; import org.projectfloodlight.openflow.protocol.OFFlowDelete; @@ -320,7 +320,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { } private OFAction buildModLambdaInstruction(ModLambdaInstruction instruction) { - return factory().actions().circuit(factory().oxms().ochSigidBasic( + return factory().actions().circuit(factory().oxms().expOchSigId( new CircuitSignalID((byte) 1, (byte) 2, instruction.lambda(), (short) 1))); } @@ -329,7 +329,7 @@ public class FlowModBuilderVer13 extends FlowModBuilder { byte gridType = OpenFlowValueMapper.lookupGridType(signal.gridType()); byte channelSpacing = OpenFlowValueMapper.lookupChannelSpacing(signal.channelSpacing()); - return factory().actions().circuit(factory().oxms().ochSigidBasic( + return factory().actions().circuit(factory().oxms().expOchSigId( new CircuitSignalID(gridType, channelSpacing, (short) signal.spacingMultiplier(), (short) signal.slotGranularity()) )); @@ -482,16 +482,16 @@ public class FlowModBuilderVer13 extends FlowModBuilder { return null; } - private OFAction buildExtensionAction(ExtensionInstruction i) { + private OFAction buildExtensionAction(ExtensionTreatment i) { if (!driverService.isPresent()) { log.error("No driver service present"); return null; } Driver driver = driverService.get().getDriver(deviceId); - if (driver.hasBehaviour(ExtensionInterpreter.class)) { + if (driver.hasBehaviour(ExtensionTreatmentInterpreter.class)) { DefaultDriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId)); - ExtensionInterpreter interpreter = handler.behaviour(ExtensionInterpreter.class); + ExtensionTreatmentInterpreter interpreter = handler.behaviour(ExtensionTreatmentInterpreter.class); return interpreter.mapInstruction(factory(), i); } diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java index c91616df..9c93844a 100644 --- a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java +++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java @@ -33,7 +33,7 @@ import org.onosproject.net.driver.DefaultDriverHandler; import org.onosproject.net.driver.Driver; import org.onosproject.net.driver.DriverService; import org.onosproject.net.flow.TrafficTreatment; -import org.onosproject.net.flow.instructions.ExtensionInstruction; +import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.flow.instructions.L0ModificationInstruction; @@ -42,7 +42,7 @@ import org.onosproject.net.flow.instructions.L3ModificationInstruction; import org.onosproject.net.group.GroupBucket; import org.onosproject.net.group.GroupBuckets; import org.onosproject.net.group.GroupDescription; -import org.onosproject.openflow.controller.ExtensionInterpreter; +import org.onosproject.openflow.controller.ExtensionTreatmentInterpreter; import org.projectfloodlight.openflow.protocol.OFBucket; import org.projectfloodlight.openflow.protocol.OFFactory; import org.projectfloodlight.openflow.protocol.OFGroupAdd; @@ -424,16 +424,16 @@ public final class GroupModBuilder { return null; } - private OFAction buildExtensionAction(ExtensionInstruction i, DeviceId deviceId) { + private OFAction buildExtensionAction(ExtensionTreatment i, DeviceId deviceId) { if (!driverService.isPresent()) { log.error("No driver service present"); return null; } Driver driver = driverService.get().getDriver(deviceId); - if (driver.hasBehaviour(ExtensionInterpreter.class)) { + if (driver.hasBehaviour(ExtensionTreatmentInterpreter.class)) { DefaultDriverHandler handler = new DefaultDriverHandler(new DefaultDriverData(driver, deviceId)); - ExtensionInterpreter interpreter = handler.behaviour(ExtensionInterpreter.class); + ExtensionTreatmentInterpreter interpreter = handler.behaviour(ExtensionTreatmentInterpreter.class); return interpreter.mapInstruction(factory, i); } diff --git a/framework/src/onos/providers/ovsdb/app/features.xml b/framework/src/onos/providers/ovsdb/app/features.xml index 19fab4cd..d2cb0aa6 100644 --- a/framework/src/onos/providers/ovsdb/app/features.xml +++ b/framework/src/onos/providers/ovsdb/app/features.xml @@ -15,7 +15,6 @@ ~ limitations under the License. --> <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> - <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository> <feature name="${project.artifactId}" version="${project.version}" description="${project.description}"> <feature>onos-api</feature> diff --git a/framework/src/onos/providers/pcep/app/features.xml b/framework/src/onos/providers/pcep/app/features.xml index 1cd92e44..ad344cd7 100644 --- a/framework/src/onos/providers/pcep/app/features.xml +++ b/framework/src/onos/providers/pcep/app/features.xml @@ -15,7 +15,6 @@ ~ limitations under the License. --> <features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}"> - <repository>mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features</repository> <feature name="${project.artifactId}" version="${project.version}" description="${project.description}"> <feature>onos-api</feature> diff --git a/framework/src/onos/providers/pom.xml b/framework/src/onos/providers/pom.xml index a02f8d4f..32890b92 100644 --- a/framework/src/onos/providers/pom.xml +++ b/framework/src/onos/providers/pom.xml @@ -39,6 +39,7 @@ <module>null</module> <module>pcep</module> <module>ovsdb</module> + <module>bgp</module> </modules> <dependencies> |