aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/onos/providers
diff options
context:
space:
mode:
authorAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
committerAshlee Young <ashlee@onosfw.com>2015-09-09 22:15:21 -0700
commit13d05bc8458758ee39cb829098241e89616717ee (patch)
tree22a4d1ce65f15952f07a3df5af4b462b4697cb3a /framework/src/onos/providers
parent6139282e1e93c2322076de4b91b1c85d0bc4a8b3 (diff)
ONOS checkin based on commit tag e796610b1f721d02f9b0e213cf6f7790c10ecd60
Change-Id: Ife8810491034fe7becdba75dda20de4267bd15cd
Diffstat (limited to 'framework/src/onos/providers')
-rw-r--r--framework/src/onos/providers/host/pom.xml62
-rw-r--r--framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java439
-rw-r--r--framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java369
-rw-r--r--framework/src/onos/providers/lldp/pom.xml55
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java452
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java369
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java106
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java174
-rw-r--r--framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LLDPLinkProviderTest.java502
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java80
-rw-r--r--framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java147
-rw-r--r--framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json6
-rw-r--r--framework/src/onos/providers/netconf/app/app.xml24
-rw-r--r--framework/src/onos/providers/netconf/app/features.xml27
-rw-r--r--framework/src/onos/providers/netconf/app/pom.xml48
-rw-r--r--framework/src/onos/providers/netconf/device/pom.xml165
-rw-r--r--framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java304
-rw-r--r--framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java358
-rw-r--r--framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java421
-rw-r--r--framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java46
-rw-r--r--framework/src/onos/providers/netconf/flow/pom.xml259
-rw-r--r--framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java403
-rw-r--r--framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java139
-rw-r--r--framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java223
-rw-r--r--framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java21
-rw-r--r--framework/src/onos/providers/netconf/pom.xml49
-rw-r--r--framework/src/onos/providers/null/pom.xml61
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java49
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java30
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java44
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java58
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java44
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java132
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java169
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java420
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java57
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java43
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java223
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java382
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java77
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java52
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java68
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java32
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java20
-rw-r--r--framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java21
-rw-r--r--framework/src/onos/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml44
-rw-r--r--framework/src/onos/providers/openflow/app/app.xml33
-rw-r--r--framework/src/onos/providers/openflow/app/features.xml34
-rw-r--r--framework/src/onos/providers/openflow/app/pom.xml81
-rw-r--r--framework/src/onos/providers/openflow/device/pom.xml39
-rw-r--r--framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java569
-rw-r--r--framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java113
-rw-r--r--framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java402
-rw-r--r--framework/src/onos/providers/openflow/flow/pom.xml40
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java707
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java444
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java230
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java458
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java100
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java31
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java453
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java152
-rw-r--r--framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/openflow/group/pom.xml34
-rw-r--r--framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java343
-rw-r--r--framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java376
-rw-r--r--framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java111
-rw-r--r--framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java366
-rw-r--r--framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java397
-rw-r--r--framework/src/onos/providers/openflow/meter/pom.xml34
-rw-r--r--framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java159
-rw-r--r--framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java103
-rw-r--r--framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java393
-rw-r--r--framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java20
-rw-r--r--framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java22
-rw-r--r--framework/src/onos/providers/openflow/packet/pom.xml34
-rw-r--r--framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java99
-rw-r--r--framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java176
-rw-r--r--framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java21
-rw-r--r--framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java431
-rw-r--r--framework/src/onos/providers/openflow/pom.xml63
-rw-r--r--framework/src/onos/providers/ovsdb/app/app.xml30
-rw-r--r--framework/src/onos/providers/ovsdb/app/features.xml35
-rw-r--r--framework/src/onos/providers/ovsdb/app/pom.xml70
-rw-r--r--framework/src/onos/providers/ovsdb/device/pom.xml39
-rw-r--r--framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProvider.java134
-rw-r--r--framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/package-info.java21
-rw-r--r--framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java198
-rw-r--r--framework/src/onos/providers/ovsdb/host/pom.xml39
-rw-r--r--framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/OvsdbHostProvider.java148
-rw-r--r--framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/package-info.java21
-rw-r--r--framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java199
-rw-r--r--framework/src/onos/providers/ovsdb/pom.xml41
-rw-r--r--framework/src/onos/providers/ovsdb/tunnel/pom.xml39
-rw-r--r--framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProvider.java123
-rw-r--r--framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/package-info.java21
-rw-r--r--framework/src/onos/providers/ovsdb/tunnel/src/test/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProviderTest.java185
-rw-r--r--framework/src/onos/providers/pcep/app/app.xml27
-rw-r--r--framework/src/onos/providers/pcep/app/features.xml29
-rw-r--r--framework/src/onos/providers/pcep/app/pom.xml47
-rw-r--r--framework/src/onos/providers/pcep/pom.xml17
-rw-r--r--framework/src/onos/providers/pcep/topology/pom.xml18
-rw-r--r--framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/PcepTopologyProvider.java321
-rw-r--r--framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/package-info.java19
-rw-r--r--framework/src/onos/providers/pcep/tunnel/pom.xml39
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelApiMapper.java206
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelData.java386
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java1230
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/RequestType.java41
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/SrpIdGenerators.java56
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/TunnelStatsCollector.java104
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/package-info.java19
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientAdapter.java92
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientControllerAdapter.java189
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepControllerAdapter.java85
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepReleaseTunnelProviderTest.java116
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepSetupTunnelProviderTest.java102
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProviderTest.java101
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepUpdateTunnelProviderTest.java114
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelProviderRegistryAdapter.java56
-rw-r--r--framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelServiceAdapter.java107
-rw-r--r--framework/src/onos/providers/pom.xml87
127 files changed, 19413 insertions, 0 deletions
diff --git a/framework/src/onos/providers/host/pom.xml b/framework/src/onos/providers/host/pom.xml
new file mode 100644
index 00000000..d5231f8d
--- /dev/null
+++ b/framework/src/onos/providers/host/pom.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-host-provider</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS host tracking provider</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${project.version}</version>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
new file mode 100644
index 00000000..9a823630
--- /dev/null
+++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/HostLocationProvider.java
@@ -0,0 +1,439 @@
+/*
+ * 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.host.impl;
+
+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.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP6;
+import org.onlab.packet.IPacket;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.packet.ipv6.IExtensionHeader;
+import org.onlab.packet.ndp.NeighborAdvertisement;
+import org.onlab.packet.ndp.NeighborSolicitation;
+import org.onlab.packet.ndp.RouterAdvertisement;
+import org.onlab.packet.ndp.RouterSolicitation;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+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.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+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.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyService;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.Set;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to detect network end-station
+ * hosts.
+ */
+@Component(immediate = true)
+public class HostLocationProvider extends AbstractProvider implements HostProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TopologyService topologyService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private HostProviderService providerService;
+
+ private final InternalHostProvider processor = new InternalHostProvider();
+ private final DeviceListener deviceListener = new InternalDeviceListener();
+
+ private ApplicationId appId;
+
+ @Property(name = "hostRemovalEnabled", boolValue = true,
+ label = "Enable host removal on port/device down events")
+ private boolean hostRemovalEnabled = true;
+
+ @Property(name = "ipv6NeighborDiscovery", boolValue = false,
+ label = "Enable using IPv6 Neighbor Discovery by the " +
+ "Host Location Provider; default is false")
+ private boolean ipv6NeighborDiscovery = false;
+
+ /**
+ * Creates an OpenFlow host provider.
+ */
+ public HostLocationProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.host"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+ appId = coreService.registerApplication("org.onosproject.provider.host");
+
+ providerService = providerRegistry.register(this);
+ packetService.addProcessor(processor, PacketProcessor.advisor(1));
+ deviceService.addListener(deviceListener);
+ readComponentConfiguration(context);
+ requestIntercepts();
+
+ log.info("Started with Application ID {}", appId.id());
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+
+ withdrawIntercepts();
+
+ providerRegistry.unregister(this);
+ packetService.removeProcessor(processor);
+ deviceService.removeListener(deviceListener);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ readComponentConfiguration(context);
+ requestIntercepts();
+ }
+
+ /**
+ * Request packet intercepts.
+ */
+ private void requestIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_ARP);
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+
+ // IPv6 Neighbor Solicitation packet.
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
+ selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
+ if (ipv6NeighborDiscovery) {
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ } else {
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+
+ // IPv6 Neighbor Advertisement packet.
+ selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
+ if (ipv6NeighborDiscovery) {
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ } else {
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+ }
+
+ /**
+ * Withdraw packet intercepts.
+ */
+ private void withdrawIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_ARP);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+
+ // IPv6 Neighbor Solicitation packet.
+ selector.matchEthType(Ethernet.TYPE_IPV6);
+ selector.matchIPProtocol(IPv6.PROTOCOL_ICMP6);
+ selector.matchIcmpv6Type(ICMP6.NEIGHBOR_SOLICITATION);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+
+ // IPv6 Neighbor Advertisement packet.
+ selector.matchIcmpv6Type(ICMP6.NEIGHBOR_ADVERTISEMENT);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+
+ /**
+ * Extracts properties from the component configuration context.
+ *
+ * @param context the component context
+ */
+ private void readComponentConfiguration(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ Boolean flag;
+
+ flag = isPropertyEnabled(properties, "hostRemovalEnabled");
+ if (flag == null) {
+ log.info("Host removal on port/device down events is not configured, " +
+ "using current value of {}", hostRemovalEnabled);
+ } else {
+ hostRemovalEnabled = flag;
+ log.info("Configured. Host removal on port/device down events is {}",
+ hostRemovalEnabled ? "enabled" : "disabled");
+ }
+
+ flag = isPropertyEnabled(properties, "ipv6NeighborDiscovery");
+ if (flag == null) {
+ log.info("Using IPv6 Neighbor Discovery is not configured, " +
+ "using current value of {}", ipv6NeighborDiscovery);
+ } else {
+ ipv6NeighborDiscovery = flag;
+ log.info("Configured. Using IPv6 Neighbor Discovery is {}",
+ ipv6NeighborDiscovery ? "enabled" : "disabled");
+ }
+ }
+
+ /**
+ * Check property name is defined and set to true.
+ *
+ * @param properties properties to be looked up
+ * @param propertyName the name of the property to look up
+ * @return value when the propertyName is defined or return null
+ */
+ private static Boolean isPropertyEnabled(Dictionary<?, ?> properties,
+ String propertyName) {
+ Boolean value = null;
+ try {
+ String s = (String) properties.get(propertyName);
+ value = isNullOrEmpty(s) ? null : s.trim().equals("true");
+ } catch (ClassCastException e) {
+ // No propertyName defined.
+ value = null;
+ }
+ return value;
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ log.info("Triggering probe on device {}", host);
+ }
+
+ private class InternalHostProvider implements PacketProcessor {
+ /**
+ * Update host location only.
+ *
+ * @param hid host ID
+ * @param mac source Mac address
+ * @param vlan VLAN ID
+ * @param hloc host location
+ */
+ private void updateLocation(HostId hid, MacAddress mac,
+ VlanId vlan, HostLocation hloc) {
+ HostDescription desc = new DefaultHostDescription(mac, vlan, hloc);
+ try {
+ providerService.hostDetected(hid, desc);
+ } catch (IllegalStateException e) {
+ log.debug("Host {} suppressed", hid);
+ }
+ }
+
+ /**
+ * Update host location and IP address.
+ *
+ * @param hid host ID
+ * @param mac source Mac address
+ * @param vlan VLAN ID
+ * @param hloc host location
+ * @param ip source IP address
+ */
+ private void updateLocationIP(HostId hid, MacAddress mac,
+ VlanId vlan, HostLocation hloc,
+ IpAddress ip) {
+ HostDescription desc = ip.isZero() || ip.isSelfAssigned() ?
+ new DefaultHostDescription(mac, vlan, hloc) :
+ new DefaultHostDescription(mac, vlan, hloc, ip);
+ try {
+ providerService.hostDetected(hid, desc);
+ } catch (IllegalStateException e) {
+ log.debug("Host {} suppressed", hid);
+ }
+ }
+
+ @Override
+ public void process(PacketContext context) {
+ if (context == null) {
+ return;
+ }
+
+ Ethernet eth = context.inPacket().parsed();
+ if (eth == null) {
+ return;
+ }
+ MacAddress srcMac = eth.getSourceMAC();
+
+ VlanId vlan = VlanId.vlanId(eth.getVlanID());
+ ConnectPoint heardOn = context.inPacket().receivedFrom();
+
+ // If this arrived on control port, bail out.
+ if (heardOn.port().isLogical()) {
+ return;
+ }
+
+ // If this is not an edge port, bail out.
+ Topology topology = topologyService.currentTopology();
+ if (topologyService.isInfrastructure(topology, heardOn)) {
+ return;
+ }
+
+ HostLocation hloc = new HostLocation(heardOn, System.currentTimeMillis());
+ HostId hid = HostId.hostId(eth.getSourceMAC(), vlan);
+
+ // ARP: possible new hosts, update both location and IP
+ if (eth.getEtherType() == Ethernet.TYPE_ARP) {
+ ARP arp = (ARP) eth.getPayload();
+ IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET,
+ arp.getSenderProtocolAddress());
+ updateLocationIP(hid, srcMac, vlan, hloc, ip);
+
+ // IPv4: update location only
+ } else if (eth.getEtherType() == Ethernet.TYPE_IPV4) {
+ updateLocation(hid, srcMac, vlan, hloc);
+
+ //
+ // NeighborAdvertisement and NeighborSolicitation: possible
+ // new hosts, update both location and IP.
+ //
+ // IPv6: update location only
+ } else if (eth.getEtherType() == Ethernet.TYPE_IPV6) {
+ IPv6 ipv6 = (IPv6) eth.getPayload();
+ IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET6,
+ ipv6.getSourceAddress());
+
+ // skip extension headers
+ IPacket pkt = ipv6;
+ while (pkt.getPayload() != null &&
+ pkt.getPayload() instanceof IExtensionHeader) {
+ pkt = pkt.getPayload();
+ }
+
+ // Neighbor Discovery Protocol
+ pkt = pkt.getPayload();
+ if (pkt != null && pkt instanceof ICMP6) {
+ pkt = pkt.getPayload();
+ // RouterSolicitation, RouterAdvertisement
+ if (pkt != null && (pkt instanceof RouterAdvertisement ||
+ pkt instanceof RouterSolicitation)) {
+ return;
+ }
+ if (pkt != null && (pkt instanceof NeighborSolicitation ||
+ pkt instanceof NeighborAdvertisement)) {
+ // Duplicate Address Detection
+ if (ip.isZero()) {
+ return;
+ }
+ // NeighborSolicitation, NeighborAdvertisement
+ updateLocationIP(hid, srcMac, vlan, hloc, ip);
+ return;
+ }
+ }
+
+ // multicast
+ if (eth.isMulticast()) {
+ return;
+ }
+
+ // normal IPv6 packets
+ updateLocation(hid, srcMac, vlan, hloc);
+ }
+ }
+ }
+
+ // Auxiliary listener to device events.
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ Device device = event.subject();
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ break;
+ case DEVICE_AVAILABILITY_CHANGED:
+ if (hostRemovalEnabled &&
+ !deviceService.isAvailable(device.id())) {
+ removeHosts(hostService.getConnectedHosts(device.id()));
+ }
+ break;
+ case DEVICE_SUSPENDED:
+ case DEVICE_UPDATED:
+ // Nothing to do?
+ break;
+ case DEVICE_REMOVED:
+ if (hostRemovalEnabled) {
+ removeHosts(hostService.getConnectedHosts(device.id()));
+ }
+ break;
+ case PORT_ADDED:
+ break;
+ case PORT_UPDATED:
+ if (hostRemovalEnabled) {
+ ConnectPoint point =
+ new ConnectPoint(device.id(), event.port().number());
+ removeHosts(hostService.getConnectedHosts(point));
+ }
+ break;
+ case PORT_REMOVED:
+ // Nothing to do?
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ // Signals host vanish for all specified hosts.
+ private void removeHosts(Set<Host> hosts) {
+ for (Host host : hosts) {
+ providerService.hostVanished(host.id());
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java
new file mode 100644
index 00000000..57c5c3f6
--- /dev/null
+++ b/framework/src/onos/providers/host/src/main/java/org/onosproject/provider/host/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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 packet service as a means of host discovery and tracking.
+ */
+package org.onosproject.provider.host.impl;
diff --git a/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
new file mode 100644
index 00000000..b9d90976
--- /dev/null
+++ b/framework/src/onos/providers/host/src/test/java/org/onosproject/provider/host/impl/HostLocationProviderTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.host.impl;
+
+import com.google.common.collect.ImmutableSet;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.osgi.ComponentContextAdapter;
+import org.onlab.packet.ARP;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.DefaultHost;
+import org.onosproject.net.DefaultPort;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+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.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostServiceAdapter;
+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 org.onosproject.net.topology.Topology;
+import org.onosproject.net.topology.TopologyServiceAdapter;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Set;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+import static org.onlab.packet.VlanId.vlanId;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.HostId.hostId;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.net.device.DeviceEvent.Type.*;
+
+public class HostLocationProviderTest {
+
+ private static final Integer INPORT = 10;
+ private static final String DEV1 = "of:1";
+ private static final String DEV2 = "of:2";
+ private static final String DEV3 = "of:3";
+
+ private static final VlanId VLAN = vlanId();
+ private static final MacAddress MAC = MacAddress.valueOf("00:00:11:00:00:01");
+ private static final MacAddress BCMAC = MacAddress.valueOf("ff:ff:ff:ff:ff:ff");
+ private static final byte[] IP = new byte[]{10, 0, 0, 1};
+
+ private static final IpAddress IP_ADDRESS =
+ IpAddress.valueOf(IpAddress.Version.INET, IP);
+ private static final HostLocation LOCATION =
+ new HostLocation(deviceId(DEV1), portNumber(INPORT), 0L);
+
+ private static final DefaultHost HOST =
+ new DefaultHost(ProviderId.NONE, hostId(MAC), MAC,
+ vlanId(VlanId.UNTAGGED), LOCATION,
+ ImmutableSet.of(IP_ADDRESS));
+
+ private static final ComponentContextAdapter CTX_FOR_REMOVE =
+ new ComponentContextAdapter() {
+ @Override
+ public Dictionary getProperties() {
+ Hashtable<String, String> props = new Hashtable<>();
+ props.put("hostRemovalEnabled", "true");
+ return props;
+ }
+ };
+
+ public static final ComponentContextAdapter CTX_FOR_NO_REMOVE =
+ new ComponentContextAdapter() {
+ @Override
+ public Dictionary getProperties() {
+ return new Hashtable();
+ }
+ };
+
+ private final HostLocationProvider provider = new HostLocationProvider();
+ private final TestHostRegistry hostRegistry = new TestHostRegistry();
+ private final TestTopologyService topoService = new TestTopologyService();
+ private final TestDeviceService deviceService = new TestDeviceService();
+ private final TestHostService hostService = new TestHostService();
+ private final TestPacketService packetService = new TestPacketService();
+
+ private PacketProcessor testProcessor;
+ private CoreService coreService;
+ private TestHostProviderService providerService;
+
+ private ApplicationId appId =
+ new DefaultApplicationId(100, "org.onosproject.provider.host");
+
+ @Before
+ public void setUp() {
+
+ coreService = createMock(CoreService.class);
+ expect(coreService.registerApplication(appId.name()))
+ .andReturn(appId).anyTimes();
+ replay(coreService);
+
+ provider.cfgService = new ComponentConfigAdapter();
+ provider.coreService = coreService;
+
+ provider.providerRegistry = hostRegistry;
+ provider.topologyService = topoService;
+ provider.packetService = packetService;
+ provider.deviceService = deviceService;
+ provider.hostService = hostService;
+
+ provider.activate(CTX_FOR_NO_REMOVE);
+ }
+
+ @Test
+ public void basics() {
+ assertNotNull("registration expected", providerService);
+ assertEquals("incorrect provider", provider, providerService.provider());
+ }
+
+ @Test
+ public void events() {
+ // new host
+ testProcessor.process(new TestPacketContext(DEV1));
+ assertNotNull("new host expected", providerService.added);
+ assertNull("host motion unexpected", providerService.moved);
+
+ // the host moved to new switch
+ testProcessor.process(new TestPacketContext(DEV2));
+ assertNotNull("host motion expected", providerService.moved);
+
+ // the host was misheard on a spine
+ testProcessor.process(new TestPacketContext(DEV3));
+ assertNull("host misheard on spine switch", providerService.spine);
+ }
+
+ @Test
+ public void removeHostByDeviceRemove() {
+ provider.modified(CTX_FOR_REMOVE);
+ testProcessor.process(new TestPacketContext(DEV1));
+ Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
+ "m", "h", "s", "n", new ChassisId(0L));
+ deviceService.listener.event(new DeviceEvent(DEVICE_REMOVED, device));
+ assertEquals("incorrect remove count", 1, providerService.removeCount);
+ }
+
+ @Test
+ public void removeHostByDeviceOffline() {
+ provider.modified(CTX_FOR_REMOVE);
+ testProcessor.process(new TestPacketContext(DEV1));
+ Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
+ "m", "h", "s", "n", new ChassisId(0L));
+ deviceService.listener.event(new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device));
+ assertEquals("incorrect remove count", 1, providerService.removeCount);
+ }
+
+ @Test
+ public void removeHostByDevicePortDown() {
+ provider.modified(CTX_FOR_REMOVE);
+ testProcessor.process(new TestPacketContext(DEV1));
+ Device device = new DefaultDevice(ProviderId.NONE, deviceId(DEV1), SWITCH,
+ "m", "h", "s", "n", new ChassisId(0L));
+ deviceService.listener.event(new DeviceEvent(PORT_UPDATED, device,
+ new DefaultPort(device, portNumber(INPORT),
+ false)));
+ assertEquals("incorrect remove count", 1, providerService.removeCount);
+ }
+
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.coreService = null;
+ provider.providerRegistry = null;
+ }
+
+ private class TestHostRegistry implements HostProviderRegistry {
+
+ @Override
+ public HostProviderService register(HostProvider provider) {
+ providerService = new TestHostProviderService(provider);
+ return providerService;
+ }
+
+ @Override
+ public void unregister(HostProvider provider) {
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+
+ }
+
+ private class TestHostProviderService
+ extends AbstractProviderService<HostProvider>
+ implements HostProviderService {
+
+ DeviceId added = null;
+ DeviceId moved = null;
+ DeviceId spine = null;
+ public int removeCount;
+
+ protected TestHostProviderService(HostProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void hostDetected(HostId hostId, HostDescription hostDescription) {
+ DeviceId descr = hostDescription.location().deviceId();
+ if (added == null) {
+ added = descr;
+ } else if ((moved == null) && !descr.equals(added)) {
+ moved = descr;
+ } else {
+ spine = descr;
+ }
+ }
+
+ @Override
+ public void hostVanished(HostId hostId) {
+ removeCount++;
+ }
+
+ }
+
+ private class TestPacketService extends PacketServiceAdapter {
+ @Override
+ public void addProcessor(PacketProcessor processor, int priority) {
+ testProcessor = processor;
+ }
+ }
+
+
+ private class TestTopologyService extends TopologyServiceAdapter {
+ @Override
+ public boolean isInfrastructure(Topology topology,
+ ConnectPoint connectPoint) {
+ //simulate DPID3 as an infrastructure switch
+ if ((connectPoint.deviceId()).equals(deviceId(DEV3))) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ private class TestPacketContext implements PacketContext {
+
+ private final String deviceId;
+
+ public TestPacketContext(String deviceId) {
+ this.deviceId = deviceId;
+ }
+
+ @Override
+ public long time() {
+ return 0;
+ }
+
+ @Override
+ public InboundPacket inPacket() {
+ ARP arp = new ARP();
+ arp.setSenderProtocolAddress(IP)
+ .setSenderHardwareAddress(MAC.toBytes())
+ .setTargetHardwareAddress(BCMAC.toBytes())
+ .setTargetProtocolAddress(IP);
+
+ Ethernet eth = new Ethernet();
+ eth.setEtherType(Ethernet.TYPE_ARP)
+ .setVlanID(VLAN.toShort())
+ .setSourceMACAddress(MAC.toBytes())
+ .setDestinationMACAddress(BCMAC)
+ .setPayload(arp);
+ ConnectPoint receivedFrom = new ConnectPoint(deviceId(deviceId),
+ portNumber(INPORT));
+ return new DefaultInboundPacket(receivedFrom, eth,
+ ByteBuffer.wrap(eth.serialize()));
+ }
+
+ @Override
+ public OutboundPacket outPacket() {
+ return null;
+ }
+
+ @Override
+ public TrafficTreatment.Builder treatmentBuilder() {
+ return null;
+ }
+
+ @Override
+ public void send() {
+
+ }
+
+ @Override
+ public boolean block() {
+ return false;
+ }
+
+ @Override
+ public boolean isHandled() {
+ return false;
+ }
+ }
+
+ private class TestDeviceService extends DeviceServiceAdapter {
+ private DeviceListener listener;
+
+ @Override
+ public void addListener(DeviceListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return Collections.emptyList();
+ }
+ }
+
+ private class TestHostService extends HostServiceAdapter {
+ @Override
+ public Set<Host> getConnectedHosts(ConnectPoint connectPoint) {
+ return ImmutableSet.of(HOST);
+ }
+
+ @Override
+ public Set<Host> getConnectedHosts(DeviceId deviceId) {
+ return ImmutableSet.of(HOST);
+ }
+
+ }
+}
diff --git a/framework/src/onos/providers/lldp/pom.xml b/framework/src/onos/providers/lldp/pom.xml
new file mode 100644
index 00000000..bc13793e
--- /dev/null
+++ b/framework/src/onos/providers/lldp/pom.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-lldp-provider</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS LLDP Link Discovery</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+</project>
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..e9e2bfad
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LLDPLinkProvider.java
@@ -0,0 +1,452 @@
+/*
+ * 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.base.Strings;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+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.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.Port;
+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.LinkProvider;
+import org.onosproject.net.link.LinkProviderRegistry;
+import org.onosproject.net.link.LinkProviderService;
+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 java.io.IOException;
+import java.util.Dictionary;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ScheduledExecutorService;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller 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 PROP_USE_BDDP = "useBDDP";
+ private static final String PROP_DISABLE_LD = "disableLinkDiscovery";
+ private static final String PROP_LLDP_SUPPRESSION = "lldpSuppression";
+
+ private static final String DEFAULT_LLDP_SUPPRESSION_CONFIG = "../config/lldp_suppression.json";
+
+ 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 PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService masterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private LinkProviderService providerService;
+
+ private ScheduledExecutorService executor;
+
+ @Property(name = PROP_USE_BDDP, boolValue = true,
+ label = "Use BDDP for link discovery")
+ private boolean useBDDP = true;
+
+ @Property(name = PROP_DISABLE_LD, boolValue = false,
+ label = "Permanently disable link discovery")
+ private boolean disableLinkDiscovery = false;
+
+ private static final long INIT_DELAY = 5;
+ private static final long DELAY = 5;
+
+ @Property(name = PROP_LLDP_SUPPRESSION, value = DEFAULT_LLDP_SUPPRESSION_CONFIG,
+ label = "Path to LLDP suppression configuration file")
+ private String lldpSuppression = DEFAULT_LLDP_SUPPRESSION_CONFIG;
+
+
+ private final InternalLinkProvider listener = new InternalLinkProvider();
+
+ private final InternalRoleListener roleListener = new InternalRoleListener();
+
+ protected final Map<DeviceId, LinkDiscovery> discoverers = new ConcurrentHashMap<>();
+
+ private SuppressionRules rules;
+ private ApplicationId appId;
+
+ /**
+ * 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);
+
+ // to load configuration at startup
+ modified(context);
+ if (disableLinkDiscovery) {
+ log.info("LinkDiscovery has been permanently disabled by configuration");
+ return;
+ }
+
+ providerService = providerRegistry.register(this);
+ deviceService.addListener(listener);
+ packetService.addProcessor(listener, PacketProcessor.advisor(0));
+ masterService.addListener(roleListener);
+
+ LinkDiscovery ld;
+ for (Device device : deviceService.getAvailableDevices()) {
+ if (rules.isSuppressed(device)) {
+ log.debug("LinkDiscovery from {} disabled by configuration", device.id());
+ continue;
+ }
+ ld = new LinkDiscovery(device, packetService, masterService,
+ providerService, useBDDP);
+ discoverers.put(device.id(), ld);
+ addPorts(ld, device.id());
+ }
+
+ executor = newSingleThreadScheduledExecutor(groupedThreads("onos/device", "sync-%d"));
+ executor.scheduleAtFixedRate(new SyncDeviceInfoTask(), INIT_DELAY, DELAY, SECONDS);
+
+ requestIntercepts();
+
+ log.info("Started");
+ }
+
+ private void addPorts(LinkDiscovery discoverer, DeviceId deviceId) {
+ for (Port p : deviceService.getPorts(deviceId)) {
+ if (rules.isSuppressed(p)) {
+ continue;
+ }
+ if (!p.number().isLogical()) {
+ discoverer.addPort(p);
+ }
+ }
+ }
+
+ @Deactivate
+ public void deactivate() {
+ cfgService.unregisterProperties(getClass(), false);
+ if (disableLinkDiscovery) {
+ return;
+ }
+
+ withdrawIntercepts();
+
+ providerRegistry.unregister(this);
+ deviceService.removeListener(listener);
+ packetService.removeProcessor(listener);
+ masterService.removeListener(roleListener);
+
+ executor.shutdownNow();
+ discoverers.values().forEach(LinkDiscovery::stop);
+ discoverers.clear();
+ providerService = null;
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ loadSuppressionRules();
+ return;
+ }
+ @SuppressWarnings("rawtypes")
+ Dictionary properties = context.getProperties();
+
+ String s = get(properties, PROP_DISABLE_LD);
+ if (!Strings.isNullOrEmpty(s)) {
+ disableLinkDiscovery = Boolean.valueOf(s);
+ }
+ s = get(properties, PROP_USE_BDDP);
+ if (!Strings.isNullOrEmpty(s)) {
+ useBDDP = Boolean.valueOf(s);
+ }
+ s = get(properties, PROP_LLDP_SUPPRESSION);
+ if (!Strings.isNullOrEmpty(s)) {
+ lldpSuppression = s;
+ }
+ requestIntercepts();
+ loadSuppressionRules();
+ }
+
+ private void loadSuppressionRules() {
+ SuppressionRulesStore store = new SuppressionRulesStore(lldpSuppression);
+ try {
+ log.info("Reading suppression rules from {}", lldpSuppression);
+ rules = store.read();
+ } catch (IOException e) {
+ log.info("Failed to load {}, using built-in rules", lldpSuppression);
+ // default rule to suppress ROADM to maintain compatibility
+ rules = new SuppressionRules(ImmutableSet.of(),
+ EnumSet.of(Device.Type.ROADM),
+ ImmutableMap.of());
+ }
+
+ // should refresh discoverers when we need dynamic reconfiguration
+ }
+
+ /**
+ * Request packet intercepts.
+ */
+ private void requestIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_LLDP);
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+
+ selector.matchEthType(Ethernet.TYPE_BSN);
+ if (useBDDP) {
+ packetService.requestPackets(selector.build(), PacketPriority.CONTROL, appId);
+ } else {
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+ }
+
+ /**
+ * Withdraw packet intercepts.
+ */
+ private void withdrawIntercepts() {
+ TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+ selector.matchEthType(Ethernet.TYPE_LLDP);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ selector.matchEthType(Ethernet.TYPE_BSN);
+ packetService.cancelPackets(selector.build(), PacketPriority.CONTROL, appId);
+ }
+
+ private LinkDiscovery createLinkDiscovery(Device device) {
+ return new LinkDiscovery(device, packetService, masterService,
+ providerService, useBDDP);
+ }
+
+ 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 (rules.isSuppressed(device)) {
+ return;
+ }
+ synchronized (discoverers) {
+ if (!discoverers.containsKey(deviceId)) {
+ // ideally, should never reach here
+ log.debug("Device mastership changed ({}) {}", event.type(), deviceId);
+ discoverers.put(deviceId, createLinkDiscovery(device));
+ }
+ }
+ }
+
+ }
+
+ private class InternalLinkProvider implements PacketProcessor, DeviceListener {
+
+ @Override
+ public void event(DeviceEvent event) {
+ LinkDiscovery ld = null;
+ 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:
+ synchronized (discoverers) {
+ ld = discoverers.get(deviceId);
+ if (ld == null) {
+ if (rules.isSuppressed(device)) {
+ log.debug("LinkDiscovery from {} disabled by configuration", device.id());
+ return;
+ }
+ log.debug("Device added ({}) {}", event.type(), deviceId);
+ discoverers.put(deviceId, createLinkDiscovery(device));
+ } else {
+ if (ld.isStopped()) {
+ log.debug("Device restarted ({}) {}", event.type(), deviceId);
+ ld.start();
+ }
+ }
+ }
+ break;
+ case PORT_ADDED:
+ case PORT_UPDATED:
+ if (port.isEnabled()) {
+ ld = discoverers.get(deviceId);
+ if (ld == null) {
+ return;
+ }
+ if (rules.isSuppressed(port)) {
+ log.debug("LinkDiscovery from {}@{} disabled by configuration",
+ port.number(), device.id());
+ return;
+ }
+ if (!port.number().isLogical()) {
+ log.debug("Port added {}", port);
+ ld.addPort(port);
+ }
+ } else {
+ log.debug("Port down {}", port);
+ ConnectPoint point = new ConnectPoint(deviceId, port.number());
+ providerService.linksVanished(point);
+ }
+ break;
+ case PORT_REMOVED:
+ log.debug("Port removed {}", port);
+ ConnectPoint point = new ConnectPoint(deviceId, port.number());
+ providerService.linksVanished(point);
+
+ break;
+ case DEVICE_REMOVED:
+ case DEVICE_SUSPENDED:
+ log.debug("Device removed {}", deviceId);
+ ld = discoverers.get(deviceId);
+ if (ld == null) {
+ return;
+ }
+ ld.stop();
+ providerService.linksVanished(deviceId);
+ break;
+ case DEVICE_AVAILABILITY_CHANGED:
+ ld = discoverers.get(deviceId);
+ if (ld == null) {
+ return;
+ }
+ if (deviceService.isAvailable(deviceId)) {
+ log.debug("Device up {}", deviceId);
+ ld.start();
+ } else {
+ providerService.linksVanished(deviceId);
+ log.debug("Device down {}", deviceId);
+ ld.stop();
+ }
+ break;
+ case PORT_STATS_UPDATED:
+ break;
+ default:
+ log.debug("Unknown event {}", event);
+ }
+ }
+
+ @Override
+ public void process(PacketContext context) {
+ if (context == null) {
+ return;
+ }
+ LinkDiscovery ld = discoverers.get(context.inPacket().receivedFrom().deviceId());
+ if (ld == null) {
+ return;
+ }
+
+ if (ld.handleLLDP(context)) {
+ context.block();
+ }
+ }
+ }
+
+ 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 {
+ for (Device dev : deviceService.getDevices()) {
+ if (rules.isSuppressed(dev)) {
+ continue;
+ }
+ DeviceId did = dev.id();
+ synchronized (discoverers) {
+ LinkDiscovery discoverer = discoverers.get(did);
+ if (discoverer == null) {
+ discoverer = createLinkDiscovery(dev);
+ discoverers.put(did, discoverer);
+ }
+
+ addPorts(discoverer, did);
+ }
+ }
+ } catch (Exception e) {
+ // catch all Exception to avoid Scheduled task being suppressed.
+ log.error("Exception thrown during synchronization process", e);
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
new file mode 100644
index 00000000..a81eeb1d
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/LinkDiscovery.java
@@ -0,0 +1,369 @@
+/*
+ * 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.Maps;
+import com.google.common.collect.Sets;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ONOSLLDP;
+import org.onlab.util.Timer;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link.Type;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.net.flow.DefaultTrafficTreatment.builder;
+import static org.slf4j.LoggerFactory.getLogger;
+
+// TODO: add 'fast discovery' mode: drop LLDPs in destination switch but listen for flow_removed messages
+// FIXME: add ability to track links using port pairs or the link inventory
+
+/**
+ * Run discovery process from a physical switch. Ports are initially labeled as
+ * slow ports. When an LLDP is successfully received, label the remote port as
+ * fast. Every probeRate milliseconds, loop over all fast ports and send an
+ * LLDP, send an LLDP for a single slow port. Based on FlowVisor topology
+ * discovery implementation.
+ */
+public class LinkDiscovery implements TimerTask {
+
+ private final Logger log = getLogger(getClass());
+
+ private static final short MAX_PROBE_COUNT = 3; // probes to send before link is removed
+ private static final short DEFAULT_PROBE_RATE = 3000; // millis
+ private static final String SRC_MAC = "DE:AD:BE:EF:BA:11";
+ private static final String SERVICE_NULL = "Service cannot be null";
+
+ private final Device device;
+
+ // send 1 probe every probeRate milliseconds
+ private final long probeRate = DEFAULT_PROBE_RATE;
+
+ private final Set<Long> slowPorts = Sets.newConcurrentHashSet();
+ // ports, known to have incoming links
+ private final Set<Long> fastPorts = Sets.newConcurrentHashSet();
+
+ // number of unacknowledged probes per port
+ private final Map<Long, AtomicInteger> portProbeCount = Maps.newHashMap();
+
+ private final ONOSLLDP lldpPacket;
+ private final Ethernet ethPacket;
+ private Ethernet bddpEth;
+ private final boolean useBDDP;
+
+ private Timeout timeout;
+ private volatile boolean isStopped;
+
+ private final LinkProviderService linkProvider;
+ private final PacketService pktService;
+ private final MastershipService mastershipService;
+
+ /**
+ * Instantiates discovery manager for the given physical switch. Creates a
+ * generic LLDP packet that will be customized for the port it is sent out on.
+ * Starts the the timer for the discovery process.
+ *
+ * @param device the physical switch
+ * @param pktService packet service
+ * @param masterService mastership service
+ * @param providerService link provider service
+ * @param useBDDP flag to also use BDDP for discovery
+ */
+ public LinkDiscovery(Device device, PacketService pktService,
+ MastershipService masterService,
+ LinkProviderService providerService, Boolean... useBDDP) {
+ this.device = device;
+ this.linkProvider = checkNotNull(providerService, SERVICE_NULL);
+ this.pktService = checkNotNull(pktService, SERVICE_NULL);
+ this.mastershipService = checkNotNull(masterService, SERVICE_NULL);
+
+ lldpPacket = new ONOSLLDP();
+ lldpPacket.setChassisId(device.chassisId());
+ lldpPacket.setDevice(device.id().toString());
+
+ ethPacket = new Ethernet();
+ ethPacket.setEtherType(Ethernet.TYPE_LLDP);
+ ethPacket.setDestinationMACAddress(ONOSLLDP.LLDP_NICIRA);
+ ethPacket.setPayload(this.lldpPacket);
+ ethPacket.setPad(true);
+
+ this.useBDDP = useBDDP.length > 0 ? useBDDP[0] : false;
+ if (this.useBDDP) {
+ bddpEth = new Ethernet();
+ bddpEth.setPayload(lldpPacket);
+ bddpEth.setEtherType(Ethernet.TYPE_BSN);
+ bddpEth.setDestinationMACAddress(ONOSLLDP.BDDP_MULTICAST);
+ bddpEth.setPad(true);
+ log.info("Using BDDP to discover network");
+ }
+
+ isStopped = true;
+ start();
+ log.debug("Started discovery manager for switch {}", device.id());
+
+ }
+
+ /**
+ * Add physical port port to discovery process.
+ * Send out initial LLDP and label it as slow port.
+ *
+ * @param port the port
+ */
+ public void addPort(Port port) {
+ boolean newPort = false;
+ synchronized (this) {
+ if (!containsPort(port.number().toLong())) {
+ newPort = true;
+ slowPorts.add(port.number().toLong());
+ }
+ }
+
+ boolean isMaster = mastershipService.isLocalMaster(device.id());
+ if (newPort && isMaster) {
+ log.debug("Sending init probe to port {}@{}", port.number().toLong(), device.id());
+ sendProbes(port.number().toLong());
+ }
+ }
+
+ /**
+ * Removes physical port from discovery process.
+ *
+ * @param port the port
+ */
+ public void removePort(Port port) {
+ // Ignore ports that are not on this switch
+ long portnum = port.number().toLong();
+ synchronized (this) {
+ if (slowPorts.contains(portnum)) {
+ slowPorts.remove(portnum);
+
+ } else if (fastPorts.contains(portnum)) {
+ fastPorts.remove(portnum);
+ portProbeCount.remove(portnum);
+ // no iterator to update
+ } else {
+ log.warn("Tried to dynamically remove non-existing port {}", portnum);
+ }
+ }
+ }
+
+ /**
+ * Method called by remote port to acknowledge receipt of LLDP sent by
+ * this port. If slow port, updates label to fast. If fast port, decrements
+ * number of unacknowledged probes.
+ *
+ * @param portNumber the port
+ */
+ public void ackProbe(Long portNumber) {
+ synchronized (this) {
+ if (slowPorts.contains(portNumber)) {
+ log.debug("Setting slow port to fast: {}:{}", device.id(), portNumber);
+ slowPorts.remove(portNumber);
+ fastPorts.add(portNumber);
+ portProbeCount.put(portNumber, new AtomicInteger(0));
+ } else if (fastPorts.contains(portNumber)) {
+ portProbeCount.get(portNumber).set(0);
+ } else {
+ log.debug("Got ackProbe for non-existing port: {}", portNumber);
+ }
+ }
+ }
+
+
+ /**
+ * Handles an incoming LLDP packet. Creates link in topology and sends ACK
+ * to port where LLDP originated.
+ *
+ * @param context packet context
+ * @return true if handled
+ */
+ public boolean handleLLDP(PacketContext context) {
+ Ethernet eth = context.inPacket().parsed();
+ if (eth == null) {
+ return false;
+ }
+
+ ONOSLLDP onoslldp = ONOSLLDP.parseONOSLLDP(eth);
+ if (onoslldp != null) {
+ PortNumber srcPort = portNumber(onoslldp.getPort());
+ PortNumber dstPort = context.inPacket().receivedFrom().port();
+ DeviceId srcDeviceId = DeviceId.deviceId(onoslldp.getDeviceString());
+ DeviceId dstDeviceId = context.inPacket().receivedFrom().deviceId();
+ ackProbe(dstPort.toLong());
+
+ ConnectPoint src = new ConnectPoint(srcDeviceId, srcPort);
+ ConnectPoint dst = new ConnectPoint(dstDeviceId, dstPort);
+
+ LinkDescription ld = eth.getEtherType() == Ethernet.TYPE_LLDP ?
+ new DefaultLinkDescription(src, dst, Type.DIRECT) :
+ new DefaultLinkDescription(src, dst, Type.INDIRECT);
+
+ try {
+ linkProvider.linkDetected(ld);
+ } catch (IllegalStateException e) {
+ return true;
+ }
+ return true;
+ }
+ return false;
+ }
+
+
+ /**
+ * Execute this method every t milliseconds. Loops over all ports
+ * labeled as fast and sends out an LLDP. Send out an LLDP on a single slow
+ * port.
+ *
+ * @param t timeout
+ */
+ @Override
+ public void run(Timeout t) {
+ if (isStopped()) {
+ return;
+ }
+ if (!mastershipService.isLocalMaster(device.id())) {
+ if (!isStopped()) {
+ // reschedule timer
+ timeout = Timer.getTimer().newTimeout(this, probeRate, MILLISECONDS);
+ }
+ return;
+ }
+
+ log.trace("Sending probes from {}", device.id());
+ synchronized (this) {
+ Iterator<Long> fastIterator = fastPorts.iterator();
+ while (fastIterator.hasNext()) {
+ long portNumber = fastIterator.next();
+ int probeCount = portProbeCount.get(portNumber).getAndIncrement();
+
+ if (probeCount < LinkDiscovery.MAX_PROBE_COUNT) {
+ log.trace("Sending fast probe to port {}", portNumber);
+ sendProbes(portNumber);
+
+ } else {
+ // Link down, demote to slowPorts; update fast and slow ports
+ fastIterator.remove();
+ slowPorts.add(portNumber);
+ portProbeCount.remove(portNumber);
+
+ ConnectPoint cp = new ConnectPoint(device.id(), portNumber(portNumber));
+ log.debug("Link down -> {}", cp);
+ linkProvider.linksVanished(cp);
+ }
+ }
+
+ // send a probe for the next slow port
+ for (long portNumber : slowPorts) {
+ log.trace("Sending slow probe to port {}", portNumber);
+ sendProbes(portNumber);
+ }
+ }
+
+ if (!isStopped()) {
+ // reschedule timer
+ timeout = Timer.getTimer().newTimeout(this, probeRate, MILLISECONDS);
+ }
+ }
+
+ public synchronized void stop() {
+ isStopped = true;
+ timeout.cancel();
+ }
+
+ public synchronized void start() {
+ if (isStopped) {
+ isStopped = false;
+ timeout = Timer.getTimer().newTimeout(this, 0, MILLISECONDS);
+ } else {
+ log.warn("LinkDiscovery started multiple times?");
+ }
+ }
+
+ /**
+ * Creates packet_out LLDP for specified output port.
+ *
+ * @param port the port
+ * @return Packet_out message with LLDP data
+ */
+ private OutboundPacket createOutBoundLLDP(Long port) {
+ if (port == null) {
+ return null;
+ }
+ lldpPacket.setPortId(port.intValue());
+ ethPacket.setSourceMACAddress(SRC_MAC);
+ return new DefaultOutboundPacket(device.id(),
+ builder().setOutput(portNumber(port)).build(),
+ ByteBuffer.wrap(ethPacket.serialize()));
+ }
+
+ /**
+ * Creates packet_out BDDP for specified output port.
+ *
+ * @param port the port
+ * @return Packet_out message with LLDP data
+ */
+ private OutboundPacket createOutBoundBDDP(Long port) {
+ if (port == null) {
+ return null;
+ }
+ lldpPacket.setPortId(port.intValue());
+ bddpEth.setSourceMACAddress(SRC_MAC);
+ return new DefaultOutboundPacket(device.id(),
+ builder().setOutput(portNumber(port)).build(),
+ ByteBuffer.wrap(bddpEth.serialize()));
+ }
+
+ private void sendProbes(Long portNumber) {
+ log.trace("Sending probes out to {}@{}", portNumber, device.id());
+ OutboundPacket pkt = createOutBoundLLDP(portNumber);
+ pktService.emit(pkt);
+ if (useBDDP) {
+ OutboundPacket bpkt = createOutBoundBDDP(portNumber);
+ pktService.emit(bpkt);
+ }
+ }
+
+ public boolean containsPort(Long portNumber) {
+ return slowPorts.contains(portNumber) || fastPorts.contains(portNumber);
+ }
+
+ public synchronized boolean isStopped() {
+ return isStopped || timeout.isCancelled();
+ }
+
+}
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
new file mode 100644
index 00000000..27c75ebd
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRules.java
@@ -0,0 +1,106 @@
+/*
+ * 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 java.util.Map;
+import java.util.Map.Entry;
+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;
+
+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) {
+
+ 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;
+ }
+ final Annotations annotations = device.annotations();
+ if (containsSuppressionAnnotation(annotations)) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isSuppressed(Port port) {
+ Element parent = port.element();
+ if (parent instanceof Device) {
+ if (isSuppressed((Device) parent)) {
+ return true;
+ }
+ }
+
+ final Annotations annotations = port.annotations();
+ if (containsSuppressionAnnotation(annotations)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean containsSuppressionAnnotation(final Annotations annotations) {
+ for (Entry<String, String> entry : suppressedAnnotation.entrySet()) {
+ final String suppValue = entry.getValue();
+ final String suppKey = entry.getKey();
+ if (suppValue == ANY_VALUE) {
+ if (annotations.keys().contains(suppKey)) {
+ return true;
+ }
+ } else {
+ if (suppValue.equals(annotations.value(suppKey))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ Set<DeviceId> getSuppressedDevice() {
+ return suppressedDevice;
+ }
+
+ Set<Device.Type> getSuppressedDeviceType() {
+ return suppressedDeviceType;
+ }
+
+ Map<String, String> getSuppressedAnnotation() {
+ return suppressedAnnotation;
+ }
+}
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
new file mode 100644
index 00000000..360bebd2
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/SuppressionRulesStore.java
@@ -0,0 +1,174 @@
+/*
+ * 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/main/java/org/onosproject/provider/lldp/impl/package-info.java b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/package-info.java
new file mode 100644
index 00000000..768a6cd2
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/main/java/org/onosproject/provider/lldp/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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 the core as a means of infrastructure link inference.
+ */
+package org.onosproject.provider.lldp.impl;
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..1d63a15d
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/LLDPLinkProviderTest.java
@@ -0,0 +1,502 @@
+/*
+ * 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.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.ConnectPoint;
+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.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.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.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+public class LLDPLinkProviderTest {
+
+ private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001");
+ private static final DeviceId DID2 = DeviceId.deviceId("of:0000000000000002");
+
+ 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 linkService = new TestLinkRegistry();
+ private final TestPacketService packetService = new TestPacketService();
+ private final TestDeviceService deviceService = new TestDeviceService();
+ private final TestMasterShipService masterService = new TestMasterShipService();
+
+ private CoreService coreService;
+ private TestLinkProviderService providerService;
+
+ private PacketProcessor testProcessor;
+ private DeviceListener deviceListener;
+
+ private ApplicationId appId =
+ new DefaultApplicationId(100, "org.onosproject.provider.lldp");
+
+ @Before
+ public void setUp() {
+ coreService = createMock(CoreService.class);
+ expect(coreService.registerApplication(appId.name()))
+ .andReturn(appId).anyTimes();
+ replay(coreService);
+
+ provider.cfgService = new ComponentConfigAdapter();
+ provider.coreService = coreService;
+
+ provider.deviceService = deviceService;
+ provider.packetService = packetService;
+ provider.providerRegistry = linkService;
+ 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));
+
+ assertTrue("Discoverer is not gone", provider.discoverers.get(DID1).isStopped());
+ assertTrue("Device is not gone.", vanishedDpid(DID1));
+ }
+
+ @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 portUnknown() {
+ deviceListener.event(deviceEvent(DeviceEvent.Type.DEVICE_ADDED, DID1));
+ deviceListener.event(portEvent(DeviceEvent.Type.PORT_ADDED, DID2, port(DID2, 1, false)));
+
+
+ assertNull("DeviceId exists",
+ provider.discoverers.get(DID2));
+ }
+
+ @Test
+ public void unknownPktCtx() {
+
+ PacketContext pktCtx = new TestPacketContext(deviceService.getDevice(DID2));
+
+ 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));
+
+ }
+
+ @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 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;
+ }
+
+
+ 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 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));
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return devices.values().size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return Collections.emptyList();
+ }
+
+ @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());
+ }
+ }
+
+
+}
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
new file mode 100644
index 00000000..0ac31123
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesStoreTest.java
@@ -0,0 +1,80 @@
+/*
+ * 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
new file mode 100644
index 00000000..52f0bb1e
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/java/org/onosproject/provider/lldp/impl/SuppressionRulesTest.java
@@ -0,0 +1,147 @@
+package org.onosproject.provider.lldp.impl;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.DeviceId.deviceId;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.Annotations;
+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.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+public class SuppressionRulesTest {
+
+ private static final DeviceId NON_SUPPRESSED_DID = deviceId("of:1111000000000000");
+ private static final DeviceId SUPPRESSED_DID = deviceId("of:2222000000000000");
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final String MFR = "whitebox";
+ private static final String HW = "1.1.x";
+ private static final String SW1 = "3.8.1";
+ private static final String SN = "43311-12345";
+ private static final ChassisId CID = new ChassisId();
+
+ private static final PortNumber P1 = PortNumber.portNumber(1);
+
+ private SuppressionRules rules;
+
+ @Before
+ public void setUp() throws Exception {
+ rules = new SuppressionRules(ImmutableSet.of(SUPPRESSED_DID),
+ 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,
+ Device.Type.ROADM,
+ MFR, HW, SW1, SN, CID);
+ assertTrue(rules.isSuppressed(device));
+ }
+
+ @Test
+ public void testSuppressedDeviceAnnotation() {
+ Annotations annotation = DefaultAnnotations.builder()
+ .set("no-lldp", "random")
+ .build();
+
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID, annotation);
+ assertTrue(rules.isSuppressed(device));
+ }
+
+ @Test
+ public void testSuppressedDeviceAnnotationExact() {
+ Annotations annotation = DefaultAnnotations.builder()
+ .set("sendLLDP", "false")
+ .build();
+
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID, annotation);
+ assertTrue(rules.isSuppressed(device));
+ }
+
+ @Test
+ public void testNotSuppressedDevice() {
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID);
+ assertFalse(rules.isSuppressed(device));
+ }
+
+ @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")
+ .build();
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID);
+ Port port = new DefaultPort(device, P1, true, annotation);
+
+ assertTrue(rules.isSuppressed(port));
+ }
+
+ @Test
+ public void testSuppressedPortAnnotationExact() {
+ Annotations annotation = DefaultAnnotations.builder()
+ .set("sendLLDP", "false")
+ .build();
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID);
+ Port port = new DefaultPort(device, P1, true, annotation);
+
+ assertTrue(rules.isSuppressed(port));
+ }
+
+ @Test
+ public void testNotSuppressedPort() {
+ Device device = new DefaultDevice(PID,
+ NON_SUPPRESSED_DID,
+ Device.Type.SWITCH,
+ MFR, HW, SW1, SN, CID);
+ Port port = new DefaultPort(device, P1, true);
+
+ assertFalse(rules.isSuppressed(port));
+ }
+}
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
new file mode 100644
index 00000000..062e73aa
--- /dev/null
+++ b/framework/src/onos/providers/lldp/src/test/resources/lldp_suppression.json
@@ -0,0 +1,6 @@
+{
+ "deviceId" : [ "of:2222000000000000" ],
+ "deviceType" : [ "ROADM" ],
+ "annotation" : { "no-lldp" : null, "sendLLDP" : "false" }
+}
+
diff --git a/framework/src/onos/providers/netconf/app/app.xml b/framework/src/onos/providers/netconf/app/app.xml
new file mode 100644
index 00000000..f2d47627
--- /dev/null
+++ b/framework/src/onos/providers/netconf/app/app.xml
@@ -0,0 +1,24 @@
+<?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.netconf" 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-netconf-provider-device/${project.version}</artifact>
+ <!-- Question: should there be the jnc stuff here? Or is it just for testing -->
+</app>
diff --git a/framework/src/onos/providers/netconf/app/features.xml b/framework/src/onos/providers/netconf/app/features.xml
new file mode 100644
index 00000000..6a3d1a2c
--- /dev/null
+++ b/framework/src/onos/providers/netconf/app/features.xml
@@ -0,0 +1,27 @@
+<?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}">
+ <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>
+ <bundle>mvn:io.netty/netty/3.9.2.Final</bundle>
+ <bundle>mvn:${project.groupId}/onos-netconf-provider-device/${project.version}</bundle>
+ <!-- Question: should there be the jnc stuff here? Or is it just for testing -->
+ </feature>
+</features>
+
diff --git a/framework/src/onos/providers/netconf/app/pom.xml b/framework/src/onos/providers/netconf/app/pom.xml
new file mode 100644
index 00000000..8cf56b3f
--- /dev/null
+++ b/framework/src/onos/providers/netconf/app/pom.xml
@@ -0,0 +1,48 @@
+<?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.
+ -->
+<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-netconf-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-netconf</artifactId>
+ <packaging>pom</packaging>
+
+ <description>NetConf protocol southbound providers</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-netconf-provider-device</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-netconf-provider-flow</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <!-- TODO: add other dependencies here as more bundles are added to the app -->
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/netconf/device/pom.xml b/framework/src/onos/providers/netconf/device/pom.xml
new file mode 100644
index 00000000..be05e3cd
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/pom.xml
@@ -0,0 +1,165 @@
+<?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.
+ -->
+<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-netconf-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-netconf-provider-device</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS Netconf protocol device provider</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.ethz.ganymed</groupId>
+ <artifactId>ganymed-ssh2</artifactId>
+ <version>262</version>
+ </dependency>
+ <dependency>
+ <!-- TODO: change this appropriately when the official TailF JNC is available -->
+ <groupId>org.onosproject</groupId>
+ <artifactId>jnc</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jdom</groupId>
+ <artifactId>jdom2</artifactId>
+ <version>2.0.5</version>
+ </dependency>
+ <dependency>
+ <groupId>jaxen</groupId>
+ <artifactId>jaxen</artifactId>
+ <version>1.1.4</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <filters>
+ <filter>
+ <artifact>com.tailf:JNC</artifact>
+ <includes>
+ <include>com/tailf/jnc/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>ch.ethz.ganymed:ganymed-ssh2</artifact>
+ <includes>
+ <include>ch/ethz/ssh2/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>org.jdom:jdom2</artifact>
+ <includes>
+ <include>org/jdom2/**</include>
+ </includes>
+ </filter>
+ </filters>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ com.tailf.jnc,
+ ch.ethz.ssh2,
+ ch.ethz.ssh2.auth,
+ ch.ethz.ssh2.channel,
+ ch.ethz.ssh2.crypto,
+ ch.ethz.ssh2.crypto.cipher,
+ ch.ethz.ssh2.crypto.dh,
+ ch.ethz.ssh2.crypto.digest,
+ ch.ethz.ssh2.log,
+ ch.ethz.ssh2.packets,
+ ch.ethz.ssh2.server,
+ ch.ethz.ssh2.sftp,
+ ch.ethz.ssh2.signature,
+ ch.ethz.ssh2.transport,
+ ch.ethz.ssh2.util,
+ org.jdom2,
+ org.jdom2.input,
+ org.jdom2.output,
+ org.jdom2.adapters,
+ org.jdom2.filter,
+ org.jdom2.internal,
+ org.jdom2.located,
+ org.jdom2.transform,
+ org.jdom2.util,
+ org.jdom2.xpath,
+ org.jdom2.input.sax,
+ org.jdom2.input.stax,
+ org.jdom2.output.support,
+ org.jdom2.xpath.jaxen,
+ org.jdom2.xpath.util
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java
new file mode 100644
index 00000000..b3d26b06
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDevice.java
@@ -0,0 +1,304 @@
+/*
+ * 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 static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.delay;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.input.SAXBuilder;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.slf4j.Logger;
+
+import com.tailf.jnc.Capabilities;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.SSHConnection;
+import com.tailf.jnc.SSHSession;
+
+/**
+ * This is a logical representation of actual NETCONF device, carrying all the
+ * necessary information to connect and execute NETCONF operations.
+ */
+public class NetconfDevice {
+ private final Logger log = getLogger(NetconfDevice.class);
+
+ /**
+ * The Device State is used to determine whether the device is active or
+ * inactive. This state infomation will help Device Creator to add or delete
+ * the device from the core.
+ */
+ public static enum DeviceState {
+ /* Used to specify Active state of the device */
+ ACTIVE,
+ /* Used to specify inactive state of the device */
+ INACTIVE,
+ /* Used to specify invalid state of the device */
+ INVALID
+ }
+
+ private static final int DEFAULT_SSH_PORT = 22;
+ private static final int DEFAULT_CON_TIMEOUT = 0;
+ private static final String XML_CAPABILITY_KEY = "capability";
+ private static final int EVENTINTERVAL = 2000;
+ private static final int CONNECTION_CHECK_INTERVAL = 3;
+ private static final String INPUT_HELLO_XML_MSG = new StringBuilder(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+ .append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">")
+ .append("<capabilities><capability>urn:ietf:params:netconf:base:1.0</capability>")
+ .append("</capabilities></hello>").toString();
+
+ private String sshHost;
+ private int sshPort = DEFAULT_SSH_PORT;
+ private int connectTimeout = DEFAULT_CON_TIMEOUT;
+ private String username;
+ private String password;
+ private boolean reachable = false;
+
+ private List<String> capabilities = new ArrayList<String>();
+ private SSHConnection sshConnection = null;
+
+ private DeviceState deviceState = DeviceState.INVALID;
+
+ protected NetconfDevice(String sshHost, int sshPort, String username,
+ String password) {
+ this.username = checkNotNull(username,
+ "Netconf Username Cannot be null");
+ this.sshHost = checkNotNull(sshHost, "Netconf Device IP cannot be null");
+ this.sshPort = checkNotNull(sshPort,
+ "Netconf Device SSH port cannot be null");
+ this.password = password;
+ }
+
+ /**
+ * This will try to connect to NETCONF device and find all the capabilities.
+ *
+ * @throws Exception if unable to connect to the device
+ */
+ // FIXME: this should not be a generic Exception; perhaps wrap in some RuntimeException
+ public void init() throws Exception {
+ try {
+ if (sshConnection == null) {
+ sshConnection = new SSHConnection(sshHost, sshPort, connectTimeout);
+ sshConnection.authenticateWithPassword(username, password);
+ }
+ // Send hello message to retrieve capabilities.
+ } catch (IOException e) {
+ log.error("Fatal Error while creating connection to the device: "
+ + deviceInfo(), e);
+ throw e;
+ } catch (JNCException e) {
+ log.error("Failed to connect to the device: " + deviceInfo(), e);
+ throw e;
+ }
+
+ hello();
+ }
+
+ private void hello() {
+ SSHSession ssh = null;
+ try {
+ ssh = new SSHSession(sshConnection);
+ String helloRequestXML = INPUT_HELLO_XML_MSG.trim();
+
+ log.debug("++++++++++++++++++++++++++++++++++Sending Hello: "
+ + sshConnection.getGanymedConnection().getHostname()
+ + "++++++++++++++++++++++++++++++++++");
+ printPrettyXML(helloRequestXML);
+ ssh.print(helloRequestXML);
+ // ssh.print(endCharSeq);
+ ssh.flush();
+ String xmlResponse = null;
+ int i = CONNECTION_CHECK_INTERVAL;
+ while (!ssh.ready() && i > 0) {
+ delay(EVENTINTERVAL);
+ i--;
+ }
+
+ if (ssh.ready()) {
+ StringBuffer readOne = ssh.readOne();
+ if (readOne == null) {
+ log.error("The Hello Contains No Capabilites");
+ throw new JNCException(
+ JNCException.SESSION_ERROR,
+ "server does not support NETCONF base capability: "
+ + Capabilities.NETCONF_BASE_CAPABILITY);
+ } else {
+ xmlResponse = readOne.toString().trim();
+
+ log.debug("++++++++++++++++++++++++++++++++++Reading Capabilities: "
+ + sshConnection.getGanymedConnection()
+ .getHostname()
+ + "++++++++++++++++++++++++++++++++++");
+
+ printPrettyXML(xmlResponse);
+ processCapabilities(xmlResponse);
+ }
+ }
+ reachable = true;
+ } catch (IOException e) {
+ log.error("Fatal Error while sending Hello Message to the device: "
+ + deviceInfo(), e);
+ } catch (JNCException e) {
+ log.error("Fatal Error while sending Hello Message to the device: "
+ + deviceInfo(), e);
+ } finally {
+ log.debug("Closing the session after successful execution");
+ if (ssh != null) {
+ ssh.close();
+ }
+ }
+ }
+
+ private void processCapabilities(String xmlResponse) throws JNCException {
+ if (xmlResponse.isEmpty()) {
+ log.error("The capability response cannot be empty");
+ throw new JNCException(
+ JNCException.SESSION_ERROR,
+ "server does not support NETCONF base capability: "
+ + Capabilities.NETCONF_BASE_CAPABILITY);
+ }
+ try {
+ Document doc = new SAXBuilder()
+ .build(new StringReader(xmlResponse));
+ Element rootElement = doc.getRootElement();
+ processCapabilities(rootElement);
+ } catch (Exception e) {
+ log.error("ERROR while parsing the XML " + xmlResponse);
+ }
+ }
+
+ private void processCapabilities(Element rootElement) {
+ List<Element> children = rootElement.getChildren();
+ if (children.isEmpty()) {
+ return;
+ }
+ for (Element child : children) {
+
+ if (child.getName().equals(XML_CAPABILITY_KEY)) {
+ capabilities.add(child.getValue());
+ }
+ if (!child.getChildren().isEmpty()) {
+ processCapabilities(child);
+ }
+ }
+ }
+
+ private void printPrettyXML(String xmlstring) {
+ try {
+ Document doc = new SAXBuilder().build(new StringReader(xmlstring));
+ XMLOutputter xmOut = new XMLOutputter(Format.getPrettyFormat());
+ String outputString = xmOut.outputString(doc);
+ log.debug(outputString);
+ } catch (Exception e) {
+ log.error("ERROR while parsing the XML " + xmlstring, e);
+
+ }
+ }
+
+ /**
+ * This would return host IP and host Port, used by this particular Netconf
+ * Device.
+ * @return Device Information.
+ */
+ public String deviceInfo() {
+ return new StringBuilder("host: ").append(sshHost).append(". port: ")
+ .append(sshPort).toString();
+ }
+
+ /**
+ * This will terminate the device connection.
+ */
+ public void disconnect() {
+ sshConnection.close();
+ reachable = false;
+ }
+
+ /**
+ * This will list down all the capabilities supported on the device.
+ * @return Capability list.
+ */
+ public List<String> getCapabilities() {
+ return capabilities;
+ }
+
+ /**
+ * This api is intended to know whether the device is connected or not.
+ * @return true if connected
+ */
+ public boolean isReachable() {
+ return reachable;
+ }
+
+ /**
+ * This will return the IP used connect ssh on the device.
+ * @return Netconf Device IP
+ */
+ public String getSshHost() {
+ return sshHost;
+ }
+
+ /**
+ * This will return the SSH Port used connect the device.
+ * @return SSH Port number
+ */
+ public int getSshPort() {
+ return sshPort;
+ }
+
+ /**
+ * The usename used to connect Netconf Device.
+ * @return Device Username
+ */
+ public String getUsername() {
+ return username;
+ }
+
+ /**
+ * Retrieve current state of the device.
+ * @return Current Device State
+ */
+ public DeviceState getDeviceState() {
+ return deviceState;
+ }
+
+ /**
+ * This is set the state information for the device.
+ * @param deviceState Next Device State
+ */
+ public void setDeviceState(DeviceState deviceState) {
+ this.deviceState = deviceState;
+ }
+
+ /**
+ * Check whether the device is in Active state.
+ * @return true if the device is Active
+ */
+ public boolean isActive() {
+ return deviceState == DeviceState.ACTIVE ? true : false;
+ }
+
+ public void setConnectTimeout(int connectTimeout) {
+ this.connectTimeout = connectTimeout;
+ }
+}
diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java
new file mode 100644
index 00000000..f9194a7e
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProvider.java
@@ -0,0 +1,358 @@
+/*
+ * 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 static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Dictionary;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+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.ChassisId;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+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.device.DeviceService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.provider.netconf.device.impl.NetconfDevice.DeviceState;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+/**
+ * Provider which will try to fetch the details of NETCONF devices from the core
+ * and run a capability discovery on each of the device.
+ */
+@Component(immediate = true)
+public class NetconfDeviceProvider extends AbstractProvider
+ implements DeviceProvider {
+
+ private final Logger log = getLogger(NetconfDeviceProvider.class);
+
+ protected Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<DeviceId, NetconfDevice>();
+
+ private DeviceProviderService providerService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private ExecutorService deviceBuilder = Executors
+ .newFixedThreadPool(1, groupedThreads("onos/netconf", "device-creator"));
+
+ // Delay between events in ms.
+ private static final int EVENTINTERVAL = 5;
+
+ private static final String SCHEME = "netconf";
+
+ @Property(name = "devConfigs", value = "", label = "Instance-specific configurations")
+ private String devConfigs = null;
+
+ @Property(name = "devPasswords", value = "", label = "Instance-specific password")
+ private String devPasswords = null;
+
+ /**
+ * Creates a provider with the supplier identifier.
+ */
+ public NetconfDeviceProvider() {
+ super(new ProviderId("netconf", "org.onosproject.provider.netconf"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+ providerService = providerRegistry.register(this);
+ modified(context);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ try {
+ for (Entry<DeviceId, NetconfDevice> deviceEntry : netconfDeviceMap
+ .entrySet()) {
+ deviceBuilder.submit(new DeviceCreator(deviceEntry.getValue(),
+ false));
+ }
+ deviceBuilder.awaitTermination(1000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ log.error("Device builder did not terminate");
+ }
+ deviceBuilder.shutdownNow();
+ netconfDeviceMap.clear();
+ providerRegistry.unregister(this);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ if (context == null) {
+ log.info("No configuration file");
+ return;
+ }
+ Dictionary<?, ?> properties = context.getProperties();
+ String deviceCfgValue = get(properties, "devConfigs");
+ log.info("Settings: devConfigs={}", deviceCfgValue);
+ if (!isNullOrEmpty(deviceCfgValue)) {
+ addOrRemoveDevicesConfig(deviceCfgValue);
+ }
+ }
+
+ private void addOrRemoveDevicesConfig(String deviceConfig) {
+ for (String deviceEntry : deviceConfig.split(",")) {
+ NetconfDevice device = processDeviceEntry(deviceEntry);
+ if (device != null) {
+ log.info("Device Detail: username: {}, host={}, port={}, state={}",
+ device.getUsername(), device.getSshHost(),
+ device.getSshPort(), device.getDeviceState().name());
+ if (device.isActive()) {
+ deviceBuilder.submit(new DeviceCreator(device, true));
+ } else {
+ deviceBuilder.submit(new DeviceCreator(device, false));
+ }
+ }
+ }
+ }
+
+ private NetconfDevice processDeviceEntry(String deviceEntry) {
+ if (deviceEntry == null) {
+ log.info("No content for Device Entry, so cannot proceed further.");
+ return null;
+ }
+ log.info("Trying to convert Device Entry String: " + deviceEntry
+ + " to a Netconf Device Object");
+ NetconfDevice device = null;
+ try {
+ String userInfo = deviceEntry.substring(0, deviceEntry
+ .lastIndexOf('@'));
+ String hostInfo = deviceEntry.substring(deviceEntry
+ .lastIndexOf('@') + 1);
+ String[] infoSplit = userInfo.split(":");
+ String username = infoSplit[0];
+ String password = infoSplit[1];
+ infoSplit = hostInfo.split(":");
+ String hostIp = infoSplit[0];
+ Integer hostPort;
+ try {
+ hostPort = Integer.parseInt(infoSplit[1]);
+ } catch (NumberFormatException nfe) {
+ log.error("Bad Configuration Data: Failed to parse host port number string: "
+ + infoSplit[1]);
+ throw nfe;
+ }
+ String deviceState = infoSplit[2];
+ if (isNullOrEmpty(username) || isNullOrEmpty(password)
+ || isNullOrEmpty(hostIp) || hostPort == 0) {
+ log.warn("Bad Configuration Data: both user and device information parts of Configuration "
+ + deviceEntry + " should be non-nullable");
+ } else {
+ device = new NetconfDevice(hostIp, hostPort, username, password);
+ if (!isNullOrEmpty(deviceState)) {
+ if (deviceState.toUpperCase().equals(DeviceState.ACTIVE
+ .name())) {
+ device.setDeviceState(DeviceState.ACTIVE);
+ } else if (deviceState.toUpperCase()
+ .equals(DeviceState.INACTIVE.name())) {
+ device.setDeviceState(DeviceState.INACTIVE);
+ } else {
+ log.warn("Device State Information can not be empty, so marking the state as INVALID");
+ device.setDeviceState(DeviceState.INVALID);
+ }
+ } else {
+ log.warn("The device entry do not specify state information, so marking the state as INVALID");
+ device.setDeviceState(DeviceState.INVALID);
+ }
+ }
+ } catch (ArrayIndexOutOfBoundsException aie) {
+ log.error("Error while reading config infromation from the config file: "
+ + "The user, host and device state infomation should be "
+ + "in the order 'userInfo@hostInfo:deviceState'"
+ + deviceEntry, aie);
+ } catch (Exception e) {
+ log.error("Error while parsing config information for the device entry: "
+ + deviceEntry, e);
+ }
+ return device;
+ }
+
+ @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) {
+ NetconfDevice netconfDevice = netconfDeviceMap.get(deviceId);
+ if (netconfDevice == null) {
+ log.warn("BAD REQUEST: the requested device id: "
+ + deviceId.toString()
+ + " is not associated to any NETCONF Device");
+ return false;
+ }
+ return netconfDevice.isReachable();
+ }
+
+ /**
+ * This class is intended to add or remove Configured Netconf Devices.
+ * Functionality relies on 'createFlag' and 'NetconfDevice' content. The
+ * functionality runs as a thread and dependening on the 'createFlag' value
+ * it will create or remove Device entry from the core.
+ */
+ private class DeviceCreator implements Runnable {
+
+ private NetconfDevice device;
+ private boolean createFlag;
+
+ public DeviceCreator(NetconfDevice device, boolean createFlag) {
+ this.device = device;
+ this.createFlag = createFlag;
+ }
+
+ @Override
+ public void run() {
+ if (createFlag) {
+ log.info("Trying to create Device Info on ONOS core");
+ advertiseDevices();
+ } else {
+ log.info("Trying to remove Device Info on ONOS core");
+ removeDevices();
+ }
+ }
+
+ /**
+ * For each Netconf Device, remove the entry from the device store.
+ */
+ private void removeDevices() {
+ if (device == null) {
+ log.warn("The Request Netconf Device is null, cannot proceed further");
+ return;
+ }
+ try {
+ DeviceId did = getDeviceId();
+ if (!netconfDeviceMap.containsKey(did)) {
+ log.error("BAD Request: 'Currently device is not discovered, "
+ + "so cannot remove/disconnect the device: "
+ + device.deviceInfo() + "'");
+ return;
+ }
+ providerService.deviceDisconnected(did);
+ device.disconnect();
+ netconfDeviceMap.remove(did);
+ delay(EVENTINTERVAL);
+ } catch (URISyntaxException uriSyntaxExcpetion) {
+ log.error("Syntax Error while creating URI for the device: "
+ + device.deviceInfo()
+ + " couldn't remove the device from the store",
+ uriSyntaxExcpetion);
+ }
+ }
+
+ /**
+ * Initialize Netconf Device object, and notify core saying device
+ * connected.
+ */
+ private void advertiseDevices() {
+ try {
+ if (device == null) {
+ log.warn("The Request Netconf Device is null, cannot proceed further");
+ return;
+ }
+ device.init();
+ DeviceId did = getDeviceId();
+ ChassisId cid = new ChassisId();
+ DeviceDescription desc = new DefaultDeviceDescription(
+ did.uri(),
+ Device.Type.OTHER,
+ "", "",
+ "", "",
+ cid);
+ log.info("Persisting Device" + did.uri().toString());
+
+ netconfDeviceMap.put(did, device);
+ providerService.deviceConnected(did, desc);
+ log.info("Done with Device Info Creation on ONOS core. Device Info: "
+ + device.deviceInfo() + " " + did.uri().toString());
+ delay(EVENTINTERVAL);
+ } catch (URISyntaxException e) {
+ log.error("Syntax Error while creating URI for the device: "
+ + device.deviceInfo()
+ + " couldn't persist the device onto the store", e);
+ } catch (SocketTimeoutException e) {
+ log.error("Error while setting connection for the device: "
+ + device.deviceInfo(), e);
+ } catch (IOException e) {
+ log.error("Error while setting connection for the device: "
+ + device.deviceInfo(), e);
+ } catch (Exception e) {
+ log.error("Error while initializing session for the device: "
+ + (device != null ? device.deviceInfo() : null), e);
+ }
+ }
+
+ /**
+ * This will build a device id for the device.
+ */
+ private DeviceId getDeviceId() throws URISyntaxException {
+ String additionalSSP = new StringBuilder(device.getUsername())
+ .append("@").append(device.getSshHost()).append(":")
+ .append(device.getSshPort()).toString();
+ DeviceId did = DeviceId.deviceId(new URI(SCHEME, additionalSSP,
+ null));
+ return did;
+ }
+ }
+}
diff --git a/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java
new file mode 100644
index 00000000..583aeaa2
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/src/main/java/org/onosproject/provider/netconf/device/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 Netconf capability request as a means of infrastructure device discovery.
+ */
+package org.onosproject.provider.netconf.device.impl; \ No newline at end of file
diff --git a/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java
new file mode 100644
index 00000000..e56c5959
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTest.java
@@ -0,0 +1,421 @@
+/*
+ * 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 static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.junit.Assert.assertFalse;
+import static org.onlab.util.Tools.delay;
+import static org.onosproject.provider.netconf.device.impl.NetconfDeviceProviderTestConstant.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.ChassisId;
+import org.onosproject.cfg.ComponentConfigService;
+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.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import com.tailf.jnc.JNCException;
+
+/**
+ * Test Case to Validate Netconf Device Provider.
+ */
+public class NetconfDeviceProviderTest {
+ TestDeviceCreator create;
+
+ private final Logger log = getLogger(NetconfDeviceProviderTest.class);
+
+ private Map<DeviceId, NetconfDevice> netconfDeviceMap = new ConcurrentHashMap<DeviceId, NetconfDevice>();
+
+ private DeviceProviderService providerService;
+
+ private static final DeviceId DID1 = DeviceId.deviceId(DEVICE_ID);
+
+ private final NetconfDeviceProvider provider = new NetconfDeviceProvider();
+ private final TestDeviceRegistry registry = new TestDeviceRegistry();
+
+ private ComponentConfigService mockCfgService;
+
+ @Before
+ public void setUp() {
+ mockCfgService = EasyMock.createMock(ComponentConfigService.class);
+ provider.cfgService = mockCfgService;
+ provider.providerRegistry = registry;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockWithoutValues(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG)).andReturn(NULL);
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockWithDeviceEntryNull(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG)).andReturn(NULL_NULL);
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockDeviceEntryNumberFomatEx(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG))
+ .andReturn(CONFIG_WITH_INVALID_ENTRY_NUMBER)
+ .andThrow(new NumberFormatException());
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockWithoutUsernameAndPassword(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG)).andReturn(CONFIG_WITH_NULL_ENTRY);
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockWithDifferentDeviceState(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG))
+ .andReturn(CONFIG_WITH_DIFFERENT_DEVICE_STATE);
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockDeviceWithArrayOutOFBoundEx(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG))
+ .andReturn(CONFIG_WITH_ARRAY_OUT_OF_BOUNDEX)
+ .andThrow(new ArrayIndexOutOfBoundsException());
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Dictionary<String, String> getDictionaryMockDeviceEntryForDeactivate(ComponentContext componentContext) {
+ Dictionary<String, String> dictionary = EasyMock
+ .createMock(Dictionary.class);
+ expect(dictionary.get(DEV_CONFIG))
+ .andReturn(CONFIG_ENTRY_FOR_DEACTIVATE)
+ .andThrow(new ArrayIndexOutOfBoundsException());
+ replay(dictionary);
+ expect(componentContext.getProperties()).andReturn(dictionary);
+ return dictionary;
+ }
+
+ @Ignore
+ @Test(expected = IOException.class)
+ public void testSSHAuthentication() throws IOException, JNCException {
+ TestDeviceCreator objForTestDev = new TestDeviceCreator(
+ new NetconfDevice(
+ DEVICE_IP,
+ DEVICE_PORT,
+ DEVICE_USERNAME,
+ DEVICE_PASSWORD),
+ true);
+ objForTestDev.run();
+ }
+
+ @After
+ public void tearDown() {
+ provider.providerRegistry = null;
+ provider.cfgService = null;
+ }
+
+ // To check if deviceCfgValue is empty or null
+ @Test
+ public void testActiveWithcomponentContextIsNull() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockWithoutValues(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ // To check deviceEntry and device is null
+ @Test
+ public void testActiveWithDeviceEntryIsNull() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockWithDeviceEntryNull(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ @Test
+ public void testActiveWithDeviceEntryWithoutUsernameAndPassword() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockWithoutUsernameAndPassword(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ @Test
+ public void testActiveWithDeviceEntryWithNumberFomatEx() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockDeviceEntryNumberFomatEx(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ @Test
+ public void testActiveWithDeviceEntryWithDifferentDeviceState() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockWithDifferentDeviceState(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ @Test
+ public void testActiveWithDeviceEntryWithArrayOutOFBoundEx() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockDeviceWithArrayOutOFBoundEx(componentContext);
+ replay(componentContext);
+ provider.activate(componentContext);
+ }
+
+ @Test
+ public void isReachableWithInvalidDeviceId() {
+ assertFalse("Initially the Device ID Should not be reachable",
+ provider.isReachable(DID1));
+ NetconfDevice device = new NetconfDevice(NULL, ZERO, NULL, NULL);
+ provider.netconfDeviceMap.put(DID1, device);
+ assertFalse("Particular Device ID cannot be Reachable",
+ provider.isReachable(DID1));
+ }
+
+ @Test
+ public void testDeactivate() {
+
+ ComponentContext componentContext = EasyMock
+ .createMock(ComponentContext.class);
+ getDictionaryMockDeviceEntryForDeactivate(componentContext);
+ replay(componentContext);
+ testActiveWithDeviceEntryWithDifferentDeviceState();
+ provider.deactivate(componentContext);
+ }
+
+ private class TestDeviceCreator {
+
+ private NetconfDevice device;
+ private boolean createFlag;
+
+ public TestDeviceCreator(NetconfDevice device, boolean createFlag) {
+ this.device = device;
+ this.createFlag = createFlag;
+ }
+
+ public void run() throws JNCException, IOException {
+ if (createFlag) {
+ log.info("Trying to create Device Info on ONOS core");
+ advertiseDevices();
+ } else {
+ log.info("Trying to remove Device Info on ONOS core");
+ removeDevices();
+ }
+ }
+
+ /**
+ * For each Netconf Device, remove the entry from the device store.
+ */
+ private void removeDevices() {
+ if (device == null) {
+ log.warn("The Request Netconf Device is null, cannot proceed further");
+ return;
+ }
+ try {
+ DeviceId did = getDeviceId();
+ if (!netconfDeviceMap.containsKey(did)) {
+ log.error("BAD Request: 'Currently device is not discovered, "
+ + "so cannot remove/disconnect the device: "
+ + device.deviceInfo() + "'");
+ return;
+ }
+ providerService.deviceDisconnected(did);
+ device.disconnect();
+ netconfDeviceMap.remove(did);
+ delay(EVENTINTERVAL);
+ } catch (URISyntaxException uriSyntaxExcpetion) {
+ log.error("Syntax Error while creating URI for the device: "
+ + device.deviceInfo()
+ + " couldn't remove the device from the store",
+ uriSyntaxExcpetion);
+ }
+ }
+
+ /**
+ * Initialize Netconf Device object, and notify core saying device
+ * connected.
+ */
+ private void advertiseDevices() throws JNCException, IOException {
+ try {
+ if (device == null) {
+ log.warn("The Request Netconf Device is null, cannot proceed further");
+ return;
+ }
+ device.init();
+ DeviceId did = getDeviceId();
+ ChassisId cid = new ChassisId();
+ DeviceDescription desc = new DefaultDeviceDescription(
+ did.uri(),
+ Device.Type.OTHER,
+ NULL,
+ NULL,
+ NULL,
+ NULL, cid);
+ log.info("Persisting Device" + did.uri().toString());
+
+ netconfDeviceMap.put(did, device);
+ providerService.deviceConnected(did, desc);
+ log.info("Done with Device Info Creation on ONOS core. Device Info: "
+ + device.deviceInfo() + " " + did.uri().toString());
+ delay(EVENTINTERVAL);
+ } catch (URISyntaxException e) {
+ log.error("Syntax Error while creating URI for the device: "
+ + device.deviceInfo()
+ + " couldn't persist the device onto the store", e);
+ } catch (JNCException e) {
+ throw e;
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("Error while initializing session for the device: "
+ + device.deviceInfo(), e);
+ }
+ }
+
+ private DeviceId getDeviceId() throws URISyntaxException {
+ String additionalSSP = new StringBuilder(device.getUsername())
+ .append(AT_THE_RATE).append(device.getSshHost())
+ .append(COLON).append(device.getSshPort()).toString();
+ DeviceId did = DeviceId.deviceId(new URI(SCHEME_NETCONF,
+ additionalSSP, null));
+ return did;
+ }
+ }
+
+ private class TestDeviceRegistry implements DeviceProviderRegistry {
+
+ @Override
+ public DeviceProviderService register(DeviceProvider 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) {
+ }
+
+ @Override
+ public void deviceDisconnected(DeviceId deviceId) {
+
+ }
+
+ @Override
+ public void updatePorts(DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+
+ }
+
+ @Override
+ public void portStatusChanged(DeviceId deviceId,
+ PortDescription portDescription) {
+
+ }
+
+ @Override
+ public void receivedRoleReply(DeviceId deviceId,
+ MastershipRole requested,
+ MastershipRole response) {
+
+ }
+
+ @Override
+ public void updatePortStatistics(DeviceId deviceId,
+ Collection<PortStatistics> portStatistics) {
+
+ }
+ }
+ }
+}
diff --git a/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java
new file mode 100644
index 00000000..1d848e26
--- /dev/null
+++ b/framework/src/onos/providers/netconf/device/src/test/java/org/onosproject/provider/netconf/device/impl/NetconfDeviceProviderTestConstant.java
@@ -0,0 +1,46 @@
+/*
+ * 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;
+
+public final class NetconfDeviceProviderTestConstant {
+
+ private NetconfDeviceProviderTestConstant() {
+ }
+
+ public static final int ZERO = 0;
+ public static final int EVENTINTERVAL = 5;
+ public static final String DEV_CONFIG = "devConfigs";
+ public static final String CONFIG_WITH_INVALID_ENTRY_NUMBER = "cisco:cisco"
+ + "@10.18.11.14:cisco:active";
+ public static final String CONFIG_WITH_NULL_ENTRY = "null:null@null:0:active";
+ public static final String CONFIG_WITH_DIFFERENT_DEVICE_STATE = "cisco:cisco@10.18.11.14:22:active,"
+ + "cisco:cisco@10.18.11.18:22:inactive,cisco:cisco@10.18.11.14:22:invalid,"
+ + "cisco:cisco@10.18.11.14:22:null";
+ public static final String CONFIG_WITH_ARRAY_OUT_OF_BOUNDEX = "@10.18.11.14:22:active";
+ public static final String CONFIG_ENTRY_FOR_DEACTIVATE = "netconf:cisco"
+ + "@10.18.11.14:22:active";
+ public static final String DEVICE_IP = "10.18.14.19";
+ public static final int DEVICE_PORT = 22;
+ public static final String DEVICE_USERNAME = "cisco";
+ public static final String DEVICE_PASSWORD = "cisco";
+ public static final String AT_THE_RATE = "@";
+ public static final String COLON = ":";
+ public static final String NULL = "";
+ public static final String NULL_NULL = "null,null";
+ public static final String SCHEME_NETCONF = "netconf";
+ public static final String DEVICE_ID = "of:0000000000000001";
+
+}
diff --git a/framework/src/onos/providers/netconf/flow/pom.xml b/framework/src/onos/providers/netconf/flow/pom.xml
new file mode 100644
index 00000000..8ee4c4e4
--- /dev/null
+++ b/framework/src/onos/providers/netconf/flow/pom.xml
@@ -0,0 +1,259 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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-netconf-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-netconf-provider-flow</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>ch.ethz.ganymed</groupId>
+ <artifactId>ganymed-ssh2</artifactId>
+ <version>262</version>
+ </dependency>
+ <dependency>
+ <!-- TODO: change this appropriately when the official TailF JNC is available -->
+ <groupId>org.onosproject</groupId>
+ <artifactId>jnc</artifactId>
+ <version>1.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.jdom</groupId>
+ <artifactId>jdom2</artifactId>
+ <version>2.0.5</version>
+ </dependency>
+ <dependency>
+ <groupId>jaxen</groupId>
+ <artifactId>jaxen</artifactId>
+ <version>1.1.4</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-yang-tool</artifactId>
+ <version>1.3</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <version>2.3</version>
+ <configuration>
+ <filters>
+ <filter>
+ <artifact>com.tailf:JNC</artifact>
+ <includes>
+ <include>com/tailf/jnc/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>ch.ethz.ganymed:ganymed-ssh2</artifact>
+ <includes>
+ <include>ch/ethz/ssh2/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>org.jdom:jdom2</artifact>
+ <includes>
+ <include>org/jdom2/**</include>
+ </includes>
+ </filter>
+ <filter>
+ <artifact>org.onosproject:onos-yang-tool</artifact>
+ <includes>
+ <include>org/opendaylight/yang/gen/**</include>
+ </includes>
+ </filter>
+ </filters>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Export-Package>
+ com.tailf.jnc,
+ ch.ethz.ssh2,
+ ch.ethz.ssh2.auth,
+ ch.ethz.ssh2.channel,
+ ch.ethz.ssh2.crypto,
+ ch.ethz.ssh2.crypto.cipher,
+ ch.ethz.ssh2.crypto.dh,
+ ch.ethz.ssh2.crypto.digest,
+ ch.ethz.ssh2.log,
+ ch.ethz.ssh2.packets,
+ ch.ethz.ssh2.server,
+ ch.ethz.ssh2.sftp,
+ ch.ethz.ssh2.signature,
+ ch.ethz.ssh2.transport,
+ ch.ethz.ssh2.util,
+ org.jdom2,
+ org.jdom2.input,
+ org.jdom2.output,
+ org.jdom2.adapters,
+ org.jdom2.filter,
+ org.jdom2.internal,
+ org.jdom2.located,
+ org.jdom2.transform,
+ org.jdom2.util,
+ org.jdom2.xpath,
+ org.jdom2.input.sax,
+ org.jdom2.input.stax,
+ org.jdom2.output.support,
+ org.jdom2.xpath.jaxen,
+ org.jdom2.xpath.util,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520,
+ org.opendaylight.yangtools.yang.data.impl.schema.tree,
+ org.opendaylight.yangtools.yang.data.impl.codec,
+ org.opendaylight.yangtools.yang.model.parser.api,
+ org.opendaylight.yangtools.yang.data.impl.schema.nodes,
+ org.opendaylight.yangtools.yang.binding.util,
+ org.opendaylight.yangtools.yang.data.impl,
+ org.opendaylight.yangtools.sal.binding.generator.impl,
+ org.opendaylight.yangtools.yang.parser.impl.util,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625,
+ org.opendaylight.yangtools.yang.data.api,
+ org.opendaylight.yangtools.objcache.spi,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser,
+ org.opendaylight.yangtools.maven.sal.api.gen.plugin,
+ org.opendaylight.yangtools.yang.data.impl.schema.builder.impl,
+ org.opendaylight.yangtools.yang.data.api.schema.tree,
+ org.opendaylight.yangtools.binding.generator.util,
+ org.opendaylight.yangtools.sal.binding.generator.spi,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715,
+ org.opendaylight.yangtools.yang2sources.spi,
+ org.opendaylight.yangtools.yang.model.repo.api,
+ org.opendaylight.yangtools.util,
+ org.opendaylight.yangtools.yang.parser.util,
+ org.opendaylight.yangtools.yang.data.api.schema.stream,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.base.serializer,
+ org.opendaylight.yangtools.concepts,
+ org.opendaylight.yangtools.yang.binding,
+ org.opendaylight.yangtools.yang.model.util.repo,
+ org.opendaylight.yangtools.yang.wadl.generator.maven,
+ org.opendaylight.yangtools.yang.data.api.schema,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type,
+ org.opendaylight.yangtools.concepts.util,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version,
+ org.opendaylight.yangtools.sal.binding.model.api,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip,
+ org.opendaylight.yangtools.yang.data.impl.schema.builder.api,
+ org.opendaylight.yangtools.util.concurrent,
+ org.opendaylight.yangtools.yang.parser.builder.impl,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.base,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling,
+ org.opendaylight.yangtools.sal.binding.model.api.type.builder,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields,
+ org.opendaylight.yangtools.yang2sources.plugin,
+ org.opendaylight.yangtools.yang.data.impl.codec.xml,
+ org.opendaylight.yangtools.antlrv4.code.gen,
+ org.opendaylight.yangtools.yang.parser.builder.util,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform,
+ org.opendaylight.yangtools.yang.model.api.type,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer,
+ org.opendaylight.yangtools.yang.data.api.schema.tree.spi,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser,
+ org.opendaylight.yangtools.sal.binding.yang.types,
+ org.opendaylight.yangtools.yang.data.impl.schema.transform.dom,
+ org.opendaylight.yangtools.yang.data.impl.util,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.timerange,
+ org.opendaylight.yangtools.sal.binding.generator.api,
+ org.opendaylight.yangtools.sal.java.api.generator,
+ org.opendaylight.yangtools.yang.binding.annotations,
+ org.opendaylight.yangtools.sal.binding.generator.util,
+ org.opendaylight.yangtools.yang.model.repo.util,
+ org.opendaylight.yangtools.yang.model.api,
+ org.opendaylight.yangtools.yang.common,
+ org.opendaylight.yangtools.yang.wadl.generator,
+ org.opendaylight.yangtools.yang.parser.builder.api,
+ org.opendaylight.yangtools.yang.model.util,
+ org.opendaylight.yangtools.yang.parser.impl,
+ org.opendaylight.yangtools.yang.data.impl.schema,
+ org.opendaylight.yangtools.yang.data.api.codec,
+ org.opendaylight.yangtools.yang.unified.doc.generator,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list,
+ org.opendaylight.yangtools.objcache,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions,
+ org.opendaylight.yangtools.yang.data.util,
+ org.opendaylight.yangtools.yang.unified.doc.generator.maven,
+ org.opendaylight.yangtools.binding.generator.util.generated.type.builder,
+ org.opendaylight.yangtools.yang.model.repo.spi,
+ org.opendaylight.yangtools.yang.parser.repo,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715,
+ org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches,
+ org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid,
+ com.romix.scala,
+ com.romix.scala.collection,
+ com.romix.scala.collection.concurrent,
+ org.opendaylight.yangtools.objcache.impl
+ </Export-Package>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java
new file mode 100644
index 00000000..b29d687e
--- /dev/null
+++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfFlowRuleProvider.java
@@ -0,0 +1,403 @@
+/*
+ * 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.flow.impl;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+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.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleProvider;
+import org.onosproject.net.flow.FlowRuleProviderRegistry;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessList;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessListBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.AccessListEntries;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.AccessListEntriesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.ActionsBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.Matches;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.MatchesBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling.DenyBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.actions.packet.handling.PermitBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIpBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4Builder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Prefix;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRange;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRangeBuilder;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRange;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRangeBuilder;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+/**
+ * Netconf provider to accept any flow and report them.
+ */
+@Component(immediate = true)
+public class NetconfFlowRuleProvider extends AbstractProvider
+ implements FlowRuleProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleProviderRegistry providerRegistry;
+
+ private ConcurrentMap<DeviceId, Set<FlowEntry>> flowTable = new ConcurrentHashMap<>();
+
+ private FlowRuleProviderService providerService;
+
+ private XmlBuilder xmlBuilder;
+
+ private AceIp aceIp;
+ private SourcePortRange srcPortRange;
+ private DestinationPortRange destPortRange;
+ private Matches matches;
+ private HashedWheelTimer timer = Timer.getTimer();
+ private Timeout timeout;
+ private static final String ACL_NAME_KEY = "acl-name";
+ private static final String ACL_LIST_ENTRIES_RULE_NAME_KEY = "access-list-entries.rule-name";
+ private static final String ACL_LIST_SP_LOWER_KEY = "source-port-range.lower-port";
+ private static final String ACL_LIST_SP_UPPER_KEY = "source-port-range.upper-port";
+ private static final String ACL_LIST_DP_LOWER_KEY = "destination-port-range.lower-port";
+ private static final String ACL_LIST_DP_UPPER_KEY = "destination-port-range.upper-port";
+ private static final String ACL_LIST_DEST_IPV4_KEY = "matches.destination-ipv4-address";
+ private static final String ACL_LIST_SRC_IPV4_KEY = "matches.source-ipv4-address";
+ private static final String ACL_LIST_ACTIONS_KEY = "actions";
+
+ public NetconfFlowRuleProvider() {
+ super(new ProviderId("netconf", "org.onosproject.provider.netconf"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ providerService = providerRegistry.register(this);
+ timeout = timer.newTimeout(new StatisticTask(), 5, TimeUnit.SECONDS);
+ applyRule();
+ modified(context);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ providerService = null;
+ timeout.cancel();
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ if (xmlBuilder == null) {
+ xmlBuilder = new XmlBuilder();
+ }
+ if (context == null) {
+ log.info("No configuration file");
+ return;
+ }
+ Dictionary<?, ?> properties = context.getProperties();
+ String deviceEntry = get(properties, "devConfigs");
+ log.info("Settings: devConfigs={}", deviceEntry);
+ Enumeration<?> elements = properties.keys();
+ Object nextElement = elements.nextElement();
+ while (elements.hasMoreElements()) {
+ if (nextElement instanceof String) {
+ log.info("key::" + nextElement + ", value::"
+ + get(properties, (String) nextElement));
+ }
+ nextElement = elements.nextElement();
+ }
+ if (!isNullOrEmpty(deviceEntry)) {
+ Map<String, String> deviceMap = processDeviceEntry(deviceEntry);
+ AccessList accessList = buildAccessList(properties);
+ String xmlMsg = xmlBuilder.buildAclRequestXml(accessList);
+ log.info("The resultant xml from the builder\n" + xmlMsg);
+ NetconfOperation netconfOperation = new NetconfOperation();
+ netconfOperation.sendXmlMessage(xmlMsg, deviceMap.get("username"),
+ deviceMap.get("password"),
+ deviceMap.get("hostIp"), Integer
+ .parseInt(deviceMap
+ .get("hostPort")));
+ }
+ }
+
+ /**
+ * @param properties
+ * @return accessList
+ */
+ private AccessList buildAccessList(Dictionary<?, ?> properties) {
+ /**
+ * Populating Access List.
+ */
+ AccessListBuilder abuilder = new AccessListBuilder();
+ String aclName = get(properties, ACL_NAME_KEY);
+ if (aclName != null) {
+ abuilder.setAclName(aclName);
+ }
+ AccessList accessList = abuilder.build();
+ abuilder.setAccessListEntries(getAccessListEntries(properties, matches));
+ srcPortRange = getSourcePortRange(properties);
+ destPortRange = getDestinationPortRange(properties);
+ aceIp = getAceIp(properties, srcPortRange, destPortRange);
+ matches = getMatches(properties);
+ return accessList;
+ }
+
+ /**
+ * @param properties
+ * @return matches
+ */
+ private Matches getMatches(Dictionary<?, ?> properties) {
+ /**
+ * Building Matches for given ACL model.
+ */
+ MatchesBuilder matchesBuilder = new MatchesBuilder();
+ if (aceIp != null) {
+ matchesBuilder.setAceType(aceIp);
+ }
+ matches = matchesBuilder.build();
+ return matches;
+ }
+
+ /**
+ * @param properties
+ * @return srcPortRange
+ */
+ private SourcePortRange getSourcePortRange(Dictionary<?, ?> properties) {
+ /**
+ * Building Source Port Range for given ACL model.
+ */
+ String spRangeLowerStr = get(properties, ACL_LIST_SP_LOWER_KEY);
+ String spRangeUpperStr = get(properties, ACL_LIST_SP_UPPER_KEY);
+ SourcePortRangeBuilder srcPortRangeBuilder = new SourcePortRangeBuilder();
+ if (spRangeLowerStr != null) {
+ int spRangeLower = Integer.parseInt(spRangeLowerStr);
+ srcPortRangeBuilder.setLowerPort(new PortNumber(spRangeLower));
+ }
+ if (spRangeUpperStr != null) {
+ int spRangeUpper = Integer.parseInt(spRangeUpperStr);
+ srcPortRangeBuilder.setUpperPort(new PortNumber(spRangeUpper));
+ }
+ srcPortRange = srcPortRangeBuilder.build();
+ return srcPortRange;
+ }
+
+ /**
+ * @param properties
+ * @return destPortRange
+ */
+ private DestinationPortRange getDestinationPortRange(Dictionary<?, ?> properties) {
+ /**
+ * Building Destination Port Range for given ACL model.
+ */
+ String dpRangeLowerStr = get(properties, ACL_LIST_DP_LOWER_KEY);
+ String dpRangeUpperStr = get(properties, ACL_LIST_DP_UPPER_KEY);
+ DestinationPortRangeBuilder destPortRangeBuilder = new DestinationPortRangeBuilder();
+ if (dpRangeLowerStr != null) {
+ int dpRangeLower = Integer.parseInt(dpRangeLowerStr);
+ destPortRangeBuilder.setLowerPort(new PortNumber(dpRangeLower));
+ }
+ if (dpRangeUpperStr != null) {
+ int dpRangeUpper = Integer.parseInt(dpRangeUpperStr);
+ destPortRangeBuilder.setUpperPort(new PortNumber(dpRangeUpper));
+ }
+ destPortRange = destPortRangeBuilder.build();
+ return destPortRange;
+ }
+
+ /**
+ * @param properties
+ * @return accessListEntries
+ */
+ private List<AccessListEntries> getAccessListEntries(Dictionary<?, ?> properties,
+ Matches matches) {
+ /**
+ * Build and Populate Access List Entries.
+ */
+ AccessListEntriesBuilder acLListEntriesBuilder = new AccessListEntriesBuilder();
+ String aclListEntriesRuleName = get(properties,
+ ACL_LIST_ENTRIES_RULE_NAME_KEY);
+ if (aclListEntriesRuleName != null) {
+ acLListEntriesBuilder.setRuleName(aclListEntriesRuleName);
+ }
+ acLListEntriesBuilder.setMatches(matches);
+ String aclActions = get(properties, ACL_LIST_ACTIONS_KEY);
+ if (aclActions != null) {
+ ActionsBuilder actionBuilder = new ActionsBuilder();
+ if (aclActions.equalsIgnoreCase("deny")) {
+ DenyBuilder denyBuilder = new DenyBuilder();
+ actionBuilder.setPacketHandling(denyBuilder.build());
+ } else if (aclActions.equalsIgnoreCase("permit")) {
+ PermitBuilder permitBuilder = new PermitBuilder();
+ actionBuilder.setPacketHandling(permitBuilder.build());
+ }
+ acLListEntriesBuilder.setActions(actionBuilder.build());
+ }
+ AccessListEntries aclListEntries = acLListEntriesBuilder.build();
+ List<AccessListEntries> accessListEntries = new ArrayList<AccessListEntries>();
+ accessListEntries.add(aclListEntries);
+ return accessListEntries;
+ }
+
+ /**
+ * @param properties
+ * @return aceIp
+ */
+ private AceIp getAceIp(Dictionary<?, ?> properties,
+ SourcePortRange srcPortRange,
+ DestinationPortRange destPortRange) {
+ /**
+ * Building Ace IPV4 Type
+ */
+ String destIpv4 = get(properties, ACL_LIST_DEST_IPV4_KEY);
+ String srcIpv4 = get(properties, ACL_LIST_SRC_IPV4_KEY);
+ AceIpv4Builder aceIpv4Builder = new AceIpv4Builder();
+ aceIp = null;
+ if (destIpv4 != null) {
+ Ipv4Prefix destinationIp = new Ipv4Prefix(destIpv4);
+ aceIpv4Builder.setDestinationIpv4Address(destinationIp);
+ }
+ if (srcIpv4 != null) {
+ Ipv4Prefix sourceIp = new Ipv4Prefix(srcIpv4);
+ aceIpv4Builder.setSourceIpv4Address(sourceIp);
+ }
+ if (destIpv4 != null || srcIpv4 != null) {
+ AceIpv4 aceIpv4 = aceIpv4Builder.build();
+ AceIpBuilder aceIpBuilder = new AceIpBuilder();
+ aceIpBuilder.setAceIpVersion(aceIpv4);
+ aceIpBuilder.setSourcePortRange(srcPortRange);
+ aceIpBuilder.setDestinationPortRange(destPortRange);
+ aceIp = aceIpBuilder.build();
+ }
+ return aceIp;
+ }
+
+ /**
+ * @param deviceEntry
+ * @return deviceMap
+ */
+ private Map<String, String> processDeviceEntry(String deviceEntry) {
+ if (deviceEntry == null) {
+ log.info("No content for Device Entry, so cannot proceed further.");
+ return null;
+ }
+
+ Map<String, String> deviceMap = new HashMap<String, String>();
+ log.info("Trying to convert Device Entry String: " + deviceEntry
+ + " to a Netconf Device Object");
+ try {
+ URI uri = new URI(deviceEntry);
+ String path = uri.getPath();
+ String userInfo = path.substring(path.lastIndexOf('@'));
+ String hostInfo = path.substring(path.lastIndexOf('@') + 1);
+ String[] infoSplit = userInfo.split(":");
+ String username = infoSplit[0];
+ String password = infoSplit[1];
+ infoSplit = hostInfo.split(":");
+ String hostIp = infoSplit[0];
+ String hostPort = infoSplit[1];
+ if (isNullOrEmpty(username) || isNullOrEmpty(password)
+ || isNullOrEmpty(hostIp) || isNullOrEmpty(hostPort)) {
+ log.warn("Bad Configuration Data: both user and device"
+ + " information parts of Configuration " + deviceEntry
+ + " should be non-nullable");
+ } else {
+ deviceMap.put("hostIp", hostIp);
+ deviceMap.put("hostPort", hostPort);
+ deviceMap.put("username", username);
+ deviceMap.put("password", password);
+ }
+ } catch (ArrayIndexOutOfBoundsException aie) {
+ log.error("Error while reading config infromation from the config file: "
+ + "The user, host and device state infomation should be "
+ + "in the order 'userInfo@hostInfo:deviceState'"
+ + deviceEntry, aie);
+ } catch (URISyntaxException urie) {
+ log.error("Error while parsing config information for the device entry: "
+ + "Illegal character in path " + deviceEntry,
+ urie);
+ } catch (Exception e) {
+ log.error("Error while parsing config information for the device entry: "
+ + deviceEntry, e);
+ }
+ return deviceMap;
+ }
+
+ @Override
+ public void applyFlowRule(FlowRule... flowRules) {
+ }
+
+ @Override
+ public void removeFlowRule(FlowRule... flowRules) {
+ }
+
+ private void applyRule() {
+ // applyFlowRule(flowRules);//currentl
+ }
+
+ @Override
+ public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
+ log.info("removal by app id not supported in null provider");
+ }
+
+ @Override
+ public void executeBatch(FlowRuleBatchOperation batch) {
+
+ }
+
+ private class StatisticTask implements TimerTask {
+
+ @Override
+ public void run(Timeout to) throws Exception {
+ for (DeviceId devId : flowTable.keySet()) {
+ providerService.pushFlowMetrics(devId, flowTable
+ .getOrDefault(devId, Collections.emptySet()));
+ }
+ timeout = timer.newTimeout(to.getTask(), 5, TimeUnit.SECONDS);
+
+ }
+ }
+}
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
new file mode 100644
index 00000000..d03e75ac
--- /dev/null
+++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/NetconfOperation.java
@@ -0,0 +1,139 @@
+/*
+ * 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.flow.impl;
+
+import static org.onlab.util.Tools.delay;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.io.IOException;
+
+import org.slf4j.Logger;
+
+import com.tailf.jnc.Capabilities;
+import com.tailf.jnc.JNCException;
+import com.tailf.jnc.SSHConnection;
+import com.tailf.jnc.SSHSession;
+
+/**
+ * This is to carry necessary information to connect and execute NETCONF
+ * operations.
+ */
+public class NetconfOperation {
+ private final Logger log = getLogger(NetconfOperation.class);
+ private static final int EVENTINTERVAL = 2000;
+ private static final int CONNECTION_CHECK_INTERVAL = 3;
+ private static final String INPUT_HELLO_XML_MSG = new StringBuilder(
+ "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
+ .append("<hello xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">")
+ .append("<capabilities><capability>urn:ietf:params:netconf:base:1.0</capability>")
+ .append("</capabilities></hello>").toString();
+
+ /**
+ * This will send a Xml message to the device.
+ * @param xmlMsg XML to send
+ * @param username user name
+ * @param password pass word
+ * @param deviceIp ip address of the device
+ * @param devicePort port on the device
+ */
+ protected void sendXmlMessage(String xmlMsg, String username,
+ String password, String deviceIp,
+ Integer devicePort) {
+ SSHSession ssh = null;
+ try {
+ SSHConnection sshConnection = getConnection(username, password,
+ deviceIp, devicePort);
+ ssh = new SSHSession(sshConnection);
+ executeMessage(ssh, INPUT_HELLO_XML_MSG);
+ /*
+ * execute acl message
+ */
+ executeMessage(ssh, xmlMsg);
+
+ } catch (IOException e) {
+ log.error("Unable to send Hello Message to the device: ", e);
+ } catch (JNCException e) {
+ log.error("Authentication fail while sending Hello Message to the device: ",
+ e);
+ } catch (Exception e) {
+ log.error("Unable to send Hello Message to the device: ", e);
+ } finally {
+ log.debug("Closing the session after successful execution");
+ ssh.close();
+ }
+ }
+
+ private void executeMessage(SSHSession ssh, String xmlMsg)
+ throws IOException, JNCException {
+ String helloRequestXML = xmlMsg.trim();
+
+ log.debug("Sending Hello");
+ ssh.print(helloRequestXML);
+ ssh.flush();
+ String xmlResponse = null;
+ int i = CONNECTION_CHECK_INTERVAL;
+ while (!ssh.ready() && i > 0) {
+ delay(EVENTINTERVAL);
+ i--;
+ }
+
+ if (ssh.ready()) {
+ StringBuffer readOne = ssh.readOne();
+ if (readOne == null) {
+ log.error("The Hello Contains No Capabilites");
+ throw new JNCException(
+ JNCException.SESSION_ERROR,
+ "server does not support NETCONF base capability: "
+ + Capabilities.NETCONF_BASE_CAPABILITY);
+ } else {
+ xmlResponse = readOne.toString().trim();
+
+ log.debug("Reading Capabilities: "
+ + ssh.getSSHConnection().getGanymedConnection()
+ .getHostname());
+ }
+ }
+ }
+
+ /**
+ * To establish SSH Connection.
+ *
+ * @param username user name
+ * @param password pass word
+ * @param sshHost host
+ * @param sshPort port
+ * @return new SSH connection
+ * @throws IOException if connection fails
+ * @throws JNCException if connection causes an error
+ */
+ public SSHConnection getConnection(String username, String password,
+ String sshHost, Integer sshPort)
+ throws IOException, JNCException {
+ SSHConnection sshConnection;
+ try {
+ sshConnection = new SSHConnection(sshHost, sshPort);
+ sshConnection.authenticateWithPassword(username, password);
+ } catch (IOException e) {
+ log.error("Unable to create a connection to the device: ");
+ throw e;
+ } catch (JNCException e) {
+ log.error("Failed to connect to the device: ");
+ throw e;
+ }
+ return sshConnection;
+ }
+
+}
diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java
new file mode 100644
index 00000000..389f1669
--- /dev/null
+++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/XmlBuilder.java
@@ -0,0 +1,223 @@
+/*
+ * 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.flow.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.Namespace;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.AccessList;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.AceType;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceEth;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.AceIp;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.AceIpVersion;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv4;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.acl.rev140520.access.list.access.list.entries.matches.ace.type.ace.ip.ace.ip.version.AceIpv6;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.DestinationPortRange;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.packet.fields.rev140625.acl.transport.header.fields.SourcePortRange;
+import org.slf4j.Logger;
+
+/**
+ * Xml Builder to generate the xml according to given ACL model.
+ */
+public class XmlBuilder {
+ private final Logger log = getLogger(XmlBuilder.class);
+
+ public String buildAclRequestXml(AccessList accessList) {
+ Document doc = new Document();
+ Namespace namespaceRpc = Namespace
+ .getNamespace("urn:ietf:params:xml:ns:netconf:base:1.0");
+ Namespace accessNamespaceRpc = Namespace
+ .getNamespace("urn:ietf:params:xml:ns:yang:ietf-acl");
+ doc.setRootElement(new Element("rpc", namespaceRpc)
+ .setAttribute("message-id", "101"));
+
+ /**
+ * Access list elements of given ACL model.
+ */
+ Element access = new Element("access-list", accessNamespaceRpc);
+ access.addContent(new Element("acl-name", accessNamespaceRpc)
+ .setText(accessList.getAclName()));
+ // access.addContent(accessEntries);
+
+ if (!accessList.getAccessListEntries().isEmpty()
+ && accessList.getAccessListEntries() != null) {
+ for (int accessEntryIntVlu = 0; accessEntryIntVlu < accessList
+ .getAccessListEntries().size(); accessEntryIntVlu++) {
+ access.addContent(getAccessEntries(accessEntryIntVlu,
+ accessList,
+ accessNamespaceRpc));
+ }
+ }
+
+ /**
+ * edit-config operation for given ACL model.
+ */
+ Element editConfig = new Element("edit-config", namespaceRpc);
+ editConfig.addContent(new Element("target", namespaceRpc)
+ .addContent(new Element("running", namespaceRpc)));
+ editConfig.addContent(new Element("config", Namespace
+ .getNamespace("urn:ietf:params:xml:ns:netconf:base:1.0"))
+ .addContent(access));
+
+ doc.getRootElement().addContent(editConfig);
+ XMLOutputter xmlOutputter = new XMLOutputter(Format.getPrettyFormat());
+ String outputString = xmlOutputter.outputString(doc);
+
+ return outputString;
+ }
+
+ /**
+ * access entries operation for given ACL model.
+ */
+ private Element getAccessEntries(int accessEntryIntVlu,
+ AccessList accessList,
+ Namespace accessNamespaceRpc) {
+
+ /**
+ * Port Number
+ */
+
+ int srcPortRangeLower = 0;
+ int srcPortRangeUpper = 0;
+ int destPortRangeLower = 0;
+ int destPortRangeUpper = 0;
+
+ String sourceIpAdd = "";
+ String destinationIpAdd = "";
+
+ /*
+ * checking accessList is null or not
+ */
+ if (accessList != null) {
+ /*
+ * checking list entries are empty or null
+ */
+ if (!accessList.getAccessListEntries().isEmpty()
+ && accessList.getAccessListEntries() != null) {
+ AceType aceType = accessList.getAccessListEntries()
+ .get(accessEntryIntVlu).getMatches().getAceType();
+
+ if (aceType instanceof AceIp) {
+ AceIp aceIp = (AceIp) aceType;
+ SourcePortRange sourcePortRange = aceIp
+ .getSourcePortRange();
+ if (sourcePortRange != null) {
+ PortNumber lowerPort = sourcePortRange.getLowerPort();
+ PortNumber upperPort = sourcePortRange.getUpperPort();
+
+ if (lowerPort != null) {
+ srcPortRangeLower = lowerPort.getValue();
+ }
+ if (upperPort != null) {
+ srcPortRangeUpper = upperPort.getValue();
+ }
+ }
+ DestinationPortRange destinationPortRange = aceIp
+ .getDestinationPortRange();
+
+ if (destinationPortRange != null) {
+ PortNumber lowerPort = destinationPortRange
+ .getLowerPort();
+ if (lowerPort != null) {
+ destPortRangeLower = lowerPort.getValue();
+ }
+
+ PortNumber upperPort = destinationPortRange
+ .getUpperPort();
+ if (upperPort != null) {
+ destPortRangeUpper = upperPort.getValue();
+ }
+
+ }
+
+ AceIpVersion aceIpVersion = aceIp.getAceIpVersion();
+ if (aceIpVersion instanceof AceIpv4) {
+ AceIpv4 obj = (AceIpv4) aceIpVersion;
+ destinationIpAdd = obj.getDestinationIpv4Address()
+ .getValue();
+ sourceIpAdd = obj.getSourceIpv4Address().getValue();
+ } else if (aceIpVersion instanceof AceIpv6) {
+ AceIpv6 obj = (AceIpv6) aceIpVersion;
+ destinationIpAdd = obj.getDestinationIpv6Address()
+ .getValue();
+ sourceIpAdd = obj.getSourceIpv6Address().getValue();
+ }
+ } else if (aceType instanceof AceEth) {
+ log.debug("Need to add execution loging for Ace Type Ethernet");
+ }
+ }
+ }
+
+ /**
+ * Matches elements to define IP address & Port range for given ACL
+ * model.
+ */
+ Element matchesElement = new Element("matches", accessNamespaceRpc);
+ if (String.valueOf(srcPortRangeLower) != null
+ && !String.valueOf(srcPortRangeLower).isEmpty()) {
+
+ matchesElement.addContent(new Element("source-port-range",
+ accessNamespaceRpc)
+ .addContent(new Element("lower-port", accessNamespaceRpc)
+ .setText(String.valueOf(srcPortRangeLower))));
+
+ matchesElement.addContent(new Element("source-port-range",
+ accessNamespaceRpc)
+ .addContent(new Element("upper-port", accessNamespaceRpc)
+ .setText(String.valueOf(srcPortRangeUpper))));
+
+ matchesElement.addContent(new Element("destination-port-range",
+ accessNamespaceRpc)
+ .addContent(new Element("lower-port", accessNamespaceRpc)
+ .setText(String.valueOf(destPortRangeLower))));
+
+ matchesElement.addContent(new Element("destination-port-range",
+ accessNamespaceRpc)
+ .addContent(new Element("upper-port", accessNamespaceRpc)
+ .setText(String.valueOf(destPortRangeUpper))));
+ }
+
+ if (destinationIpAdd != null && !destinationIpAdd.isEmpty()) {
+ matchesElement.addContent(new Element("destination-ipv4-address",
+ accessNamespaceRpc)
+ .setText(destinationIpAdd));
+ }
+ if (sourceIpAdd != null && !sourceIpAdd.isEmpty()) {
+ matchesElement.addContent(new Element("source-ipv4-address",
+ accessNamespaceRpc)
+ .setText(sourceIpAdd));
+ }
+
+ /**
+ * Access entries elements for given ACL model.
+ */
+ Element accessEntries = new Element("access-list-entries",
+ accessNamespaceRpc);
+ accessEntries.addContent(new Element("rule-name", accessNamespaceRpc)
+ .setText(accessList.getAccessListEntries()
+ .get(accessEntryIntVlu).getRuleName()));
+ accessEntries.addContent(matchesElement);
+ accessEntries.addContent(new Element("actions", accessNamespaceRpc)
+ .addContent(new Element("deny", accessNamespaceRpc)));
+
+ return accessEntries;
+ }
+}
diff --git a/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java
new file mode 100644
index 00000000..b095fc9a
--- /dev/null
+++ b/framework/src/onos/providers/netconf/flow/src/main/java/org/onosproject/provider/netconf/flow/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 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 will accept any flow rules.
+ */
+package org.onosproject.provider.netconf.flow.impl;
+
diff --git a/framework/src/onos/providers/netconf/pom.xml b/framework/src/onos/providers/netconf/pom.xml
new file mode 100644
index 00000000..ac08dd4a
--- /dev/null
+++ b/framework/src/onos/providers/netconf/pom.xml
@@ -0,0 +1,49 @@
+<?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.
+ -->
+<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-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-netconf-providers</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS Netconf protocol adapters</description>
+
+ <modules>
+ <module>device</module>
+ <module>app</module>
+ <module>flow</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/null/pom.xml b/framework/src/onos/providers/null/pom.xml
new file mode 100644
index 00000000..ce547588
--- /dev/null
+++ b/framework/src/onos/providers/null/pom.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-null-provider</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>Null southbound providers</description>
+
+ <properties>
+ <onos.app.name>org.onosproject.null</onos.app.name>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.karaf.shell</groupId>
+ <artifactId>org.apache.karaf.shell.console</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-cli</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java
new file mode 100644
index 00000000..2d09ebed
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/AggLinkTopologySimulator.java
@@ -0,0 +1,49 @@
+/*
+ * 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Simple triangle topology with multiple links between same devices.
+ */
+public class AggLinkTopologySimulator extends CentipedeTopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ infrastructurePorts = 2 * deviceCount - 1;
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 2, "There must be at least 3 devices");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int srcPortOffset = deviceCount + 1;
+ for (int i = 0, n = deviceCount; i < n; i++) {
+ int dstPortOffset = 1;
+ for (int j = 0; j <= i; j++) {
+ createLink(i, (i + 1) % n, srcPortOffset + j, dstPortOffset + j);
+ }
+ srcPortOffset = dstPortOffset + i + 1;
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
new file mode 100644
index 00000000..5234d448
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/CentipedeTopologySimulator.java
@@ -0,0 +1,30 @@
+/*
+ * 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.nil;
+
+/**
+ * Linear topology with hosts on every device.
+ */
+public class CentipedeTopologySimulator extends LinearTopologySimulator {
+
+ /**
+ * Creates simulated hosts.
+ */
+ protected void createHosts() {
+ deviceIds.forEach(id -> createHosts(id, infrastructurePorts));
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java
new file mode 100644
index 00000000..ad57bf3f
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/ConfiguredTopologySimulator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.nil;
+
+/**
+ * Topology simulator which operates on topology configured via the REST API
+ * config service.
+ */
+public class ConfiguredTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void createDevices() {
+ deviceService.getDevices()
+ .forEach(device -> deviceProviderService
+ .deviceConnected(device.id(), description(device)));
+ }
+
+ @Override
+ protected void createLinks() {
+ linkService.getLinks()
+ .forEach(link -> linkProviderService
+ .linkDetected(description(link)));
+ }
+
+ @Override
+ protected void createHosts() {
+ hostService.getHosts()
+ .forEach(host -> hostProviderService
+ .hostDetected(host.id(), description(host)));
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java
new file mode 100644
index 00000000..beb06c33
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/LinearTopologySimulator.java
@@ -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.
+ */
+package org.onosproject.provider.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Linear topology simulator.
+ */
+public class LinearTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 1, "There must be at least 2 devices");
+
+ prepareForDeviceEvents(deviceCount);
+ createDevices();
+ waitForDeviceEvents();
+
+ createLinks();
+ createHosts();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int i = 0, n = deviceCount - 1; i < n; i++) {
+ createLink(i, i + 1, portOffset, 1);
+ portOffset = 2;
+ }
+ }
+
+ @Override
+ protected void createHosts() {
+ createHosts(deviceIds.get(0), infrastructurePorts);
+ createHosts(deviceIds.get(deviceCount - 1), infrastructurePorts);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java
new file mode 100644
index 00000000..d3f2d6ad
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/MeshTopologySimulator.java
@@ -0,0 +1,44 @@
+/*
+ * 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.nil;
+
+/**
+ * Full mesh topology with hosts at each device.
+ */
+public class MeshTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ // FIXME: implement this
+ }
+
+ @Override
+ public void setUpTopology() {
+ // FIXME: implement this
+ // checkArgument(FIXME, "There must be at least ...");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ }
+
+ @Override
+ protected void createHosts() {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
new file mode 100644
index 00000000..9b01d12a
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullFlowRuleProvider.java
@@ -0,0 +1,132 @@
+/*
+ * 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.nil;
+
+import com.google.common.collect.Sets;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
+import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleProvider;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Null provider to accept any flow and report them.
+ */
+class NullFlowRuleProvider extends NullProviders.AbstractNullProvider
+ implements FlowRuleProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ private ConcurrentMap<DeviceId, Set<FlowEntry>> flowTable = new ConcurrentHashMap<>();
+
+ private FlowRuleProviderService providerService;
+
+ private HashedWheelTimer timer = Timer.getTimer();
+ private Timeout timeout;
+
+ /**
+ * Starts the flow rule provider simulation.
+ *
+ * @param providerService flow rule provider service
+ */
+ void start(FlowRuleProviderService providerService) {
+ this.providerService = providerService;
+ timeout = timer.newTimeout(new StatisticTask(), 5, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the flow rule provider simulation.
+ */
+ void stop() {
+ timeout.cancel();
+ }
+
+ @Override
+ public void applyFlowRule(FlowRule... flowRules) {
+ // FIXME: invoke executeBatch
+ }
+
+ @Override
+ public void removeFlowRule(FlowRule... flowRules) {
+ // FIXME: invoke executeBatch
+ }
+
+ @Override
+ public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
+ throw new UnsupportedOperationException("Cannot remove by appId from null provider");
+ }
+
+ @Override
+ public void executeBatch(FlowRuleBatchOperation batch) {
+ // TODO: consider checking mastership
+ Set<FlowEntry> entries =
+ flowTable.getOrDefault(batch.deviceId(),
+ Sets.newConcurrentHashSet());
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ switch (fbe.operator()) {
+ case ADD:
+ entries.add(new DefaultFlowEntry(fbe.target()));
+ break;
+ case REMOVE:
+ entries.remove(new DefaultFlowEntry(fbe.target()));
+ break;
+ case MODIFY:
+ FlowEntry entry = new DefaultFlowEntry(fbe.target());
+ entries.remove(entry);
+ entries.add(entry);
+ break;
+ default:
+ log.error("Unknown flow operation: {}", fbe);
+ }
+ }
+ flowTable.put(batch.deviceId(), entries);
+ CompletedBatchOperation op =
+ new CompletedBatchOperation(true, Collections.emptySet(),
+ batch.deviceId());
+ providerService.batchOperationCompleted(batch.id(), op);
+ }
+
+ // Periodically reports flow rule statistics.
+ private class StatisticTask implements TimerTask {
+ @Override
+ public void run(Timeout to) throws Exception {
+ for (DeviceId devId : flowTable.keySet()) {
+ Set<FlowEntry> entries =
+ flowTable.getOrDefault(devId, Collections.emptySet());
+ providerService.pushFlowMetrics(devId, entries);
+ }
+ timeout = timer.newTimeout(to.getTask(), 5, TimeUnit.SECONDS);
+ }
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java
new file mode 100644
index 00000000..07a137e9
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullPacketProvider.java
@@ -0,0 +1,169 @@
+/*
+ * 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.nil;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.ICMP;
+import org.onlab.util.Timer;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderService;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.google.common.collect.ImmutableList.copyOf;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which generates simulated packets and acts as a sink for outbound
+ * packets. To be used for benchmarking only.
+ */
+class NullPacketProvider extends NullProviders.AbstractNullProvider
+ implements PacketProvider {
+
+ private static final int INITIAL_DELAY = 5;
+ private final Logger log = getLogger(getClass());
+
+ // Arbitrary host src/dst
+ private static final int SRC_HOST = 2;
+ private static final int DST_HOST = 5;
+
+ // Time between event firing, in milliseconds
+ private int delay;
+
+ // TODO: use host service to pick legitimate hosts connected to devices
+ private HostService hostService;
+ private PacketProviderService providerService;
+
+ private List<Device> devices;
+ private int currentDevice = 0;
+
+ private HashedWheelTimer timer = Timer.getTimer();
+ private Timeout timeout;
+
+ /**
+ * Starts the packet generation process.
+ *
+ * @param packetRate packets per second
+ * @param hostService host service
+ * @param deviceService device service
+ * @param providerService packet provider service
+ */
+ void start(int packetRate, HostService hostService,
+ DeviceAdminService deviceService,
+ PacketProviderService providerService) {
+ this.hostService = hostService;
+ this.providerService = providerService;
+
+ this.devices = copyOf(deviceService.getDevices()).stream()
+ .filter(d -> deviceService.getRole(d.id()) == MASTER)
+ .collect(Collectors.toList());
+
+ adjustRate(packetRate);
+ timeout = timer.newTimeout(new PacketDriverTask(), INITIAL_DELAY, SECONDS);
+ }
+
+ /**
+ * Adjusts packet rate.
+ *
+ * @param packetRate new packet rate
+ */
+ void adjustRate(int packetRate) {
+ delay = 1000 / packetRate;
+ log.info("Settings: packetRate={}, delay={}", packetRate, delay);
+ }
+
+ /**
+ * Stops the packet generation process.
+ */
+ void stop() {
+ if (timeout != null) {
+ timeout.cancel();
+ }
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ // We don't have a network to emit to. Keep a counter here, maybe?
+ }
+
+ /**
+ * Generates packet events at a given rate.
+ */
+ private class PacketDriverTask implements TimerTask {
+
+ // Filler echo request
+ ICMP icmp;
+ Ethernet eth;
+
+ public PacketDriverTask() {
+ icmp = new ICMP();
+ icmp.setIcmpType((byte) 8).setIcmpCode((byte) 0).setChecksum((short) 0);
+ eth = new Ethernet();
+ eth.setEtherType(Ethernet.TYPE_IPV4);
+ eth.setPayload(icmp);
+ }
+
+ @Override
+ public void run(Timeout to) {
+ if (!devices.isEmpty() && !to.isCancelled()) {
+ sendEvent(devices.get(Math.min(currentDevice, devices.size() - 1)));
+ currentDevice = (currentDevice + 1) % devices.size();
+ timeout = timer.newTimeout(to.getTask(), delay, TimeUnit.MILLISECONDS);
+ }
+ }
+
+ private void sendEvent(Device device) {
+ // Make it look like things came from ports attached to hosts
+ eth.setSourceMACAddress("00:00:10:00:00:0" + SRC_HOST)
+ .setDestinationMACAddress("00:00:10:00:00:0" + DST_HOST);
+ InboundPacket inPkt = new DefaultInboundPacket(
+ new ConnectPoint(device.id(), PortNumber.portNumber(SRC_HOST)),
+ eth, ByteBuffer.wrap(eth.serialize()));
+ providerService.processPacket(new NullPacketContext(inPkt, null));
+ }
+ }
+
+ // Minimal PacketContext to make core and applications happy.
+ private final class NullPacketContext extends DefaultPacketContext {
+ private NullPacketContext(InboundPacket inPkt, OutboundPacket outPkt) {
+ super(System.currentTimeMillis(), inPkt, outPkt, false);
+ }
+
+ @Override
+ public void send() {
+ // We don't send anything out.
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
new file mode 100644
index 00000000..c5688419
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/NullProviders.java
@@ -0,0 +1,420 @@
+/*
+ * 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.nil;
+
+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.apache.felix.scr.annotations.Service;
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceProvider;
+import org.onosproject.net.device.DeviceProviderRegistry;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.flow.FlowRuleProviderRegistry;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+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.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+
+import java.util.Dictionary;
+import java.util.Properties;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.net.MastershipRole.NONE;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider of a fake network environment, i.e. devices, links, hosts, etc.
+ * To be used for benchmarking only.
+ */
+@Component(immediate = true)
+@Service(value = NullProviders.class)
+public class NullProviders {
+
+ private static final Logger log = getLogger(NullProviders.class);
+
+ static final String SCHEME = "null";
+ static final String PROVIDER_ID = "org.onosproject.provider.nil";
+
+ private static final String FORMAT =
+ "Settings: enabled={}, topoShape={}, deviceCount={}, " +
+ "hostCount={}, packetRate={}, mutationRate={}";
+
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipAdminService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceAdminService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry deviceProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostProviderRegistry hostProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkProviderRegistry linkProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleProviderRegistry flowRuleProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketProviderRegistry packetProviderRegistry;
+
+
+ private final NullDeviceProvider deviceProvider = new NullDeviceProvider();
+ private final NullLinkProvider linkProvider = new NullLinkProvider();
+ private final NullHostProvider hostProvider = new NullHostProvider();
+ private final NullFlowRuleProvider flowRuleProvider = new NullFlowRuleProvider();
+ private final NullPacketProvider packetProvider = new NullPacketProvider();
+ private final TopologyMutationDriver topologyMutationDriver = new TopologyMutationDriver();
+
+ private DeviceProviderService deviceProviderService;
+ private HostProviderService hostProviderService;
+ private LinkProviderService linkProviderService;
+ private FlowRuleProviderService flowRuleProviderService;
+ private PacketProviderService packetProviderService;
+
+ private TopologySimulator simulator;
+
+ @Property(name = "enabled", boolValue = false,
+ label = "Enables or disables the provider")
+ private boolean enabled = false;
+
+ private static final String DEFAULT_TOPO_SHAPE = "configured";
+ @Property(name = "topoShape", value = DEFAULT_TOPO_SHAPE,
+ label = "Topology shape: configured, linear, reroute, tree, spineleaf, mesh")
+ private String topoShape = DEFAULT_TOPO_SHAPE;
+
+ private static final int DEFAULT_DEVICE_COUNT = 10;
+ @Property(name = "deviceCount", intValue = DEFAULT_DEVICE_COUNT,
+ label = "Number of devices to generate")
+ private int deviceCount = DEFAULT_DEVICE_COUNT;
+
+ private static final int DEFAULT_HOST_COUNT = 5;
+ @Property(name = "hostCount", intValue = DEFAULT_HOST_COUNT,
+ label = "Number of host to generate per device")
+ private int hostCount = DEFAULT_HOST_COUNT;
+
+ private static final int DEFAULT_PACKET_RATE = 5;
+ @Property(name = "packetRate", intValue = DEFAULT_PACKET_RATE,
+ label = "Packet-in/s rate; 0 for no packets")
+ private int packetRate = DEFAULT_PACKET_RATE;
+
+ private static final double DEFAULT_MUTATION_RATE = 0;
+ @Property(name = "mutationRate", doubleValue = DEFAULT_MUTATION_RATE,
+ label = "Link event/s topology mutation rate; 0 for no mutations")
+ private double mutationRate = DEFAULT_MUTATION_RATE;
+
+ private static final String DEFAULT_MASTERSHIP = "random";
+ @Property(name = "mastership", value = DEFAULT_MASTERSHIP,
+ label = "Mastership given as 'random' or 'node1=dpid,dpid/node2=dpid,...'")
+ private String mastership = DEFAULT_MASTERSHIP;
+
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+
+ deviceProviderService = deviceProviderRegistry.register(deviceProvider);
+ hostProviderService = hostProviderRegistry.register(hostProvider);
+ linkProviderService = linkProviderRegistry.register(linkProvider);
+ flowRuleProviderService = flowRuleProviderRegistry.register(flowRuleProvider);
+ packetProviderService = packetProviderRegistry.register(packetProvider);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ tearDown();
+
+ deviceProviderRegistry.unregister(deviceProvider);
+ hostProviderRegistry.unregister(hostProvider);
+ linkProviderRegistry.unregister(linkProvider);
+ flowRuleProviderRegistry.unregister(flowRuleProvider);
+ packetProviderRegistry.unregister(packetProvider);
+
+ deviceProviderService = null;
+ hostProviderService = null;
+ linkProviderService = null;
+ flowRuleProviderService = null;
+ packetProviderService = null;
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+ boolean newEnabled;
+ int newDeviceCount, newHostCount, newPacketRate;
+ double newMutationRate;
+ String newTopoShape, newMastership;
+ try {
+ String s = get(properties, "enabled");
+ newEnabled = isNullOrEmpty(s) ? enabled : Boolean.parseBoolean(s.trim());
+
+ newTopoShape = get(properties, "topoShape");
+ newMastership = get(properties, "mastership");
+
+ s = get(properties, "deviceCount");
+ newDeviceCount = isNullOrEmpty(s) ? deviceCount : Integer.parseInt(s.trim());
+
+ s = get(properties, "hostCount");
+ newHostCount = isNullOrEmpty(s) ? hostCount : Integer.parseInt(s.trim());
+
+ s = get(properties, "packetRate");
+ newPacketRate = isNullOrEmpty(s) ? packetRate : Integer.parseInt(s.trim());
+
+ s = get(properties, "mutationRate");
+ newMutationRate = isNullOrEmpty(s) ? mutationRate : Double.parseDouble(s.trim());
+
+ } catch (NumberFormatException e) {
+ log.warn(e.getMessage());
+ newEnabled = enabled;
+ newTopoShape = topoShape;
+ newDeviceCount = deviceCount;
+ newHostCount = hostCount;
+ newPacketRate = packetRate;
+ newMutationRate = mutationRate;
+ newMastership = mastership;
+ }
+
+ // Any change in the following parameters implies hard restart
+ if (newEnabled != enabled || !newTopoShape.equals(topoShape) ||
+ newDeviceCount != deviceCount || newHostCount != hostCount) {
+ enabled = newEnabled;
+ topoShape = newTopoShape;
+ deviceCount = newDeviceCount;
+ hostCount = newHostCount;
+ packetRate = newPacketRate;
+ mutationRate = newMutationRate;
+ restartSimulation();
+ }
+
+ // Any change in the following parameters implies just a rate change
+ if (newPacketRate != packetRate || newMutationRate != mutationRate) {
+ packetRate = newPacketRate;
+ mutationRate = newMutationRate;
+ adjustRates();
+ }
+
+ // Any change in mastership implies just reassignments.
+ if (!newMastership.equals(mastership)) {
+ mastership = newMastership;
+ reassignMastership();
+ }
+
+ log.info(FORMAT, enabled, topoShape, deviceCount, hostCount,
+ packetRate, mutationRate);
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ public void severLink(ConnectPoint one, ConnectPoint two) {
+ if (enabled) {
+ topologyMutationDriver.severLink(one, two);
+ }
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ public void repairLink(ConnectPoint one, ConnectPoint two) {
+ if (enabled) {
+ topologyMutationDriver.repairLink(one, two);
+ }
+ }
+
+ // Resets simulation based on the current configuration parameters.
+ private void restartSimulation() {
+ tearDown();
+ if (enabled) {
+ setUp();
+ }
+ }
+
+ // Sets up the topology simulation and all providers.
+ private void setUp() {
+ simulator = selectSimulator(topoShape);
+ simulator.init(topoShape, deviceCount, hostCount,
+ new DefaultServiceDirectory(),
+ deviceProviderService, hostProviderService,
+ linkProviderService);
+ simulator.setUpTopology();
+ flowRuleProvider.start(flowRuleProviderService);
+ packetProvider.start(packetRate, hostService, deviceService,
+ packetProviderService);
+ topologyMutationDriver.start(mutationRate, linkService, deviceService,
+ linkProviderService);
+ }
+
+ // Selects the simulator based on the specified name.
+ private TopologySimulator selectSimulator(String topoShape) {
+ if (topoShape.matches("linear([,].*|$)")) {
+ return new LinearTopologySimulator();
+ } else if (topoShape.matches("centipede([,].*|$)")) {
+ return new CentipedeTopologySimulator();
+ } else if (topoShape.matches("reroute([,].*|$)")) {
+ return new RerouteTopologySimulator();
+ } else if (topoShape.matches("tree([,].*|$)")) {
+ return new TreeTopologySimulator();
+ } else if (topoShape.matches("agglink([,].*|$)")) {
+ return new AggLinkTopologySimulator();
+ } else if (topoShape.matches("spineleaf([,].*|$)")) {
+ return new SpineLeafTopologySimulator();
+ } else if (topoShape.matches("mesh([,].*|$)")) {
+ return new MeshTopologySimulator();
+ } else {
+ return new ConfiguredTopologySimulator();
+ }
+ }
+
+ // Shuts down the topology simulator and all providers.
+ private void tearDown() {
+ if (simulator != null) {
+ topologyMutationDriver.stop();
+ packetProvider.stop();
+ flowRuleProvider.stop();
+ delay(500);
+ simulator.tearDownTopology();
+ simulator = null;
+ }
+ }
+
+ // Changes packet and mutation rates.
+ private void adjustRates() {
+ packetProvider.adjustRate(packetRate);
+ topologyMutationDriver.adjustRate(mutationRate);
+ }
+
+ // Re-assigns mastership roles.
+ private void reassignMastership() {
+ if (mastership.equals(DEFAULT_MASTERSHIP)) {
+ mastershipService.balanceRoles();
+ } else {
+ NodeId localNode = clusterService.getLocalNode().id();
+ rejectMastership();
+ String[] nodeSpecs = mastership.split("/");
+ for (int i = 0; i < nodeSpecs.length; i++) {
+ String[] specs = nodeSpecs[i].split("=");
+ if (specs[0].equals(localNode.toString())) {
+ String[] ids = specs[1].split(",");
+ for (String id : ids) {
+ mastershipService.setRole(localNode, deviceId(id), MASTER);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Rejects mastership of all devices.
+ private void rejectMastership() {
+ NodeId localNode = clusterService.getLocalNode().id();
+ deviceService.getDevices()
+ .forEach(device -> mastershipService.setRole(localNode, device.id(),
+ NONE));
+ }
+
+ // Null provider base class.
+ abstract static class AbstractNullProvider extends AbstractProvider {
+ protected AbstractNullProvider() {
+ super(new ProviderId(SCHEME, PROVIDER_ID));
+ }
+ }
+
+ // Device provider facade.
+ private class NullDeviceProvider extends AbstractNullProvider implements DeviceProvider {
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ deviceProviderService.receivedRoleReply(deviceId, newRole, newRole);
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ return topoShape.equals("configured") ||
+ (simulator != null && simulator.contains(deviceId));
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ }
+ }
+
+ // Host provider facade.
+ private class NullHostProvider extends AbstractNullProvider implements HostProvider {
+ @Override
+ public void triggerProbe(Host host) {
+ }
+ }
+
+ // Host provider facade.
+ private class NullLinkProvider extends AbstractNullProvider implements LinkProvider {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java
new file mode 100644
index 00000000..d17466b9
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/RerouteTopologySimulator.java
@@ -0,0 +1,57 @@
+/*
+ * 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Re-routable linear topology simulator with an alternate path in the middle.
+ */
+public class RerouteTopologySimulator extends LinearTopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ infrastructurePorts = 5;
+ deviceCount = (topoShape.length == 1) ? deviceCount : Integer.parseInt(topoShape[1]);
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(deviceCount > 2, "There must be at least 3 devices");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int i = 0, n = deviceCount - 2; i < n; i++) {
+ createLink(i, i + 1, portOffset, 1);
+ portOffset = 2;
+ }
+ int middle = (deviceCount - 1) / 2;
+ int alternate = deviceCount - 1;
+ createLink(middle - 1, alternate, 3, 1);
+ createLink(middle, alternate, 3, 2);
+ }
+
+ @Override
+ protected void createHosts() {
+ createHosts(deviceIds.get(0), infrastructurePorts);
+ createHosts(deviceIds.get(deviceCount - 2), infrastructurePorts);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java
new file mode 100644
index 00000000..876fc5dc
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/SpineLeafTopologySimulator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.nil;
+
+/**
+ * Spine-leaf topology with hosts at the leaf devices.
+ */
+public class SpineLeafTopologySimulator extends TopologySimulator {
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ // FIXME: implement this
+ }
+
+ @Override
+ public void setUpTopology() {
+ // checkArgument(FIXME, "There must be at least one spine tier");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ }
+
+ @Override
+ protected void createHosts() {
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
new file mode 100644
index 00000000..ccf7e08a
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologyMutationDriver.java
@@ -0,0 +1,223 @@
+/*
+ * 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.nil;
+
+import com.google.common.collect.Lists;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkDescription;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.delay;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.net.Link.Type.DIRECT;
+import static org.onosproject.net.MastershipRole.MASTER;
+import static org.onosproject.provider.nil.TopologySimulator.description;
+
+/**
+ * Drives topology mutations at a specified rate of events per second.
+ */
+class TopologyMutationDriver implements Runnable {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private static final int WAIT_DELAY = 2_000;
+ private static final int MAX_DOWN_LINKS = 5;
+
+ private final Random random = new Random();
+
+ private volatile boolean stopped = true;
+
+ private double mutationRate;
+ private int millis, nanos;
+
+ private LinkService linkService;
+ private DeviceService deviceService;
+ private LinkProviderService linkProviderService;
+
+ private List<LinkDescription> activeLinks;
+ private List<LinkDescription> inactiveLinks;
+
+ private final ExecutorService executor =
+ newSingleThreadScheduledExecutor(groupedThreads("onos/null", "topo-mutator"));
+
+ /**
+ * Starts the mutation process.
+ *
+ * @param mutationRate link events per second
+ * @param linkService link service
+ * @param deviceService device service
+ * @param linkProviderService link provider service
+ */
+ void start(double mutationRate,
+ LinkService linkService, DeviceService deviceService,
+ LinkProviderService linkProviderService) {
+ stopped = false;
+ this.linkService = linkService;
+ this.deviceService = deviceService;
+ this.linkProviderService = linkProviderService;
+ activeLinks = reduceLinks();
+ inactiveLinks = Lists.newArrayList();
+ adjustRate(mutationRate);
+ executor.submit(this);
+ }
+
+ /**
+ * Adjusts the topology mutation rate.
+ *
+ * @param mutationRate new topology mutation rate
+ */
+ void adjustRate(double mutationRate) {
+ this.mutationRate = mutationRate;
+ if (mutationRate > 0) {
+ this.millis = (int) (1_000 / mutationRate / 2);
+ this.nanos = (int) (1_000_000 / mutationRate / 2) % 1_000_000;
+ } else {
+ this.millis = 0;
+ this.nanos = 0;
+ }
+ log.info("Settings: millis={}, nanos={}", millis, nanos);
+ }
+
+ /**
+ * Stops the mutation process.
+ */
+ void stop() {
+ stopped = true;
+ }
+
+ /**
+ * Severs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ void severLink(ConnectPoint one, ConnectPoint two) {
+ LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
+ linkProviderService.linkVanished(link);
+ linkProviderService.linkVanished(reverse(link));
+
+ }
+
+ /**
+ * Repairs the link between the specified end-points in both directions.
+ *
+ * @param one link endpoint
+ * @param two link endpoint
+ */
+ void repairLink(ConnectPoint one, ConnectPoint two) {
+ LinkDescription link = new DefaultLinkDescription(one, two, DIRECT);
+ linkProviderService.linkDetected(link);
+ linkProviderService.linkDetected(reverse(link));
+ }
+
+ @Override
+ public void run() {
+ delay(WAIT_DELAY);
+
+ while (!stopped) {
+ if (mutationRate > 0 && inactiveLinks.isEmpty()) {
+ primeInactiveLinks();
+ } else if (mutationRate <= 0 && !inactiveLinks.isEmpty()) {
+ repairInactiveLinks();
+ } else if (inactiveLinks.isEmpty()) {
+ delay(WAIT_DELAY);
+
+ } else {
+ activeLinks.add(repairLink());
+ pause();
+ inactiveLinks.add(severLink());
+ pause();
+ }
+ }
+ }
+
+ // Primes the inactive links with a few random links.
+ private void primeInactiveLinks() {
+ for (int i = 0, n = Math.min(MAX_DOWN_LINKS, activeLinks.size()); i < n; i++) {
+ inactiveLinks.add(severLink());
+ }
+ }
+
+ // Repairs all inactive links.
+ private void repairInactiveLinks() {
+ while (!inactiveLinks.isEmpty()) {
+ repairLink();
+ }
+ }
+
+ // Picks a random active link and severs it.
+ private LinkDescription severLink() {
+ LinkDescription link = getRandomLink(activeLinks);
+ linkProviderService.linkVanished(link);
+ linkProviderService.linkVanished(reverse(link));
+ return link;
+ }
+
+ // Picks a random inactive link and repairs it.
+ private LinkDescription repairLink() {
+ LinkDescription link = getRandomLink(inactiveLinks);
+ linkProviderService.linkDetected(link);
+ linkProviderService.linkDetected(reverse(link));
+ return link;
+ }
+
+ // Produces a reverse of the specified link.
+ private LinkDescription reverse(LinkDescription link) {
+ return new DefaultLinkDescription(link.dst(), link.src(), link.type());
+ }
+
+ // Returns a random link from the specified list of links.
+ private LinkDescription getRandomLink(List<LinkDescription> links) {
+ return links.remove(random.nextInt(links.size()));
+ }
+
+ // Reduces the given list of links to just a single link in each original pair.
+ private List<LinkDescription> reduceLinks() {
+ List<LinkDescription> links = Lists.newArrayList();
+ linkService.getLinks().forEach(link -> links.add(description(link)));
+ return links.stream()
+ .filter(this::isOurLink)
+ .filter(this::isRightDirection)
+ .collect(Collectors.toList());
+ }
+
+ // Returns true if the specified link is ours.
+ private boolean isOurLink(LinkDescription linkDescription) {
+ return deviceService.getRole(linkDescription.src().deviceId()) == MASTER;
+ }
+
+ // Returns true if the link source is greater than the link destination.
+ private boolean isRightDirection(LinkDescription link) {
+ return link.src().deviceId().toString().compareTo(link.dst().deviceId().toString()) > 0;
+ }
+
+ // Pauses the current thread for the pre-computed time of millis & nanos.
+ private void pause() {
+ delay(millis, nanos);
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
new file mode 100644
index 00000000..9f2320ed
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TopologySimulator.java
@@ -0,0 +1,382 @@
+/*
+ * 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.nil;
+
+import com.google.common.collect.Lists;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Link;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceDescription;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceProviderService;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.link.DefaultLinkDescription;
+import org.onosproject.net.link.LinkProviderService;
+import org.onosproject.net.link.LinkService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import static org.onlab.util.Tools.toHex;
+import static org.onosproject.net.HostId.hostId;
+import static org.onosproject.net.Link.Type.DIRECT;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.net.device.DeviceEvent.Type.*;
+import static org.onosproject.provider.nil.NullProviders.SCHEME;
+
+/**
+ * Abstraction of a provider capable to simulate some network topology.
+ */
+public abstract class TopologySimulator {
+
+ protected final Logger log = LoggerFactory.getLogger(getClass());
+
+ protected String[] topoShape;
+ protected int deviceCount;
+ protected int hostCount;
+
+ protected ServiceDirectory directory;
+ protected NodeId localNode;
+
+ protected ClusterService clusterService;
+ protected MastershipService mastershipService;
+
+ protected DeviceAdminService deviceService;
+ protected HostService hostService;
+ protected LinkService linkService;
+
+ protected DeviceProviderService deviceProviderService;
+ protected HostProviderService hostProviderService;
+ protected LinkProviderService linkProviderService;
+
+ protected int maxWaitSeconds = 1;
+ protected int infrastructurePorts = 2;
+ protected CountDownLatch deviceLatch;
+
+ protected final List<DeviceId> deviceIds = Lists.newArrayList();
+
+ private DeviceListener deviceEventCounter = new DeviceEventCounter();
+
+ /**
+ * Initializes a new topology simulator with access to the specified service
+ * directory and various provider services.
+ *
+ * @param topoShape topology shape specifier
+ * @param deviceCount number of devices in the topology
+ * @param hostCount number of hosts per device
+ * @param directory service directory
+ * @param deviceProviderService device provider service
+ * @param hostProviderService host provider service
+ * @param linkProviderService link provider service
+ */
+ protected void init(String topoShape, int deviceCount, int hostCount,
+ ServiceDirectory directory,
+ DeviceProviderService deviceProviderService,
+ HostProviderService hostProviderService,
+ LinkProviderService linkProviderService) {
+ this.deviceCount = deviceCount;
+ this.hostCount = hostCount;
+ this.directory = directory;
+
+ this.clusterService = directory.get(ClusterService.class);
+ this.mastershipService = directory.get(MastershipService.class);
+
+ this.deviceService = directory.get(DeviceAdminService.class);
+ this.hostService = directory.get(HostService.class);
+ this.linkService = directory.get(LinkService.class);
+ this.deviceProviderService = deviceProviderService;
+ this.hostProviderService = hostProviderService;
+ this.linkProviderService = linkProviderService;
+
+ localNode = clusterService.getLocalNode().id();
+
+ processTopoShape(topoShape);
+ }
+
+ /**
+ * Processes the topology shape specifier.
+ *
+ * @param shape topology shape specifier
+ */
+ protected void processTopoShape(String shape) {
+ this.topoShape = shape.split(",");
+ }
+
+ /**
+ * Sets up network topology simulation.
+ */
+ public void setUpTopology() {
+ prepareForDeviceEvents(deviceCount);
+ createDevices();
+ waitForDeviceEvents();
+
+ createLinks();
+ createHosts();
+ }
+
+ /**
+ * Creates simulated devices.
+ */
+ protected void createDevices() {
+ for (int i = 0; i < deviceCount; i++) {
+ createDevice(i + 1);
+ }
+ }
+
+ /**
+ * Creates simulated links.
+ */
+ protected abstract void createLinks();
+
+ /**
+ * Creates simulated hosts.
+ */
+ protected abstract void createHosts();
+
+ /**
+ * Creates simulated device.
+ *
+ * @param i index of the device id in the list.
+ */
+ protected void createDevice(int i) {
+ DeviceId id = DeviceId.deviceId(SCHEME + ":" + toHex(i));
+ DeviceDescription desc =
+ new DefaultDeviceDescription(id.uri(), Device.Type.SWITCH,
+ "ON.Lab", "0.1", "0.1", "1234",
+ new ChassisId(i));
+ deviceIds.add(id);
+ deviceProviderService.deviceConnected(id, desc);
+ deviceProviderService.updatePorts(id, buildPorts(hostCount + infrastructurePorts));
+ }
+
+// /**
+// * Creates simulated link between two devices on port 1 and port 2.
+// *
+// * @param i index of one simulated device
+// * @param j index of another simulated device
+// */
+// protected void createLink(int i, int j) {
+// createLink(i, j, 1, 2);
+// }
+
+ /**
+ * Creates simulated link between two devices.
+ *
+ * @param i index of one simulated device
+ * @param j index of another simulated device
+ * @param pi port number of i-th device
+ * @param pj port number of j-th device
+ */
+ protected void createLink(int i, int j, int pi, int pj) {
+ ConnectPoint one = new ConnectPoint(deviceIds.get(i), PortNumber.portNumber(pi));
+ ConnectPoint two = new ConnectPoint(deviceIds.get(j), PortNumber.portNumber(pj));
+ linkProviderService.linkDetected(new DefaultLinkDescription(one, two, DIRECT));
+ linkProviderService.linkDetected(new DefaultLinkDescription(two, one, DIRECT));
+ }
+
+ /**
+ * Creates simularted hosts for the specified device.
+ *
+ * @param deviceId device identifier
+ * @param portOffset port offset where to start attaching hosts
+ */
+ protected void createHosts(DeviceId deviceId, int portOffset) {
+ String s = deviceId.toString();
+ byte dByte = Byte.parseByte(s.substring(s.length() - 1), 16);
+ // TODO: this limits the simulation to 256 devices & 256 hosts/device.
+ byte[] macBytes = new byte[]{0, 0, 0, 0, dByte, 0};
+ byte[] ipBytes = new byte[]{(byte) 192, (byte) 168, dByte, 0};
+
+ for (int i = 0; i < hostCount; i++) {
+ int port = portOffset + i + 1;
+ macBytes[5] = (byte) (i + 1);
+ ipBytes[3] = (byte) (i + 1);
+ HostId id = hostId(MacAddress.valueOf(macBytes), VlanId.NONE);
+ IpAddress ip = IpAddress.valueOf(IpAddress.Version.INET, ipBytes);
+ hostProviderService.hostDetected(id, description(id, ip, deviceId, port));
+ }
+ }
+
+ /**
+ * Prepares to count device added/available/removed events.
+ *
+ * @param count number of events to count
+ */
+ protected void prepareForDeviceEvents(int count) {
+ deviceLatch = new CountDownLatch(count);
+ deviceService.addListener(deviceEventCounter);
+ }
+
+ /**
+ * Waits for all expected device added/available/removed events.
+ */
+ protected void waitForDeviceEvents() {
+ try {
+ deviceLatch.await(maxWaitSeconds, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ log.warn("Device events did not arrive in time");
+ }
+ deviceService.removeListener(deviceEventCounter);
+ }
+
+
+ /**
+ * Tears down network topology simulation.
+ */
+ public void tearDownTopology() {
+ removeHosts();
+ removeLinks();
+ removeDevices();
+ }
+
+ /**
+ * Removes any hosts previously advertised by this provider.
+ */
+ protected void removeHosts() {
+ hostService.getHosts()
+ .forEach(host -> hostProviderService.hostVanished(host.id()));
+ }
+
+ /**
+ * Removes any links previously advertised by this provider.
+ */
+ protected void removeLinks() {
+ linkService.getLinks()
+ .forEach(link -> linkProviderService.linkVanished(description(link)));
+ }
+
+ /**
+ * Removes any devices previously advertised by this provider.
+ */
+ protected void removeDevices() {
+ prepareForDeviceEvents(deviceIds.size());
+ deviceIds.forEach(deviceProviderService::deviceDisconnected);
+ waitForDeviceEvents();
+ deviceIds.clear();
+ }
+
+
+ /**
+ * Produces a device description from the given device.
+ *
+ * @param device device to copy
+ * @return device description
+ */
+ static DeviceDescription description(Device device) {
+ return new DefaultDeviceDescription(device.id().uri(), device.type(),
+ device.manufacturer(),
+ device.hwVersion(), device.swVersion(),
+ device.serialNumber(), device.chassisId());
+ }
+
+ /**
+ * Produces a link description from the given link.
+ *
+ * @param link link to copy
+ * @return link description
+ */
+ static DefaultLinkDescription description(Link link) {
+ return new DefaultLinkDescription(link.src(), link.dst(), link.type());
+ }
+
+ /**
+ * Produces a host description from the given host.
+ *
+ * @param host host to copy
+ * @return host description
+ */
+ static DefaultHostDescription description(Host host) {
+ return new DefaultHostDescription(host.mac(), host.vlan(), host.location(),
+ host.ipAddresses());
+ }
+
+ /**
+ * Generates a host description from the given id and location information.
+ *
+ * @param hostId host identifier
+ * @param ip host IP
+ * @param deviceId edge device
+ * @param port edge port
+ * @return host description
+ */
+ static HostDescription description(HostId hostId, IpAddress ip,
+ DeviceId deviceId, int port) {
+ HostLocation location = new HostLocation(deviceId, portNumber(port), 0L);
+ return new DefaultHostDescription(hostId.mac(), hostId.vlanId(), location, ip);
+ }
+
+ /**
+ * Generates a list of a configured number of ports.
+ *
+ * @param portCount number of ports
+ * @return list of ports
+ */
+ protected List<PortDescription> buildPorts(int portCount) {
+ List<PortDescription> ports = Lists.newArrayList();
+ for (int i = 0; i < portCount; i++) {
+ ports.add(new DefaultPortDescription(PortNumber.portNumber(i), true,
+ Port.Type.COPPER, 0));
+ }
+ return ports;
+ }
+
+ /**
+ * Indicates whether or not the simulation knows of this device.
+ *
+ * @param deviceId device identifier
+ * @return true if device is known
+ */
+ public boolean contains(DeviceId deviceId) {
+ return deviceIds.contains(deviceId);
+ }
+
+ // Counts down number of device added/available/removed events.
+ private class DeviceEventCounter implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ DeviceEvent.Type type = event.type();
+ if (type == DEVICE_ADDED || type == DEVICE_REMOVED ||
+ type == DEVICE_AVAILABILITY_CHANGED) {
+ deviceLatch.countDown();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
new file mode 100644
index 00000000..2c049333
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/TreeTopologySimulator.java
@@ -0,0 +1,77 @@
+/*
+ * 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.nil;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Tree topology with hosts at the leaf devices.
+ */
+public class TreeTopologySimulator extends TopologySimulator {
+
+ private int[] tierMultiplier;
+ private int[] tierDeviceCount;
+ private int[] tierOffset;
+
+ @Override
+ protected void processTopoShape(String shape) {
+ super.processTopoShape(shape);
+ tierOffset = new int[topoShape.length];
+ tierMultiplier = new int[topoShape.length];
+ tierDeviceCount = new int[topoShape.length];
+
+ deviceCount = 1;
+
+ tierOffset[0] = 0;
+ tierMultiplier[0] = 1;
+ tierDeviceCount[0] = deviceCount;
+
+ for (int i = 1; i < topoShape.length; i++) {
+ tierOffset[i] = deviceCount;
+ tierMultiplier[i] = Integer.parseInt(topoShape[i]);
+ tierDeviceCount[i] = tierDeviceCount[i - 1] * tierMultiplier[i];
+ deviceCount = deviceCount + tierDeviceCount[i];
+ }
+ }
+
+ @Override
+ public void setUpTopology() {
+ checkArgument(tierDeviceCount.length > 0, "There must be at least one tree tier");
+ super.setUpTopology();
+ }
+
+ @Override
+ protected void createLinks() {
+ int portOffset = 1;
+ for (int t = 1; t < tierOffset.length; t++) {
+ int child = tierOffset[t];
+ for (int parent = tierOffset[t - 1]; parent < tierOffset[t]; parent++) {
+ for (int i = 0; i < tierMultiplier[t]; i++) {
+ createLink(parent, child, i + portOffset, 1);
+ child++;
+ }
+ }
+ portOffset = 2; // beyond first tier, allow for up-links
+ }
+ }
+
+ @Override
+ protected void createHosts() {
+ for (int i = tierOffset[tierOffset.length - 1]; i < deviceCount; i++) {
+ createHosts(deviceIds.get(i), hostCount);
+ }
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java
new file mode 100644
index 00000000..261f46de
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullControlCommand.java
@@ -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.
+ */
+package org.onosproject.provider.nil.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.provider.nil.NullProviders;
+
+import static org.onosproject.cli.StartStopCompleter.START;
+
+/**
+ * Starts or stops topology simulation.
+ */
+@Command(scope = "onos", name = "null-simulation",
+ description = "Starts or stops topology simulation")
+public class NullControlCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "cmd", description = "Control command: start/stop",
+ required = true, multiValued = false)
+ String cmd = null;
+
+ @Argument(index = 1, name = "topoShape",
+ description = "Topology shape: e.g. configured, linear, reroute, centipede, tree, spineleaf, mesh",
+ required = false, multiValued = false)
+ String topoShape = null;
+
+ @Override
+ protected void execute() {
+ ComponentConfigService service = get(ComponentConfigService.class);
+ if (topoShape != null) {
+ service.setProperty(NullProviders.class.getName(), "topoShape", topoShape);
+ }
+ service.setProperty(NullProviders.class.getName(), "enabled",
+ cmd.equals(START) ? "true" : "false");
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
new file mode 100644
index 00000000..a76da3b8
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/NullLinkCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.nil.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.provider.nil.NullProviders;
+
+import static org.onosproject.cli.UpDownCompleter.DOWN;
+import static org.onosproject.cli.UpDownCompleter.UP;
+
+/**
+ * Servers or repairs a simulated link.
+ */
+@Command(scope = "onos", name = "null-link",
+ description = "Severs or repairs a simulated link")
+public class NullLinkCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "one", description = "One link end-point as device/port",
+ required = true, multiValued = false)
+ String one = null;
+
+ @Argument(index = 1, name = "two", description = "Another link end-point as device/port",
+ required = true, multiValued = false)
+ String two = null;
+
+ @Argument(index = 2, name = "cmd", description = "up/down",
+ required = true, multiValued = false)
+ String cmd = null;
+
+
+ @Override
+ protected void execute() {
+ NullProviders service = get(NullProviders.class);
+
+ try {
+ ConnectPoint onePoint = ConnectPoint.deviceConnectPoint(one);
+
+ ConnectPoint twoPoint = ConnectPoint.deviceConnectPoint(two);
+
+ if (cmd.equals(UP)) {
+ service.repairLink(onePoint, twoPoint);
+ } else if (cmd.equals(DOWN)) {
+ service.severLink(onePoint, twoPoint);
+ } else {
+ error("Illegal command %s; must be up or down", cmd);
+ }
+ } catch (NumberFormatException e) {
+ error("Invalid port number specified", e);
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
new file mode 100644
index 00000000..fad29b6e
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/TopologyShapeCompleter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.nil.cli;
+
+import com.google.common.collect.ImmutableList;
+import org.onosproject.cli.AbstractChoicesCompleter;
+
+import java.util.List;
+
+/**
+ * Topology shape completer.
+ */
+public class TopologyShapeCompleter extends AbstractChoicesCompleter {
+ @Override
+ public List<String> choices() {
+ return ImmutableList.of("configured", "linear", "reroute", "centipede",
+ "tree", "spineleaf", "mesh");
+ }
+}
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java
new file mode 100644
index 00000000..f876ced3
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Null provider CLI commands and completers.
+ */
+package org.onosproject.provider.nil.cli;
diff --git a/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java
new file mode 100644
index 00000000..e8d309f7
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/java/org/onosproject/provider/nil/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+/**
+ * Set of null south-bound providers which permit simulating a network
+ * topology using fake devices, links, hosts, etc.
+ */
+package org.onosproject.provider.nil;
diff --git a/framework/src/onos/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..f96f10cc
--- /dev/null
+++ b/framework/src/onos/providers/null/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,44 @@
+<!--
+ ~ 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.
+ -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+ <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+ <command>
+ <action class="org.onosproject.provider.nil.cli.NullControlCommand"/>
+ <completers>
+ <ref component-id="startStopCompleter"/>
+ <ref component-id="topoShapeCompleter"/>
+ <null/>
+ </completers>
+ </command>
+ <command>
+ <action class="org.onosproject.provider.nil.cli.NullLinkCommand"/>
+ <completers>
+ <ref component-id="linkSrcCompleter"/>
+ <ref component-id="linkDstCompleter"/>
+ <ref component-id="upDownCompleter"/>
+ <null/>
+ </completers>
+ </command>
+ </command-bundle>
+
+ <bean id="startStopCompleter" class="org.onosproject.cli.StartStopCompleter"/>
+ <bean id="upDownCompleter" class="org.onosproject.cli.UpDownCompleter"/>
+ <bean id="topoShapeCompleter" class="org.onosproject.provider.nil.cli.TopologyShapeCompleter"/>
+ <bean id="linkSrcCompleter" class="org.onosproject.cli.net.LinkSrcCompleter"/>
+ <bean id="linkDstCompleter" class="org.onosproject.cli.net.LinkDstCompleter"/>
+
+</blueprint>
diff --git a/framework/src/onos/providers/openflow/app/app.xml b/framework/src/onos/providers/openflow/app/app.xml
new file mode 100644
index 00000000..e54d1a8a
--- /dev/null
+++ b/framework/src/onos/providers/openflow/app/app.xml
@@ -0,0 +1,33 @@
+<?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.openflow" 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-lldp-provider/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-host-provider/${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/app/features.xml b/framework/src/onos/providers/openflow/app/features.xml
new file mode 100644
index 00000000..7c410172
--- /dev/null
+++ b/framework/src/onos/providers/openflow/app/features.xml
@@ -0,0 +1,34 @@
+<?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}">
+ <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>
+ <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-lldp-provider/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-host-provider/${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/app/pom.xml b/framework/src/onos/providers/openflow/app/pom.xml
new file mode 100644
index 00000000..62e5eb87
--- /dev/null
+++ b/framework/src/onos/providers/openflow/app/pom.xml
@@ -0,0 +1,81 @@
+<?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.
+ -->
+<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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-openflow</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>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-lldp-provider</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-host-provider</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/openflow/device/pom.xml b/framework/src/onos/providers/openflow/device/pom.xml
new file mode 100644
index 00000000..5f27d42b
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-provider-device</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS OpenFlow protocol device provider</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
new file mode 100644
index 00000000..cb19dc52
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -0,0 +1,569 @@
+/*
+ * 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.of.device.impl;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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.ChassisId;
+import org.onlab.util.Frequency;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onlab.util.Spectrum;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.GridType;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.OduSignalType;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+import org.onosproject.net.device.DefaultPortStatistics;
+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.OchPortDescription;
+import org.onosproject.net.device.OmsPortDescription;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.device.PortStatistics;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowOpticalSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.PortDescPropertyType;
+import org.onosproject.openflow.controller.RoleState;
+import org.osgi.service.component.ComponentContext;
+import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortConfig;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortDescPropOpticalTransport;
+import org.projectfloodlight.openflow.protocol.OFPortFeatures;
+import org.projectfloodlight.openflow.protocol.OFPortOptical;
+import org.projectfloodlight.openflow.protocol.OFPortReason;
+import org.projectfloodlight.openflow.protocol.OFPortState;
+import org.projectfloodlight.openflow.protocol.OFPortStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFPortStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsReplyFlags;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.types.PortSpeed;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.Port.Type.COPPER;
+import static org.onosproject.net.Port.Type.FIBER;
+import static org.onosproject.openflow.controller.Dpid.dpid;
+import static org.onosproject.openflow.controller.Dpid.uri;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to detect network
+ * infrastructure devices.
+ */
+@Component(immediate = true)
+public class OpenFlowDeviceProvider extends AbstractProvider implements DeviceProvider {
+
+ private static final Logger LOG = getLogger(OpenFlowDeviceProvider.class);
+ private static final long MBPS = 1_000 * 1_000;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private DeviceProviderService providerService;
+
+ private final InternalDeviceProvider listener = new InternalDeviceProvider();
+
+ // TODO: We need to make the poll interval configurable.
+ static final int POLL_INTERVAL = 5;
+ @Property(name = "PortStatsPollFrequency", intValue = POLL_INTERVAL,
+ label = "Frequency (in seconds) for polling switch Port statistics")
+ private int portStatsPollFrequency = POLL_INTERVAL;
+
+ private HashMap<Dpid, PortStatsCollector> collectors = Maps.newHashMap();
+
+ /**
+ * Creates an OpenFlow device provider.
+ */
+ public OpenFlowDeviceProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.openflow"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+ providerService = providerRegistry.register(this);
+ controller.addListener(listener);
+ controller.addEventListener(listener);
+ for (OpenFlowSwitch sw : controller.getSwitches()) {
+ try {
+ listener.switchAdded(new Dpid(sw.getId()));
+ } catch (Exception e) {
+ LOG.warn("Failed initially adding {} : {}", sw.getStringId(), e.getMessage());
+ LOG.debug("Error details:", e);
+ // disconnect to trigger switch-add later
+ sw.disconnectSwitch();
+ }
+ PortStatsCollector psc = new PortStatsCollector(sw, portStatsPollFrequency);
+ psc.start();
+ collectors.put(new Dpid(sw.getId()), psc);
+ }
+ LOG.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ providerRegistry.unregister(this);
+ controller.removeListener(listener);
+ collectors.values().forEach(PortStatsCollector::stop);
+ providerService = null;
+ LOG.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ int newPortStatsPollFrequency;
+ try {
+ String s = get(properties, "PortStatsPollFrequency");
+ newPortStatsPollFrequency = isNullOrEmpty(s) ? portStatsPollFrequency : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException | ClassCastException e) {
+ newPortStatsPollFrequency = portStatsPollFrequency;
+ }
+
+ if (newPortStatsPollFrequency != portStatsPollFrequency) {
+ portStatsPollFrequency = newPortStatsPollFrequency;
+ collectors.values().forEach(psc -> psc.adjustPollInterval(portStatsPollFrequency));
+ }
+
+ LOG.info("Settings: portStatsPollFrequency={}", portStatsPollFrequency);
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ OpenFlowSwitch sw = controller.getSwitch(dpid(deviceId.uri()));
+ if (sw == null || !sw.isConnected()) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ LOG.debug("Triggering probe on device {}", deviceId);
+
+ final Dpid dpid = dpid(deviceId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (sw == null || !sw.isConnected()) {
+ LOG.error("Failed to probe device {} on sw={}", deviceId, sw);
+ providerService.deviceDisconnected(deviceId);
+ return;
+ } else {
+ LOG.trace("Confirmed device {} connection", deviceId);
+ }
+
+ // Prompt an update of port information. We can use any XID for this.
+ OFFactory fact = sw.factory();
+ switch (fact.getVersion()) {
+ case OF_10:
+ sw.sendMsg(fact.buildFeaturesRequest().setXid(0).build());
+ break;
+ case OF_13:
+ sw.sendMsg(fact.buildPortDescStatsRequest().setXid(0).build());
+ break;
+ default:
+ LOG.warn("Unhandled protocol version");
+ }
+ }
+
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ switch (newRole) {
+ case MASTER:
+ controller.setRole(dpid(deviceId.uri()), RoleState.MASTER);
+ break;
+ case STANDBY:
+ controller.setRole(dpid(deviceId.uri()), RoleState.EQUAL);
+ break;
+ case NONE:
+ controller.setRole(dpid(deviceId.uri()), RoleState.SLAVE);
+ break;
+ default:
+ LOG.error("Unknown Mastership state : {}", newRole);
+
+ }
+ LOG.debug("Accepting mastership role change for device {}", deviceId);
+ }
+
+ private void pushPortMetrics(Dpid dpid, List<OFPortStatsEntry> portStatsEntries) {
+ DeviceId deviceId = DeviceId.deviceId(dpid.uri(dpid));
+ Collection<PortStatistics> stats = buildPortStatistics(deviceId, portStatsEntries);
+ providerService.updatePortStatistics(deviceId, stats);
+ }
+
+ private Collection<PortStatistics> buildPortStatistics(DeviceId deviceId,
+ List<OFPortStatsEntry> entries) {
+ HashSet<PortStatistics> stats = Sets.newHashSet();
+
+ for (OFPortStatsEntry entry : entries) {
+ try {
+ if (entry == null || entry.getPortNo() == null || entry.getPortNo().getPortNumber() < 0) {
+ continue;
+ }
+ DefaultPortStatistics.Builder builder = DefaultPortStatistics.builder();
+ DefaultPortStatistics stat = builder.setDeviceId(deviceId)
+ .setPort(entry.getPortNo().getPortNumber())
+ .setPacketsReceived(entry.getRxPackets().getValue())
+ .setPacketsSent(entry.getTxPackets().getValue())
+ .setBytesReceived(entry.getRxBytes().getValue())
+ .setBytesSent(entry.getTxBytes().getValue())
+ .setPacketsRxDropped(entry.getRxDropped().getValue())
+ .setPacketsTxDropped(entry.getTxDropped().getValue())
+ .setPacketsRxErrors(entry.getRxErrors().getValue())
+ .setPacketsTxErrors(entry.getTxErrors().getValue())
+ .setDurationSec(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationSec())
+ .setDurationNano(entry.getVersion() == OFVersion.OF_10 ? 0 : entry.getDurationNsec())
+ .build();
+
+ stats.add(stat);
+ } catch (Exception e) {
+ LOG.warn("Unable to process port stats", e);
+ }
+ }
+
+ return Collections.unmodifiableSet(stats);
+
+ }
+
+ private class InternalDeviceProvider implements OpenFlowSwitchListener, OpenFlowEventListener {
+
+ private HashMap<Dpid, List<OFPortStatsEntry>> portStatsReplies = new HashMap<>();
+
+ @Override
+ public void switchAdded(Dpid dpid) {
+ if (providerService == null) {
+ return;
+ }
+ DeviceId did = deviceId(uri(dpid));
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
+ ChassisId cId = new ChassisId(dpid.value());
+
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("protocol", sw.factory().getVersion().toString())
+ .set("channelId", sw.channelId())
+ .build();
+
+ DeviceDescription description =
+ new DefaultDeviceDescription(did.uri(), sw.deviceType(),
+ sw.manufacturerDescription(),
+ sw.hardwareDescription(),
+ sw.softwareDescription(),
+ sw.serialNumber(),
+ cId, annotations);
+ providerService.deviceConnected(did, description);
+ providerService.updatePorts(did, buildPortDescriptions(sw));
+
+ PortStatsCollector psc =
+ new PortStatsCollector(controller.getSwitch(dpid), portStatsPollFrequency);
+ psc.start();
+ collectors.put(dpid, psc);
+ }
+
+ @Override
+ public void switchRemoved(Dpid dpid) {
+ if (providerService == null) {
+ return;
+ }
+ providerService.deviceDisconnected(deviceId(uri(dpid)));
+
+ PortStatsCollector collector = collectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ }
+
+ @Override
+ public void switchChanged(Dpid dpid) {
+ if (providerService == null) {
+ return;
+ }
+ DeviceId did = deviceId(uri(dpid));
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ providerService.updatePorts(did, buildPortDescriptions(sw));
+ }
+
+ @Override
+ public void portChanged(Dpid dpid, OFPortStatus status) {
+ PortDescription portDescription = buildPortDescription(status);
+ providerService.portStatusChanged(deviceId(uri(dpid)), portDescription);
+ }
+
+ @Override
+ public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+ MastershipRole request = roleOf(requested);
+ MastershipRole reply = roleOf(response);
+
+ providerService.receivedRoleReply(deviceId(uri(dpid)), request, reply);
+ }
+
+ /**
+ * Translates a RoleState to the corresponding MastershipRole.
+ *
+ * @param response role state
+ * @return a MastershipRole
+ */
+ private MastershipRole roleOf(RoleState response) {
+ switch (response) {
+ case MASTER:
+ return MastershipRole.MASTER;
+ case EQUAL:
+ return MastershipRole.STANDBY;
+ case SLAVE:
+ return MastershipRole.NONE;
+ default:
+ LOG.warn("unknown role {}", response);
+ return null;
+ }
+ }
+
+ /**
+ * Builds a list of port descriptions for a given list of ports.
+ *
+ * @return list of portdescriptions
+ */
+ private List<PortDescription> buildPortDescriptions(OpenFlowSwitch sw) {
+ final List<PortDescription> portDescs = new ArrayList<>(sw.getPorts().size());
+ sw.getPorts().forEach(port -> portDescs.add(buildPortDescription(port)));
+
+ OpenFlowOpticalSwitch opsw;
+ switch (sw.deviceType()) {
+ case ROADM:
+ opsw = (OpenFlowOpticalSwitch) sw;
+ opsw.getPortTypes().forEach(type -> {
+ opsw.getPortsOf(type).forEach(
+ op -> {
+ portDescs.add(buildPortDescription(type, (OFPortOptical) op));
+ }
+ );
+ });
+ break;
+ case FIBER_SWITCH:
+ opsw = (OpenFlowOpticalSwitch) sw;
+ opsw.getPortTypes().forEach(type -> {
+ opsw.getPortsOf(type).forEach(
+ op -> {
+ portDescs.add(buildPortDescription((OFCalientPortDescStatsEntry) op));
+ }
+ );
+ });
+ break;
+ default:
+ break;
+ }
+
+ return portDescs;
+ }
+
+ /**
+ * Creates an annotation for the port name if one is available.
+ *
+ * @param port description of the port
+ * @return annotation containing the port name if one is found,
+ * null otherwise
+ */
+ private SparseAnnotations makePortNameAnnotation(String port) {
+ SparseAnnotations annotations = null;
+ String portName = Strings.emptyToNull(port);
+ if (portName != null) {
+ annotations = DefaultAnnotations.builder()
+ .set(AnnotationKeys.PORT_NAME, portName).build();
+ }
+ return annotations;
+ }
+
+ /**
+ * Build a portDescription from a given Ethernet port description.
+ *
+ * @param port the port to build from.
+ * @return portDescription for the port.
+ */
+ private PortDescription buildPortDescription(OFPortDesc port) {
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+ boolean enabled =
+ !port.getState().contains(OFPortState.LINK_DOWN) &&
+ !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+ Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
+ SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+ return new DefaultPortDescription(portNo, enabled, type,
+ portSpeed(port), annotations);
+ }
+
+ /**
+ * Build a portDescription from a given a port description describing some
+ * Optical port.
+ *
+ * @param port description property type.
+ * @param port the port to build from.
+ * @return portDescription for the port.
+ */
+ private PortDescription buildPortDescription(PortDescPropertyType ptype, OFPortOptical port) {
+ checkArgument(port.getDesc().size() >= 1);
+
+ // Minimally functional fixture. This needs to be fixed as we add better support.
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+
+ boolean enabled = !port.getState().contains(OFPortState.LINK_DOWN)
+ && !port.getConfig().contains(OFPortConfig.PORT_DOWN);
+ SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+
+ if (port.getVersion() == OFVersion.OF_13
+ && ptype == PortDescPropertyType.OPTICAL_TRANSPORT) {
+ // At this point, not much is carried in the optical port message.
+ LOG.debug("Optical transport port message {}", port.toString());
+ } else {
+ // removable once 1.4+ support complete.
+ LOG.debug("Unsupported optical port properties");
+ }
+
+ OFPortDescPropOpticalTransport desc = port.getDesc().get(0);
+ switch (desc.getPortSignalType()) {
+ // FIXME: use constants once loxi has full optical extensions
+ case 2: // OMS port
+ // Assume complete optical spectrum and 50 GHz grid
+ // LINC-OE is only supported optical OF device for now
+ return new OmsPortDescription(portNo, enabled,
+ Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(50), annotations);
+ case 5: // OCH port
+ OchSignal signal = new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, 0, 4);
+ return new OchPortDescription(portNo, enabled, OduSignalType.ODU4,
+ true, signal, annotations);
+ default:
+ break;
+ }
+
+ return new DefaultPortDescription(portNo, enabled, FIBER, 0, annotations);
+ }
+
+ /**
+ * Build a portDescription from a given port description describing a fiber switch optical port.
+ *
+ * @param port description property type.
+ * @param port the port to build from.
+ * @return portDescription for the port.
+ */
+ private PortDescription buildPortDescription(OFCalientPortDescStatsEntry port) {
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+
+ // FIXME when Calient OF agent reports port status
+ boolean enabled = true;
+ SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+
+ // S160 data sheet
+ // Wavelength range: 1260 - 1630 nm, grid is irrelevant for this type of switch
+ return new OmsPortDescription(portNo, enabled,
+ Spectrum.U_BAND_MIN, Spectrum.O_BAND_MAX, Frequency.ofGHz(100), annotations);
+ }
+
+ private PortDescription buildPortDescription(OFPortStatus status) {
+ OFPortDesc port = status.getDesc();
+ if (status.getReason() != OFPortReason.DELETE) {
+ return buildPortDescription(port);
+ } else {
+ PortNumber portNo = PortNumber.portNumber(port.getPortNo().getPortNumber());
+ Port.Type type = port.getCurr().contains(OFPortFeatures.PF_FIBER) ? FIBER : COPPER;
+ SparseAnnotations annotations = makePortNameAnnotation(port.getName());
+ return new DefaultPortDescription(portNo, false, type,
+ portSpeed(port), annotations);
+ }
+ }
+
+ private long portSpeed(OFPortDesc port) {
+ if (port.getVersion() == OFVersion.OF_13) {
+ return port.getCurrSpeed() / MBPS;
+ }
+
+ PortSpeed portSpeed = PortSpeed.SPEED_NONE;
+ for (OFPortFeatures feat : port.getCurr()) {
+ portSpeed = PortSpeed.max(portSpeed, feat.getPortSpeed());
+ }
+ return portSpeed.getSpeedBps() / MBPS;
+ }
+
+ @Override
+ public void handleMessage(Dpid dpid, OFMessage msg) {
+ switch (msg.getType()) {
+ case STATS_REPLY:
+ if (((OFStatsReply) msg).getStatsType() == OFStatsType.PORT) {
+ OFPortStatsReply portStatsReply = (OFPortStatsReply) msg;
+ List<OFPortStatsEntry> portStatsReplyList = portStatsReplies.get(dpid);
+ if (portStatsReplyList == null) {
+ portStatsReplyList = Lists.newArrayList();
+ }
+ portStatsReplyList.addAll(portStatsReply.getEntries());
+ portStatsReplies.put(dpid, portStatsReplyList);
+ if (!portStatsReply.getFlags().contains(OFStatsReplyFlags.REPLY_MORE)) {
+ pushPortMetrics(dpid, portStatsReplies.get(dpid));
+ portStatsReplies.get(dpid).clear();
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java
new file mode 100644
index 00000000..8383fa3f
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/PortStatsCollector.java
@@ -0,0 +1,113 @@
+/*
+ * 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.of.device.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFPortStatsRequest;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Sends Group Stats Request and collect the group statistics with a time interval.
+ */
+public class PortStatsCollector implements TimerTask {
+
+ // TODO: Refactoring is required using ScheduledExecutorService
+
+ private final HashedWheelTimer timer = Timer.getTimer();
+ private final OpenFlowSwitch sw;
+ private final Logger log = getLogger(getClass());
+ private int refreshInterval;
+ private final AtomicLong xidAtomic = new AtomicLong(1);
+
+ private Timeout timeout;
+ private volatile boolean stopped;
+
+ /**
+ * Creates a GroupStatsCollector object.
+ *
+ * @param sw Open Flow switch
+ * @param interval time interval for collecting group statistic
+ */
+ public PortStatsCollector(OpenFlowSwitch sw, int interval) {
+ this.sw = sw;
+ this.refreshInterval = interval;
+ }
+
+ @Override
+ public void run(Timeout to) throws Exception {
+ if (stopped || timeout.isCancelled()) {
+ return;
+ }
+ log.trace("Collecting stats for {}", sw.getStringId());
+
+ sendPortStatistic();
+
+ if (!stopped && !timeout.isCancelled()) {
+ log.trace("Scheduling stats collection in {} seconds for {}",
+ this.refreshInterval, this.sw.getStringId());
+ timeout.getTimer().newTimeout(this, refreshInterval, TimeUnit.SECONDS);
+ }
+ }
+
+ synchronized void adjustPollInterval(int pollInterval) {
+ this.refreshInterval = pollInterval;
+ // task.cancel();
+ // task = new InternalTimerTask();
+ // timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000);
+ }
+
+ private void sendPortStatistic() {
+ if (sw.getRole() != RoleState.MASTER) {
+ return;
+ }
+ Long statsXid = xidAtomic.getAndIncrement();
+ OFPortStatsRequest statsRequest = sw.factory().buildPortStatsRequest()
+ .setPortNo(OFPort.ANY)
+ .setXid(statsXid)
+ .build();
+ sw.sendMsg(statsRequest);
+ }
+
+ /**
+ * Starts the collector.
+ */
+ public synchronized void start() {
+ log.info("Starting Port Stats collection thread for {}", sw.getStringId());
+ stopped = false;
+ timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the collector.
+ */
+ public synchronized void stop() {
+ log.info("Stopping Port Stats collection thread for {}", sw.getStringId());
+ stopped = true;
+ timeout.cancel();
+ }
+}
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java
new file mode 100644
index 00000000..9376b47d
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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 OpenFlow controller as a means of infrastructure device discovery.
+ */
+package org.onosproject.provider.of.device.impl;
diff --git a/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
new file mode 100644
index 00000000..7b4d7922
--- /dev/null
+++ b/framework/src/onos/providers/openflow/device/src/test/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProviderTest.java
@@ -0,0 +1,402 @@
+/*
+ * 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.of.device.impl;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+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;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.PacketListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFPortReason;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
+import org.projectfloodlight.openflow.types.OFPort;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+import static org.onosproject.net.Device.Type.SWITCH;
+import static org.onosproject.net.MastershipRole.*;
+
+public class OpenFlowDeviceProviderTest {
+
+ private static final ProviderId PID = new ProviderId("of", "test");
+ private static final DeviceId DID1 = DeviceId.deviceId("of:0000000000000001");
+ private static final Dpid DPID1 = Dpid.dpid(DID1.uri());
+
+ private static final OFPortDesc PD1 = portDesc(1);
+ private static final OFPortDesc PD2 = portDesc(2);
+ private static final OFPortDesc PD3 = portDesc(3);
+
+ private static final List<OFPortDesc> PLIST = Lists.newArrayList(PD1, PD2);
+
+ private static final Device DEV1 =
+ new DefaultDevice(PID, DID1, SWITCH, "", "", "", "", null);
+
+ private static final TestOpenFlowSwitch SW1 = new TestOpenFlowSwitch();
+
+ private final OpenFlowDeviceProvider provider = new OpenFlowDeviceProvider();
+ private final TestDeviceRegistry registry = new TestDeviceRegistry();
+ private final TestController controller = new TestController();
+
+ @Before
+ public void startUp() {
+ provider.providerRegistry = registry;
+ provider.controller = controller;
+ provider.cfgService = new ComponentConfigAdapter();
+ controller.switchMap.put(DPID1, SW1);
+ provider.activate(null);
+ assertNotNull("provider should be registered", registry.provider);
+ assertNotNull("listener should be registered", controller.listener);
+ assertEquals("devices not added", 1, registry.connected.size());
+ assertEquals("ports not added", 2, registry.ports.get(DID1).size());
+ }
+
+ @After
+ public void tearDown() {
+ provider.deactivate(null);
+ assertNull("listener should be removed", controller.listener);
+ provider.controller = null;
+ provider.providerRegistry = null;
+ }
+
+ @Test
+ public void roleChanged() {
+ provider.roleChanged(DID1, MASTER);
+ assertEquals("Should be MASTER", RoleState.MASTER, controller.roleMap.get(DPID1));
+ provider.roleChanged(DID1, STANDBY);
+ assertEquals("Should be EQUAL", RoleState.EQUAL, controller.roleMap.get(DPID1));
+ provider.roleChanged(DID1, NONE);
+ assertEquals("Should be SLAVE", RoleState.SLAVE, controller.roleMap.get(DPID1));
+ }
+
+ @Test
+ public void triggerProbe() {
+
+ }
+
+ @Test
+ public void switchRemoved() {
+ controller.listener.switchRemoved(DPID1);
+ assertTrue("device not removed", registry.connected.isEmpty());
+ }
+
+ @Test
+ public void portChanged() {
+ OFPortStatus stat = SW1.factory().buildPortStatus()
+ .setReason(OFPortReason.ADD)
+ .setDesc(PD3)
+ .build();
+ controller.listener.portChanged(DPID1, stat);
+ assertNotNull("never went throught the provider service", registry.descr);
+ assertEquals("port status unhandled", 3, registry.ports.get(DID1).size());
+ }
+
+ @Test
+ public void receivedRoleReply() {
+ // check translation capabilities
+ controller.listener.receivedRoleReply(DPID1, RoleState.MASTER, RoleState.MASTER);
+ assertEquals("wrong role reported", DPID1, registry.roles.get(MASTER));
+ controller.listener.receivedRoleReply(DPID1, RoleState.EQUAL, RoleState.MASTER);
+ assertEquals("wrong role reported", DPID1, registry.roles.get(STANDBY));
+ controller.listener.receivedRoleReply(DPID1, RoleState.SLAVE, RoleState.MASTER);
+ assertEquals("wrong role reported", DPID1, registry.roles.get(NONE));
+ }
+
+ private static OFPortDesc portDesc(int port) {
+ OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc();
+ builder.setPortNo(OFPort.of(port));
+
+ return builder.build();
+ }
+
+ private class TestDeviceRegistry implements DeviceProviderRegistry {
+ DeviceProvider provider;
+
+ Set<DeviceId> connected = new HashSet<>();
+ Multimap<DeviceId, PortDescription> ports = HashMultimap.create();
+ PortDescription descr = null;
+ Map<MastershipRole, Dpid> roles = new HashMap<>();
+
+ @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) {
+ connected.add(deviceId);
+ }
+
+ @Override
+ public void deviceDisconnected(DeviceId deviceId) {
+ connected.remove(deviceId);
+ ports.removeAll(deviceId);
+ }
+
+ @Override
+ public void updatePorts(DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+ for (PortDescription p : portDescriptions) {
+ ports.put(deviceId, p);
+ }
+ }
+
+ @Override
+ public void portStatusChanged(DeviceId deviceId,
+ PortDescription portDescription) {
+ ports.put(deviceId, portDescription);
+ descr = portDescription;
+ }
+
+ @Override
+ public void receivedRoleReply(DeviceId deviceId,
+ MastershipRole requested, MastershipRole response) {
+ roles.put(requested, Dpid.dpid(deviceId.uri()));
+ }
+
+ @Override
+ public void updatePortStatistics(DeviceId deviceId, Collection<PortStatistics> portStatistics) {
+
+ }
+
+ }
+ }
+
+ private class TestController implements OpenFlowController {
+ OpenFlowSwitchListener listener = null;
+ Map<Dpid, RoleState> roleMap = new HashMap<Dpid, RoleState>();
+ Map<Dpid, OpenFlowSwitch> switchMap = new HashMap<Dpid, OpenFlowSwitch>();
+
+ @Override
+ public Iterable<OpenFlowSwitch> getSwitches() {
+ return switchMap.values();
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getMasterSwitches() {
+ return null;
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getEqualSwitches() {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getSwitch(Dpid dpid) {
+ return switchMap.get(dpid);
+ }
+
+ @Override
+ public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
+
+ return null;
+ }
+
+ @Override
+ public void addListener(OpenFlowSwitchListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void removeListener(OpenFlowSwitchListener listener) {
+ this.listener = null;
+ }
+
+ @Override
+ public void addPacketListener(int priority, PacketListener listener) {
+ }
+
+ @Override
+ public void removePacketListener(PacketListener listener) {
+ }
+
+ @Override
+ public void addEventListener(OpenFlowEventListener listener) {
+ }
+
+ @Override
+ public void removeEventListener(OpenFlowEventListener listener) {
+ }
+
+ @Override
+ public void write(Dpid dpid, OFMessage msg) {
+ }
+
+ @Override
+ public void processPacket(Dpid dpid, OFMessage msg) {
+ }
+
+ @Override
+ public void setRole(Dpid dpid, RoleState role) {
+ roleMap.put(dpid, role);
+ }
+ }
+
+ private static class TestOpenFlowSwitch implements OpenFlowSwitch {
+
+ RoleState state;
+ List<OFMessage> sent = new ArrayList<OFMessage>();
+ OFFactory factory = OFFactoryVer10.INSTANCE;
+
+ @Override
+ public void sendMsg(OFMessage msg) {
+ sent.add(msg);
+ }
+
+ @Override
+ public void sendMsg(List<OFMessage> msgs) {
+ }
+
+ @Override
+ public void handleMessage(OFMessage fromSwitch) {
+ }
+
+ @Override
+ public void setRole(RoleState role) {
+ state = role;
+ }
+
+ @Override
+ public RoleState getRole() {
+ return state;
+ }
+
+ @Override
+ public List<OFPortDesc> getPorts() {
+ return PLIST;
+ }
+
+ @Override
+ public OFFactory factory() {
+ return factory;
+ }
+
+ @Override
+ public String getStringId() {
+ return null;
+ }
+
+ @Override
+ public long getId() {
+ return DPID1.value();
+ }
+
+ @Override
+ public String manufacturerDescription() {
+ return null;
+ }
+
+ @Override
+ public String datapathDescription() {
+ return null;
+ }
+
+ @Override
+ public String hardwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String softwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String serialNumber() {
+ return null;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return true;
+ }
+
+ @Override
+ public void disconnectSwitch() {
+ }
+
+ @Override
+ public Device.Type deviceType() {
+ return Device.Type.SWITCH;
+ }
+
+ @Override
+ public void returnRoleReply(RoleState requested, RoleState response) {
+ }
+
+ @Override
+ public String channelId() {
+ return "1.2.3.4:1";
+ }
+
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/flow/pom.xml b/framework/src/onos/providers/openflow/flow/pom.xml
new file mode 100644
index 00000000..24c430e3
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-provider-flow</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS OpenFlow protocol flow provider</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </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
new file mode 100644
index 00000000..f238bdb1
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -0,0 +1,707 @@
+/*
+ * 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.of.flow.impl;
+
+import static org.onosproject.net.flow.criteria.Criteria.matchLambda;
+import static org.onosproject.net.flow.criteria.Criteria.matchOchSignalType;
+import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupChannelSpacing;
+import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupGridType;
+import static org.onosproject.provider.of.flow.impl.OpenFlowValueMapper.lookupOchSignalType;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.List;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowEntry;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowEntry.FlowEntryState;
+import org.onosproject.net.flow.FlowRule;
+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.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsEntry;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
+import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionApplyActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionGotoTable;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteActions;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstructionWriteMetadata;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.match.MatchField;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
+import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.Masked;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.U8;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+
+import com.google.common.collect.Lists;
+
+public class FlowEntryBuilder {
+ private final Logger log = getLogger(getClass());
+
+ private final OFFlowStatsEntry stat;
+ private final OFFlowRemoved removed;
+ private final OFFlowMod flowMod;
+
+ private final Match match;
+
+ // All actions are contained in an OFInstruction. For OF1.0
+ // the instruction type is apply instruction (immediate set in ONOS speak)
+ private final List<OFInstruction> instructions;
+
+ private final Dpid dpid;
+
+ public enum FlowType { STAT, REMOVED, MOD }
+
+ private final FlowType type;
+
+ public FlowEntryBuilder(Dpid dpid, OFFlowStatsEntry entry) {
+ this.stat = entry;
+ this.match = entry.getMatch();
+ this.instructions = getInstructions(entry);
+ this.dpid = dpid;
+ this.removed = null;
+ this.flowMod = null;
+ this.type = FlowType.STAT;
+ }
+
+ public FlowEntryBuilder(Dpid dpid, OFFlowRemoved removed) {
+ this.match = removed.getMatch();
+ this.removed = removed;
+
+ this.dpid = dpid;
+ this.instructions = null;
+ this.stat = null;
+ this.flowMod = null;
+ this.type = FlowType.REMOVED;
+
+ }
+
+ public FlowEntryBuilder(Dpid dpid, OFFlowMod fm) {
+ this.match = fm.getMatch();
+ this.dpid = dpid;
+ this.instructions = getInstructions(fm);
+ this.type = FlowType.MOD;
+ this.flowMod = fm;
+ this.stat = null;
+ this.removed = null;
+ }
+
+ public FlowEntry build(FlowEntryState... state) {
+ FlowRule rule;
+ switch (this.type) {
+ case STAT:
+ rule = DefaultFlowRule.builder()
+ .forDevice(DeviceId.deviceId(Dpid.uri(dpid)))
+ .withSelector(buildSelector())
+ .withTreatment(buildTreatment())
+ .withPriority(stat.getPriority())
+ .makeTemporary(stat.getIdleTimeout())
+ .withCookie(stat.getCookie().getValue())
+ .forTable(stat.getTableId().getValue())
+ .build();
+
+ return new DefaultFlowEntry(rule, FlowEntryState.ADDED,
+ stat.getDurationSec(), stat.getPacketCount().getValue(),
+ stat.getByteCount().getValue());
+ case REMOVED:
+ rule = DefaultFlowRule.builder()
+ .forDevice(DeviceId.deviceId(Dpid.uri(dpid)))
+ .withSelector(buildSelector())
+ .withPriority(removed.getPriority())
+ .makeTemporary(removed.getIdleTimeout())
+ .withCookie(removed.getCookie().getValue())
+ .forTable(removed.getTableId().getValue())
+ .build();
+
+ return new DefaultFlowEntry(rule, FlowEntryState.REMOVED, removed.getDurationSec(),
+ removed.getPacketCount().getValue(), removed.getByteCount().getValue());
+ case MOD:
+ FlowEntryState flowState = state.length > 0 ? state[0] : FlowEntryState.FAILED;
+ rule = DefaultFlowRule.builder()
+ .forDevice(DeviceId.deviceId(Dpid.uri(dpid)))
+ .withSelector(buildSelector())
+ .withTreatment(buildTreatment())
+ .withPriority(flowMod.getPriority())
+ .makeTemporary(flowMod.getIdleTimeout())
+ .withCookie(flowMod.getCookie().getValue())
+ .forTable(flowMod.getTableId().getValue())
+ .build();
+
+ return new DefaultFlowEntry(rule, flowState, 0, 0, 0);
+ default:
+ log.error("Unknown flow type : {}", this.type);
+ return null;
+ }
+
+ }
+
+ private List<OFInstruction> getInstructions(OFFlowMod entry) {
+ switch (entry.getVersion()) {
+ case OF_10:
+ return Lists.newArrayList(OFFactoryVer13.INSTANCE.instructions()
+ .applyActions(
+ entry.getActions()));
+ case OF_11:
+ case OF_12:
+ case OF_13:
+ return entry.getInstructions();
+ default:
+ log.warn("Unknown OF version {}", entry.getVersion());
+ }
+ return Lists.newLinkedList();
+ }
+
+ private List<OFInstruction> getInstructions(OFFlowStatsEntry entry) {
+ switch (entry.getVersion()) {
+ case OF_10:
+ return Lists.newArrayList(
+ OFFactoryVer13.INSTANCE.instructions().applyActions(entry.getActions()));
+ case OF_11:
+ case OF_12:
+ case OF_13:
+ return entry.getInstructions();
+ default:
+ log.warn("Unknown OF version {}", entry.getVersion());
+ }
+ return Lists.newLinkedList();
+ }
+
+ private TrafficTreatment buildTreatment() {
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ // If this is a drop rule
+ if (instructions.size() == 0) {
+ builder.drop();
+ return builder.build();
+ }
+ for (OFInstruction in : instructions) {
+ switch (in.getType()) {
+ case GOTO_TABLE:
+ builder.transition(((int) ((OFInstructionGotoTable) in)
+ .getTableId().getValue()));
+ break;
+ case WRITE_METADATA:
+ OFInstructionWriteMetadata m = (OFInstructionWriteMetadata) in;
+ builder.writeMetadata(m.getMetadata().getValue(),
+ m.getMetadataMask().getValue());
+ break;
+ case WRITE_ACTIONS:
+ builder.deferred();
+ buildActions(((OFInstructionWriteActions) in).getActions(),
+ builder);
+ break;
+ case APPLY_ACTIONS:
+ builder.immediate();
+ buildActions(((OFInstructionApplyActions) in).getActions(),
+ builder);
+ break;
+ case CLEAR_ACTIONS:
+ builder.wipeDeferred();
+ break;
+ case EXPERIMENTER:
+ break;
+ case METER:
+ break;
+ default:
+ log.warn("Unknown instructions type {}", in.getType());
+ }
+ }
+
+ return builder.build();
+ }
+
+ private TrafficTreatment.Builder buildActions(List<OFAction> actions,
+ TrafficTreatment.Builder builder) {
+ for (OFAction act : actions) {
+ switch (act.getType()) {
+ case OUTPUT:
+ OFActionOutput out = (OFActionOutput) act;
+ builder.setOutput(
+ PortNumber.portNumber(out.getPort().getPortNumber()));
+ break;
+ case SET_VLAN_VID:
+ OFActionSetVlanVid vlan = (OFActionSetVlanVid) act;
+ builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan()));
+ break;
+ case SET_VLAN_PCP:
+ OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act;
+ builder.setVlanPcp(pcp.getVlanPcp().getValue());
+ break;
+ case SET_DL_DST:
+ OFActionSetDlDst dldst = (OFActionSetDlDst) act;
+ builder.setEthDst(
+ MacAddress.valueOf(dldst.getDlAddr().getLong()));
+ break;
+ case SET_DL_SRC:
+ OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act;
+ builder.setEthSrc(
+ MacAddress.valueOf(dlsrc.getDlAddr().getLong()));
+
+ break;
+ case SET_NW_DST:
+ OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
+ IPv4Address di = nwdst.getNwAddr();
+ builder.setIpDst(Ip4Address.valueOf(di.getInt()));
+ break;
+ case SET_NW_SRC:
+ OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
+ IPv4Address si = nwsrc.getNwAddr();
+ builder.setIpSrc(Ip4Address.valueOf(si.getInt()));
+ break;
+ case EXPERIMENTER:
+ OFActionExperimenter exp = (OFActionExperimenter) act;
+ if (exp.getExperimenter() == 0x80005A06 ||
+ exp.getExperimenter() == 0x748771) {
+ OFActionCircuit ct = (OFActionCircuit) exp;
+ short lambda = ((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber();
+ builder.add(Instructions.modL0Lambda(Lambda.indexedLambda(lambda)));
+ } else {
+ log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
+ }
+ break;
+ case SET_FIELD:
+ OFActionSetField setField = (OFActionSetField) act;
+ handleSetField(builder, setField.getField());
+ break;
+ case POP_MPLS:
+ OFActionPopMpls popMpls = (OFActionPopMpls) act;
+ builder.popMpls((short) popMpls.getEthertype().getValue());
+ break;
+ case PUSH_MPLS:
+ builder.pushMpls();
+ break;
+ case COPY_TTL_IN:
+ builder.copyTtlIn();
+ break;
+ case COPY_TTL_OUT:
+ builder.copyTtlOut();
+ break;
+ case DEC_MPLS_TTL:
+ builder.decMplsTtl();
+ break;
+ case DEC_NW_TTL:
+ builder.decNwTtl();
+ break;
+ case GROUP:
+ OFActionGroup group = (OFActionGroup) act;
+ builder.group(new DefaultGroupId(group.getGroup().getGroupNumber()));
+ break;
+ case STRIP_VLAN:
+ case POP_VLAN:
+ builder.popVlan();
+ break;
+ case PUSH_VLAN:
+ builder.pushVlan();
+ break;
+ case SET_TP_DST:
+ case SET_TP_SRC:
+ case POP_PBB:
+ case PUSH_PBB:
+ case SET_MPLS_LABEL:
+ case SET_MPLS_TC:
+ case SET_MPLS_TTL:
+ case SET_NW_ECN:
+ case SET_NW_TOS:
+ case SET_NW_TTL:
+ case SET_QUEUE:
+
+ case ENQUEUE:
+ default:
+ log.warn("Action type {} not yet implemented.", act.getType());
+ }
+ }
+ return builder;
+ }
+
+
+ private void handleSetField(TrafficTreatment.Builder builder, OFOxm<?> oxm) {
+ switch (oxm.getMatchField().id) {
+ case VLAN_PCP:
+ @SuppressWarnings("unchecked")
+ OFOxm<VlanPcp> vlanpcp = (OFOxm<VlanPcp>) oxm;
+ builder.setVlanPcp(vlanpcp.getValue().getValue());
+ break;
+ case VLAN_VID:
+ @SuppressWarnings("unchecked")
+ OFOxm<OFVlanVidMatch> vlanvid = (OFOxm<OFVlanVidMatch>) oxm;
+ builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan()));
+ break;
+ case ETH_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethdst =
+ (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+ builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong()));
+ break;
+ case ETH_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethsrc =
+ (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+ builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong()));
+ break;
+ case IPV4_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<IPv4Address> ip4dst = (OFOxm<IPv4Address>) oxm;
+ builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt()));
+ break;
+ case IPV4_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<IPv4Address> ip4src = (OFOxm<IPv4Address>) oxm;
+ builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt()));
+ break;
+ case MPLS_LABEL:
+ @SuppressWarnings("unchecked")
+ OFOxm<U32> labelId = (OFOxm<U32>) oxm;
+ builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue()));
+ break;
+ case MPLS_BOS:
+ @SuppressWarnings("unchecked")
+ OFOxm<U8> mplsBos = (OFOxm<U8>) oxm;
+ builder.setMplsBos(mplsBos.getValue() == U8.ZERO ? false : true);
+ break;
+ case TUNNEL_ID:
+ @SuppressWarnings("unchecked")
+ OFOxm<U64> tunnelId = (OFOxm<U64>) oxm;
+ builder.setTunnelId(tunnelId.getValue().getValue());
+ break;
+ case TCP_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<TransportPort> tcpdst = (OFOxm<TransportPort>) oxm;
+ builder.setTcpDst(TpPort.tpPort(tcpdst.getValue().getPort()));
+ break;
+ case TCP_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<TransportPort> tcpsrc = (OFOxm<TransportPort>) oxm;
+ builder.setTcpSrc(TpPort.tpPort(tcpsrc.getValue().getPort()));
+ break;
+ case UDP_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<TransportPort> udpdst = (OFOxm<TransportPort>) oxm;
+ builder.setUdpDst(TpPort.tpPort(udpdst.getValue().getPort()));
+ break;
+ case UDP_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<TransportPort> udpsrc = (OFOxm<TransportPort>) oxm;
+ builder.setUdpSrc(TpPort.tpPort(udpsrc.getValue().getPort()));
+ break;
+ case ARP_OP:
+ case ARP_SHA:
+ case ARP_SPA:
+ case ARP_THA:
+ case ARP_TPA:
+ case BSN_EGR_PORT_GROUP_ID:
+ case BSN_GLOBAL_VRF_ALLOWED:
+ case BSN_IN_PORTS_128:
+ case BSN_L3_DST_CLASS_ID:
+ case BSN_L3_INTERFACE_CLASS_ID:
+ case BSN_L3_SRC_CLASS_ID:
+ case BSN_LAG_ID:
+ case BSN_TCP_FLAGS:
+ case BSN_UDF0:
+ case BSN_UDF1:
+ case BSN_UDF2:
+ case BSN_UDF3:
+ case BSN_UDF4:
+ case BSN_UDF5:
+ case BSN_UDF6:
+ case BSN_UDF7:
+ case BSN_VLAN_XLATE_PORT_GROUP_ID:
+ case BSN_VRF:
+ case ETH_TYPE:
+ case ICMPV4_CODE:
+ case ICMPV4_TYPE:
+ case ICMPV6_CODE:
+ case ICMPV6_TYPE:
+ case IN_PHY_PORT:
+ case IN_PORT:
+ case IPV6_DST:
+ case IPV6_FLABEL:
+ case IPV6_ND_SLL:
+ case IPV6_ND_TARGET:
+ case IPV6_ND_TLL:
+ case IPV6_SRC:
+ case IP_DSCP:
+ case IP_ECN:
+ case IP_PROTO:
+ case METADATA:
+ case MPLS_TC:
+ case OCH_SIGID:
+ case OCH_SIGID_BASIC:
+ case OCH_SIGTYPE:
+ case OCH_SIGTYPE_BASIC:
+ case SCTP_DST:
+ case SCTP_SRC:
+ default:
+ log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id);
+ break;
+ }
+ }
+
+ // CHECKSTYLE IGNORE MethodLength FOR NEXT 1 LINES
+ private TrafficSelector buildSelector() {
+ MacAddress mac;
+ Ip4Prefix ip4Prefix;
+ Ip6Address ip6Address;
+ Ip6Prefix ip6Prefix;
+
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+ for (MatchField<?> field : match.getMatchFields()) {
+ switch (field.id) {
+ case IN_PORT:
+ builder.matchInPort(PortNumber
+ .portNumber(match.get(MatchField.IN_PORT).getPortNumber()));
+ break;
+ case IN_PHY_PORT:
+ builder.matchInPhyPort(PortNumber
+ .portNumber(match.get(MatchField.IN_PHY_PORT).getPortNumber()));
+ break;
+ case METADATA:
+ long metadata =
+ match.get(MatchField.METADATA).getValue().getValue();
+ builder.matchMetadata(metadata);
+ break;
+ case ETH_DST:
+ mac = MacAddress.valueOf(match.get(MatchField.ETH_DST).getLong());
+ builder.matchEthDst(mac);
+ break;
+ case ETH_SRC:
+ mac = MacAddress.valueOf(match.get(MatchField.ETH_SRC).getLong());
+ builder.matchEthSrc(mac);
+ break;
+ case ETH_TYPE:
+ int ethType = match.get(MatchField.ETH_TYPE).getValue();
+ if (ethType == EthType.VLAN_FRAME.getValue()) {
+ builder.matchVlanId(VlanId.ANY);
+ } else {
+ builder.matchEthType((short) ethType);
+ }
+ break;
+ case VLAN_VID:
+ VlanId vlanId = null;
+ if (match.isPartiallyMasked(MatchField.VLAN_VID)) {
+ Masked<OFVlanVidMatch> masked = match.getMasked(MatchField.VLAN_VID);
+ if (masked.getValue().equals(OFVlanVidMatch.PRESENT)
+ && masked.getMask().equals(OFVlanVidMatch.PRESENT)) {
+ vlanId = VlanId.ANY;
+ }
+ } else {
+ if (!match.get(MatchField.VLAN_VID).isPresentBitSet()) {
+ vlanId = VlanId.NONE;
+ } else {
+ vlanId = VlanId.vlanId(match.get(MatchField.VLAN_VID).getVlan());
+ }
+ }
+ if (vlanId != null) {
+ builder.matchVlanId(vlanId);
+ }
+ break;
+ case VLAN_PCP:
+ byte vlanPcp = match.get(MatchField.VLAN_PCP).getValue();
+ builder.matchVlanPcp(vlanPcp);
+ break;
+ case IP_DSCP:
+ byte ipDscp = match.get(MatchField.IP_DSCP).getDscpValue();
+ builder.matchIPDscp(ipDscp);
+ break;
+ case IP_ECN:
+ byte ipEcn = match.get(MatchField.IP_ECN).getEcnValue();
+ builder.matchIPEcn(ipEcn);
+ break;
+ case IP_PROTO:
+ short proto = match.get(MatchField.IP_PROTO).getIpProtocolNumber();
+ builder.matchIPProtocol((byte) proto);
+ break;
+ case IPV4_SRC:
+ if (match.isPartiallyMasked(MatchField.IPV4_SRC)) {
+ Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_SRC);
+ ip4Prefix = Ip4Prefix.valueOf(
+ maskedIp.getValue().getInt(),
+ maskedIp.getMask().asCidrMaskLength());
+ } else {
+ ip4Prefix = Ip4Prefix.valueOf(
+ match.get(MatchField.IPV4_SRC).getInt(),
+ Ip4Prefix.MAX_MASK_LENGTH);
+ }
+ builder.matchIPSrc(ip4Prefix);
+ break;
+ case IPV4_DST:
+ if (match.isPartiallyMasked(MatchField.IPV4_DST)) {
+ Masked<IPv4Address> maskedIp = match.getMasked(MatchField.IPV4_DST);
+ ip4Prefix = Ip4Prefix.valueOf(
+ maskedIp.getValue().getInt(),
+ maskedIp.getMask().asCidrMaskLength());
+ } else {
+ ip4Prefix = Ip4Prefix.valueOf(
+ match.get(MatchField.IPV4_DST).getInt(),
+ Ip4Prefix.MAX_MASK_LENGTH);
+ }
+ builder.matchIPDst(ip4Prefix);
+ break;
+ case TCP_SRC:
+ builder.matchTcpSrc(TpPort.tpPort(match.get(MatchField.TCP_SRC).getPort()));
+ break;
+ case TCP_DST:
+ builder.matchTcpDst(TpPort.tpPort(match.get(MatchField.TCP_DST).getPort()));
+ break;
+ case UDP_SRC:
+ builder.matchUdpSrc(TpPort.tpPort(match.get(MatchField.UDP_SRC).getPort()));
+ break;
+ case UDP_DST:
+ builder.matchUdpDst(TpPort.tpPort(match.get(MatchField.UDP_DST).getPort()));
+ break;
+ case MPLS_LABEL:
+ builder.matchMplsLabel(MplsLabel.mplsLabel((int) match.get(MatchField.MPLS_LABEL)
+ .getValue()));
+ break;
+ case MPLS_BOS:
+ builder.matchMplsBos(match.get(MatchField.MPLS_BOS).getValue());
+ break;
+ case SCTP_SRC:
+ builder.matchSctpSrc(TpPort.tpPort(match.get(MatchField.SCTP_SRC).getPort()));
+ break;
+ case SCTP_DST:
+ builder.matchSctpDst(TpPort.tpPort(match.get(MatchField.SCTP_DST).getPort()));
+ break;
+ case ICMPV4_TYPE:
+ byte icmpType = (byte) match.get(MatchField.ICMPV4_TYPE).getType();
+ builder.matchIcmpType(icmpType);
+ break;
+ case ICMPV4_CODE:
+ byte icmpCode = (byte) match.get(MatchField.ICMPV4_CODE).getCode();
+ builder.matchIcmpCode(icmpCode);
+ break;
+ case IPV6_SRC:
+ if (match.isPartiallyMasked(MatchField.IPV6_SRC)) {
+ Masked<IPv6Address> maskedIp = match.getMasked(MatchField.IPV6_SRC);
+ ip6Prefix = Ip6Prefix.valueOf(
+ maskedIp.getValue().getBytes(),
+ maskedIp.getMask().asCidrMaskLength());
+ } else {
+ ip6Prefix = Ip6Prefix.valueOf(
+ match.get(MatchField.IPV6_SRC).getBytes(),
+ Ip6Prefix.MAX_MASK_LENGTH);
+ }
+ builder.matchIPv6Src(ip6Prefix);
+ break;
+ case IPV6_DST:
+ if (match.isPartiallyMasked(MatchField.IPV6_DST)) {
+ Masked<IPv6Address> maskedIp = match.getMasked(MatchField.IPV6_DST);
+ ip6Prefix = Ip6Prefix.valueOf(
+ maskedIp.getValue().getBytes(),
+ maskedIp.getMask().asCidrMaskLength());
+ } else {
+ ip6Prefix = Ip6Prefix.valueOf(
+ match.get(MatchField.IPV6_DST).getBytes(),
+ Ip6Prefix.MAX_MASK_LENGTH);
+ }
+ builder.matchIPv6Dst(ip6Prefix);
+ break;
+ case IPV6_FLABEL:
+ int flowLabel =
+ match.get(MatchField.IPV6_FLABEL).getIPv6FlowLabelValue();
+ builder.matchIPv6FlowLabel(flowLabel);
+ break;
+ case ICMPV6_TYPE:
+ byte icmpv6type = (byte) match.get(MatchField.ICMPV6_TYPE).getValue();
+ builder.matchIcmpv6Type(icmpv6type);
+ break;
+ case ICMPV6_CODE:
+ byte icmpv6code = (byte) match.get(MatchField.ICMPV6_CODE).getValue();
+ builder.matchIcmpv6Code(icmpv6code);
+ break;
+ case IPV6_ND_TARGET:
+ ip6Address =
+ Ip6Address.valueOf(match.get(MatchField.IPV6_ND_TARGET).getBytes());
+ builder.matchIPv6NDTargetAddress(ip6Address);
+ break;
+ case IPV6_ND_SLL:
+ mac = MacAddress.valueOf(match.get(MatchField.IPV6_ND_SLL).getLong());
+ builder.matchIPv6NDSourceLinkLayerAddress(mac);
+ break;
+ case IPV6_ND_TLL:
+ mac = MacAddress.valueOf(match.get(MatchField.IPV6_ND_TLL).getLong());
+ builder.matchIPv6NDTargetLinkLayerAddress(mac);
+ break;
+ case IPV6_EXTHDR:
+ builder.matchIPv6ExthdrFlags((short) match.get(MatchField.IPV6_EXTHDR)
+ .getValue());
+ break;
+ case OCH_SIGID:
+ CircuitSignalID sigId = match.get(MatchField.OCH_SIGID);
+ builder.add(matchLambda(Lambda.ochSignal(
+ lookupGridType(sigId.getGridType()), lookupChannelSpacing(sigId.getChannelSpacing()),
+ sigId.getChannelNumber(), sigId.getSpectralWidth())
+ ));
+ break;
+ case OCH_SIGTYPE:
+ U8 sigType = match.get(MatchField.OCH_SIGTYPE);
+ builder.add(matchOchSignalType(lookupOchSignalType((byte) sigType.getValue())));
+ break;
+ case TUNNEL_ID:
+ long tunnelId = match.get(MatchField.TUNNEL_ID).getValue();
+ builder.matchTunnelId(tunnelId);
+ break;
+ case ARP_OP:
+ case ARP_SHA:
+ case ARP_SPA:
+ case ARP_THA:
+ case ARP_TPA:
+ case MPLS_TC:
+ default:
+ log.warn("Match type {} not yet implemented.", field.id);
+ }
+ }
+ return builder.build();
+ }
+}
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
new file mode 100644
index 00000000..e050524a
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilder.java
@@ -0,0 +1,444 @@
+/*
+ * 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.of.flow.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.Optional;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.Ip6Address;
+import org.onlab.packet.Ip6Prefix;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthCriterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPCriterion;
+import org.onosproject.net.flow.criteria.IPDscpCriterion;
+import org.onosproject.net.flow.criteria.IPEcnCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.IPv6ExthdrFlagsCriterion;
+import org.onosproject.net.flow.criteria.IPv6FlowLabelCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDLinkLayerAddressCriterion;
+import org.onosproject.net.flow.criteria.IPv6NDTargetAddressCriterion;
+import org.onosproject.net.flow.criteria.IcmpCodeCriterion;
+import org.onosproject.net.flow.criteria.IcmpTypeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6CodeCriterion;
+import org.onosproject.net.flow.criteria.Icmpv6TypeCriterion;
+import org.onosproject.net.flow.criteria.MetadataCriterion;
+import org.onosproject.net.flow.criteria.MplsBosCriterion;
+import org.onosproject.net.flow.criteria.MplsCriterion;
+import org.onosproject.net.flow.criteria.OchSignalCriterion;
+import org.onosproject.net.flow.criteria.OchSignalTypeCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.SctpPortCriterion;
+import org.onosproject.net.flow.criteria.TcpPortCriterion;
+import org.onosproject.net.flow.criteria.TunnelIdCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.criteria.VlanPcpCriterion;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+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.CircuitSignalID;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.ICMPv4Code;
+import org.projectfloodlight.openflow.types.ICMPv4Type;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
+import org.projectfloodlight.openflow.types.IpDscp;
+import org.projectfloodlight.openflow.types.IpEcn;
+import org.projectfloodlight.openflow.types.IpProtocol;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.Masked;
+import org.projectfloodlight.openflow.types.OFBooleanValue;
+import org.projectfloodlight.openflow.types.OFMetadata;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U16;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.U8;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.projectfloodlight.openflow.types.VlanVid;
+import org.slf4j.Logger;
+
+/**
+ * Builder for OpenFlow flow mods based on FlowRules.
+ */
+public abstract class FlowModBuilder {
+
+ private final Logger log = getLogger(getClass());
+
+ private final OFFactory factory;
+ private final FlowRule flowRule;
+ private final TrafficSelector selector;
+ protected final Long xid;
+
+ /**
+ * Creates a new flow mod builder.
+ *
+ * @param flowRule the flow rule to transform into a flow mod
+ * @param factory the OpenFlow factory to use to build the flow mod
+ * @param xid the transaction ID
+ * @return the new flow mod builder
+ */
+ public static FlowModBuilder builder(FlowRule flowRule,
+ OFFactory factory,
+ Optional<Long> xid) {
+ switch (factory.getVersion()) {
+ case OF_10:
+ return new FlowModBuilderVer10(flowRule, factory, xid);
+ case OF_13:
+ return new FlowModBuilderVer13(flowRule, factory, xid);
+ default:
+ throw new UnsupportedOperationException(
+ "No flow mod builder for protocol version " + factory.getVersion());
+ }
+ }
+
+ /**
+ * Constructs a flow mod builder.
+ *
+ * @param flowRule the flow rule to transform into a flow mod
+ * @param factory the OpenFlow factory to use to build the flow mod
+ * @param xid the transaction ID
+ */
+ protected FlowModBuilder(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
+ this.factory = factory;
+ this.flowRule = flowRule;
+ this.selector = flowRule.selector();
+ this.xid = xid.orElse(0L);
+
+ }
+
+ /**
+ * Builds an ADD flow mod.
+ *
+ * @return the flow mod
+ */
+ public abstract OFFlowAdd buildFlowAdd();
+
+ /**
+ * Builds a MODIFY flow mod.
+ *
+ * @return the flow mod
+ */
+ public abstract OFFlowMod buildFlowMod();
+
+ /**
+ * Builds a DELETE flow mod.
+ *
+ * @return the flow mod
+ */
+ public abstract OFFlowDelete buildFlowDel();
+
+ /**
+ * Builds the match for the flow mod.
+ *
+ * @return the match
+ */
+ // CHECKSTYLE IGNORE MethodLength FOR NEXT 300 LINES
+ protected Match buildMatch() {
+ Match.Builder mBuilder = factory.buildMatch();
+ Ip6Address ip6Address;
+ Ip4Prefix ip4Prefix;
+ Ip6Prefix ip6Prefix;
+ EthCriterion ethCriterion;
+ IPCriterion ipCriterion;
+ TcpPortCriterion tcpPortCriterion;
+ UdpPortCriterion udpPortCriterion;
+ SctpPortCriterion sctpPortCriterion;
+ IPv6NDLinkLayerAddressCriterion llAddressCriterion;
+
+ for (Criterion c : selector.criteria()) {
+ switch (c.type()) {
+ case IN_PORT:
+ PortCriterion inPort = (PortCriterion) c;
+ mBuilder.setExact(MatchField.IN_PORT,
+ OFPort.of((int) inPort.port().toLong()));
+ break;
+ case IN_PHY_PORT:
+ PortCriterion inPhyPort = (PortCriterion) c;
+ mBuilder.setExact(MatchField.IN_PORT,
+ OFPort.of((int) inPhyPort.port().toLong()));
+ break;
+ case METADATA:
+ MetadataCriterion metadata = (MetadataCriterion) c;
+ mBuilder.setExact(MatchField.METADATA,
+ OFMetadata.ofRaw(metadata.metadata()));
+ break;
+ case ETH_DST:
+ ethCriterion = (EthCriterion) c;
+ mBuilder.setExact(MatchField.ETH_DST,
+ MacAddress.of(ethCriterion.mac().toLong()));
+ break;
+ case ETH_SRC:
+ ethCriterion = (EthCriterion) c;
+ mBuilder.setExact(MatchField.ETH_SRC,
+ MacAddress.of(ethCriterion.mac().toLong()));
+ break;
+ case ETH_TYPE:
+ EthTypeCriterion ethType = (EthTypeCriterion) c;
+ mBuilder.setExact(MatchField.ETH_TYPE, EthType.of(ethType.ethType().toShort()));
+ break;
+ case VLAN_VID:
+ VlanIdCriterion vid = (VlanIdCriterion) c;
+
+ if (vid.vlanId().equals(VlanId.ANY)) {
+ mBuilder.setMasked(MatchField.VLAN_VID, OFVlanVidMatch.PRESENT,
+ OFVlanVidMatch.PRESENT);
+ } else if (vid.vlanId().equals(VlanId.NONE)) {
+ mBuilder.setExact(MatchField.VLAN_VID, OFVlanVidMatch.NONE);
+ } else {
+ mBuilder.setExact(MatchField.VLAN_VID,
+ OFVlanVidMatch.ofVlanVid(VlanVid.ofVlan(vid.vlanId().toShort())));
+ }
+ break;
+ case VLAN_PCP:
+ VlanPcpCriterion vpcp = (VlanPcpCriterion) c;
+ mBuilder.setExact(MatchField.VLAN_PCP, VlanPcp.of(vpcp.priority()));
+ break;
+ case IP_DSCP:
+ IPDscpCriterion ipDscpCriterion = (IPDscpCriterion) c;
+ mBuilder.setExact(MatchField.IP_DSCP,
+ IpDscp.of(ipDscpCriterion.ipDscp()));
+ break;
+ case IP_ECN:
+ IPEcnCriterion ipEcnCriterion = (IPEcnCriterion) c;
+ mBuilder.setExact(MatchField.IP_ECN,
+ IpEcn.of(ipEcnCriterion.ipEcn()));
+ break;
+ case IP_PROTO:
+ IPProtocolCriterion p = (IPProtocolCriterion) c;
+ mBuilder.setExact(MatchField.IP_PROTO, IpProtocol.of(p.protocol()));
+ break;
+ case IPV4_SRC:
+ ipCriterion = (IPCriterion) c;
+ ip4Prefix = ipCriterion.ip().getIp4Prefix();
+ if (ip4Prefix.prefixLength() != Ip4Prefix.MAX_MASK_LENGTH) {
+ Ip4Address maskAddr =
+ Ip4Address.makeMaskPrefix(ip4Prefix.prefixLength());
+ Masked<IPv4Address> maskedIp =
+ Masked.of(IPv4Address.of(ip4Prefix.address().toInt()),
+ IPv4Address.of(maskAddr.toInt()));
+ mBuilder.setMasked(MatchField.IPV4_SRC, maskedIp);
+ } else {
+ mBuilder.setExact(MatchField.IPV4_SRC,
+ IPv4Address.of(ip4Prefix.address().toInt()));
+ }
+ break;
+ case IPV4_DST:
+ ipCriterion = (IPCriterion) c;
+ ip4Prefix = ipCriterion.ip().getIp4Prefix();
+ if (ip4Prefix.prefixLength() != Ip4Prefix.MAX_MASK_LENGTH) {
+ Ip4Address maskAddr =
+ Ip4Address.makeMaskPrefix(ip4Prefix.prefixLength());
+ Masked<IPv4Address> maskedIp =
+ Masked.of(IPv4Address.of(ip4Prefix.address().toInt()),
+ IPv4Address.of(maskAddr.toInt()));
+ mBuilder.setMasked(MatchField.IPV4_DST, maskedIp);
+ } else {
+ mBuilder.setExact(MatchField.IPV4_DST,
+ IPv4Address.of(ip4Prefix.address().toInt()));
+ }
+ break;
+ case TCP_SRC:
+ tcpPortCriterion = (TcpPortCriterion) c;
+ mBuilder.setExact(MatchField.TCP_SRC,
+ TransportPort.of(tcpPortCriterion.tcpPort().toInt()));
+ break;
+ case TCP_DST:
+ tcpPortCriterion = (TcpPortCriterion) c;
+ mBuilder.setExact(MatchField.TCP_DST,
+ TransportPort.of(tcpPortCriterion.tcpPort().toInt()));
+ break;
+ case UDP_SRC:
+ udpPortCriterion = (UdpPortCriterion) c;
+ mBuilder.setExact(MatchField.UDP_SRC,
+ TransportPort.of(udpPortCriterion.udpPort().toInt()));
+ break;
+ case UDP_DST:
+ udpPortCriterion = (UdpPortCriterion) c;
+ mBuilder.setExact(MatchField.UDP_DST,
+ TransportPort.of(udpPortCriterion.udpPort().toInt()));
+ break;
+ case SCTP_SRC:
+ sctpPortCriterion = (SctpPortCriterion) c;
+ mBuilder.setExact(MatchField.SCTP_SRC,
+ TransportPort.of(sctpPortCriterion.sctpPort().toInt()));
+ break;
+ case SCTP_DST:
+ sctpPortCriterion = (SctpPortCriterion) c;
+ mBuilder.setExact(MatchField.SCTP_DST,
+ TransportPort.of(sctpPortCriterion.sctpPort().toInt()));
+ break;
+ case ICMPV4_TYPE:
+ IcmpTypeCriterion icmpType = (IcmpTypeCriterion) c;
+ mBuilder.setExact(MatchField.ICMPV4_TYPE,
+ ICMPv4Type.of(icmpType.icmpType()));
+ break;
+ case ICMPV4_CODE:
+ IcmpCodeCriterion icmpCode = (IcmpCodeCriterion) c;
+ mBuilder.setExact(MatchField.ICMPV4_CODE,
+ ICMPv4Code.of(icmpCode.icmpCode()));
+ break;
+ case IPV6_SRC:
+ ipCriterion = (IPCriterion) c;
+ ip6Prefix = ipCriterion.ip().getIp6Prefix();
+ if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) {
+ Ip6Address maskAddr =
+ Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength());
+ Masked<IPv6Address> maskedIp =
+ Masked.of(IPv6Address.of(ip6Prefix.address().toString()),
+ IPv6Address.of(maskAddr.toString()));
+ mBuilder.setMasked(MatchField.IPV6_SRC, maskedIp);
+ } else {
+ mBuilder.setExact(MatchField.IPV6_SRC,
+ IPv6Address.of(ip6Prefix.address().toString()));
+ }
+ break;
+ case IPV6_DST:
+ ipCriterion = (IPCriterion) c;
+ ip6Prefix = ipCriterion.ip().getIp6Prefix();
+ if (ip6Prefix.prefixLength() != Ip6Prefix.MAX_MASK_LENGTH) {
+ Ip6Address maskAddr =
+ Ip6Address.makeMaskPrefix(ip6Prefix.prefixLength());
+ Masked<IPv6Address> maskedIp =
+ Masked.of(IPv6Address.of(ip6Prefix.address().toString()),
+ IPv6Address.of(maskAddr.toString()));
+ mBuilder.setMasked(MatchField.IPV6_DST, maskedIp);
+ } else {
+ mBuilder.setExact(MatchField.IPV6_DST,
+ IPv6Address.of(ip6Prefix.address().toString()));
+ }
+ break;
+ case IPV6_FLABEL:
+ IPv6FlowLabelCriterion flowLabelCriterion =
+ (IPv6FlowLabelCriterion) c;
+ mBuilder.setExact(MatchField.IPV6_FLABEL,
+ IPv6FlowLabel.of(flowLabelCriterion.flowLabel()));
+ break;
+ case ICMPV6_TYPE:
+ Icmpv6TypeCriterion icmpv6Type = (Icmpv6TypeCriterion) c;
+ mBuilder.setExact(MatchField.ICMPV6_TYPE,
+ U8.of(icmpv6Type.icmpv6Type()));
+ break;
+ case ICMPV6_CODE:
+ Icmpv6CodeCriterion icmpv6Code = (Icmpv6CodeCriterion) c;
+ mBuilder.setExact(MatchField.ICMPV6_CODE,
+ U8.of(icmpv6Code.icmpv6Code()));
+ break;
+ case IPV6_ND_TARGET:
+ IPv6NDTargetAddressCriterion targetAddressCriterion =
+ (IPv6NDTargetAddressCriterion) c;
+ ip6Address = targetAddressCriterion.targetAddress();
+ mBuilder.setExact(MatchField.IPV6_ND_TARGET,
+ IPv6Address.of(ip6Address.toOctets()));
+ break;
+ case IPV6_ND_SLL:
+ llAddressCriterion =
+ (IPv6NDLinkLayerAddressCriterion) c;
+ mBuilder.setExact(MatchField.IPV6_ND_SLL,
+ MacAddress.of(llAddressCriterion.mac().toLong()));
+ break;
+ case IPV6_ND_TLL:
+ llAddressCriterion =
+ (IPv6NDLinkLayerAddressCriterion) c;
+ mBuilder.setExact(MatchField.IPV6_ND_TLL,
+ MacAddress.of(llAddressCriterion.mac().toLong()));
+ break;
+ case MPLS_LABEL:
+ MplsCriterion mp = (MplsCriterion) c;
+ mBuilder.setExact(MatchField.MPLS_LABEL, U32.of(mp.label().toInt()));
+ break;
+ case IPV6_EXTHDR:
+ IPv6ExthdrFlagsCriterion exthdrFlagsCriterion =
+ (IPv6ExthdrFlagsCriterion) c;
+ mBuilder.setExact(MatchField.IPV6_EXTHDR,
+ U16.of(exthdrFlagsCriterion.exthdrFlags()));
+ break;
+ case OCH_SIGID:
+ try {
+ OchSignalCriterion ochSignalCriterion = (OchSignalCriterion) c;
+ OchSignal signal = ochSignalCriterion.lambda();
+ byte gridType = OpenFlowValueMapper.lookupGridType(signal.gridType());
+ byte channelSpacing = OpenFlowValueMapper.lookupChannelSpacing(signal.channelSpacing());
+ mBuilder.setExact(MatchField.OCH_SIGID,
+ new CircuitSignalID(gridType, channelSpacing,
+ (short) signal.spacingMultiplier(), (short) signal.slotGranularity()));
+ } catch (NoMappingFoundException e) {
+ log.warn(e.getMessage());
+ }
+ break;
+ case OCH_SIGTYPE:
+ OchSignalTypeCriterion sc = (OchSignalTypeCriterion) c;
+ byte signalType = OpenFlowValueMapper.lookupOchSignalType(sc.signalType());
+ mBuilder.setExact(MatchField.OCH_SIGTYPE, U8.of(signalType));
+ break;
+ case TUNNEL_ID:
+ TunnelIdCriterion tunnelId = (TunnelIdCriterion) c;
+ mBuilder.setExact(MatchField.TUNNEL_ID,
+ U64.of(tunnelId.tunnelId()));
+ break;
+ case MPLS_BOS:
+ MplsBosCriterion mplsBos = (MplsBosCriterion) c;
+ mBuilder.setExact(MatchField.MPLS_BOS,
+ mplsBos.mplsBos() ? OFBooleanValue.TRUE
+ : OFBooleanValue.FALSE);
+ break;
+ case ARP_OP:
+ case ARP_SHA:
+ case ARP_SPA:
+ case ARP_THA:
+ case ARP_TPA:
+ case MPLS_TC:
+ case PBB_ISID:
+ default:
+ log.warn("Match type {} not yet implemented.", c.type());
+ }
+ }
+ return mBuilder.build();
+ }
+
+ /**
+ * Returns the flow rule for this builder.
+ *
+ * @return the flow rule
+ */
+ protected FlowRule flowRule() {
+ return flowRule;
+ }
+
+ /**
+ * Returns the factory used for building OpenFlow constructs.
+ *
+ * @return the factory
+ */
+ protected OFFactory factory() {
+ return factory;
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
new file mode 100644
index 00000000..c9de4500
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer10.java
@@ -0,0 +1,230 @@
+/*
+ * 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.of.flow.impl;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFFlowDelete;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.projectfloodlight.openflow.types.VlanVid;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Flow mod builder for OpenFlow 1.0.
+ */
+public class FlowModBuilderVer10 extends FlowModBuilder {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private static final int OFPCML_NO_BUFFER = 0xffff;
+
+ private final TrafficTreatment treatment;
+
+ /**
+ * Constructor for a flow mod builder for OpenFlow 1.0.
+ *
+ * @param flowRule the flow rule to transform into a flow mod
+ * @param factory the OpenFlow factory to use to build the flow mod
+ * @param xid the transaction ID
+ */
+ protected FlowModBuilderVer10(FlowRule flowRule,
+ OFFactory factory, Optional<Long> xid) {
+ super(flowRule, factory, xid);
+
+ this.treatment = flowRule.treatment();
+ }
+
+ @Override
+ public OFFlowAdd buildFlowAdd() {
+ Match match = buildMatch();
+ List<OFAction> actions = buildActions();
+
+ long cookie = flowRule().id().value();
+
+
+ OFFlowAdd fm = factory().buildFlowAdd()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setActions(actions)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .build();
+
+ return fm;
+ }
+
+ @Override
+ public OFFlowMod buildFlowMod() {
+ Match match = buildMatch();
+ List<OFAction> actions = buildActions();
+
+ long cookie = flowRule().id().value();
+
+ OFFlowMod fm = factory().buildFlowModify()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setActions(actions)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .build();
+
+ return fm;
+ }
+
+ @Override
+ public OFFlowDelete buildFlowDel() {
+ Match match = buildMatch();
+
+ long cookie = flowRule().id().value();
+
+ OFFlowDelete fm = factory().buildFlowDelete()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .build();
+
+ return fm;
+ }
+
+ private List<OFAction> buildActions() {
+ List<OFAction> acts = new LinkedList<>();
+ OFAction act;
+ if (treatment == null) {
+ return acts;
+ }
+ for (Instruction i : treatment.immediate()) {
+ switch (i.type()) {
+ case DROP:
+ log.warn("Saw drop action; assigning drop action");
+ return Collections.emptyList();
+ case L2MODIFICATION:
+ act = buildL2Modification(i);
+ if (act != null) {
+ acts.add(buildL2Modification(i));
+ }
+ break;
+ case L3MODIFICATION:
+ act = buildL3Modification(i);
+ if (act != null) {
+ acts.add(buildL3Modification(i));
+ }
+ break;
+ case OUTPUT:
+ OutputInstruction out = (OutputInstruction) i;
+ OFActionOutput.Builder action = factory().actions().buildOutput()
+ .setPort(OFPort.of((int) out.port().toLong()));
+ if (out.port().equals(PortNumber.CONTROLLER)) {
+ action.setMaxLen(OFPCML_NO_BUFFER);
+ }
+ acts.add(action.build());
+ break;
+ case L0MODIFICATION:
+ case GROUP:
+ case TABLE:
+ case METADATA:
+ log.warn("Instruction type {} not supported with protocol version {}",
+ i.type(), factory().getVersion());
+ break;
+ default:
+ log.warn("Instruction type {} not yet implemented.", i.type());
+ }
+ }
+
+ return acts;
+ }
+
+ private OFAction buildL3Modification(Instruction i) {
+ L3ModificationInstruction l3m = (L3ModificationInstruction) i;
+ ModIPInstruction ip;
+ Ip4Address ip4;
+ switch (l3m.subtype()) {
+ case IPV4_SRC:
+ ip = (ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ return factory().actions().setNwSrc(IPv4Address.of(ip4.toInt()));
+ case IPV4_DST:
+ ip = (ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ return factory().actions().setNwDst(IPv4Address.of(ip4.toInt()));
+ default:
+ log.warn("Unimplemented action type {}.", l3m.subtype());
+ break;
+ }
+ return null;
+ }
+
+ private OFAction buildL2Modification(Instruction i) {
+ L2ModificationInstruction l2m = (L2ModificationInstruction) i;
+ ModEtherInstruction eth;
+ switch (l2m.subtype()) {
+ case ETH_DST:
+ eth = (ModEtherInstruction) l2m;
+ return factory().actions().setDlDst(MacAddress.of(eth.mac().toLong()));
+ case ETH_SRC:
+ eth = (ModEtherInstruction) l2m;
+ return factory().actions().setDlSrc(MacAddress.of(eth.mac().toLong()));
+ case VLAN_ID:
+ ModVlanIdInstruction vlanId = (ModVlanIdInstruction) l2m;
+ return factory().actions().setVlanVid(VlanVid.ofVlan(vlanId.vlanId().toShort()));
+ case VLAN_PCP:
+ ModVlanPcpInstruction vlanPcp = (ModVlanPcpInstruction) l2m;
+ return factory().actions().setVlanPcp(VlanPcp.of(vlanPcp.vlanPcp()));
+ case VLAN_POP:
+ return factory().actions().stripVlan();
+ case VLAN_PUSH:
+ return null;
+ default:
+ log.warn("Unimplemented action type {}.", l2m.subtype());
+ break;
+ }
+ return null;
+ }
+
+}
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
new file mode 100644
index 00000000..8918d337
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -0,0 +1,458 @@
+/*
+ * 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.of.flow.impl;
+
+import com.google.common.collect.Lists;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModEtherInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsBosInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModMplsLabelInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanIdInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModVlanPcpInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.PushHeaderInstructions;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction.ModTunnelIdInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction;
+import org.onosproject.net.flow.instructions.L3ModificationInstruction.ModIPInstruction;
+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.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFFlowAdd;
+import org.projectfloodlight.openflow.protocol.OFFlowDelete;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
+import org.projectfloodlight.openflow.protocol.match.Match;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBooleanValue;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.TableId;
+import org.projectfloodlight.openflow.types.TransportPort;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U64;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Flow mod builder for OpenFlow 1.3+.
+ */
+public class FlowModBuilderVer13 extends FlowModBuilder {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private static final int OFPCML_NO_BUFFER = 0xffff;
+
+ private final TrafficTreatment treatment;
+
+ /**
+ * Constructor for a flow mod builder for OpenFlow 1.3.
+ *
+ * @param flowRule the flow rule to transform into a flow mod
+ * @param factory the OpenFlow factory to use to build the flow mod
+ * @param xid the transaction ID
+ */
+ protected FlowModBuilderVer13(FlowRule flowRule, OFFactory factory, Optional<Long> xid) {
+ super(flowRule, factory, xid);
+
+ this.treatment = flowRule.treatment();
+ }
+
+ @Override
+ public OFFlowAdd buildFlowAdd() {
+ Match match = buildMatch();
+ List<OFAction> deferredActions = buildActions(treatment.deferred());
+ List<OFAction> immediateActions = buildActions(treatment.immediate());
+ List<OFInstruction> instructions = Lists.newLinkedList();
+
+
+ if (treatment.clearedDeferred()) {
+ instructions.add(factory().instructions().clearActions());
+ }
+ if (immediateActions.size() > 0) {
+ instructions.add(factory().instructions().applyActions(immediateActions));
+ }
+ if (deferredActions.size() > 0) {
+ instructions.add(factory().instructions().writeActions(deferredActions));
+ }
+ if (treatment.tableTransition() != null) {
+ instructions.add(buildTableGoto(treatment.tableTransition()));
+ }
+ if (treatment.writeMetadata() != null) {
+ instructions.add(buildMetadata(treatment.writeMetadata()));
+ }
+
+ long cookie = flowRule().id().value();
+
+ OFFlowAdd fm = factory().buildFlowAdd()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setInstructions(instructions)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .setTableId(TableId.of(flowRule().tableId()))
+ .build();
+
+ return fm;
+ }
+
+ @Override
+ public OFFlowMod buildFlowMod() {
+ Match match = buildMatch();
+ List<OFAction> deferredActions = buildActions(treatment.deferred());
+ List<OFAction> immediateActions = buildActions(treatment.immediate());
+ List<OFInstruction> instructions = Lists.newLinkedList();
+
+
+ if (immediateActions.size() > 0) {
+ instructions.add(factory().instructions().applyActions(immediateActions));
+ }
+ if (treatment.clearedDeferred()) {
+ instructions.add(factory().instructions().clearActions());
+ }
+ if (deferredActions.size() > 0) {
+ instructions.add(factory().instructions().writeActions(deferredActions));
+ }
+ if (treatment.tableTransition() != null) {
+ instructions.add(buildTableGoto(treatment.tableTransition()));
+ }
+ if (treatment.writeMetadata() != null) {
+ instructions.add(buildMetadata(treatment.writeMetadata()));
+ }
+ if (treatment.metered() != null) {
+ instructions.add(buildMeter(treatment.metered()));
+ }
+
+ long cookie = flowRule().id().value();
+
+ OFFlowMod fm = factory().buildFlowModify()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setInstructions(instructions)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .setTableId(TableId.of(flowRule().tableId()))
+ .build();
+
+ return fm;
+ }
+
+ @Override
+ public OFFlowDelete buildFlowDel() {
+ Match match = buildMatch();
+
+ long cookie = flowRule().id().value();
+
+ OFFlowDelete fm = factory().buildFlowDelete()
+ .setXid(xid)
+ .setCookie(U64.of(cookie))
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setMatch(match)
+ .setFlags(Collections.singleton(OFFlowModFlags.SEND_FLOW_REM))
+ .setPriority(flowRule().priority())
+ .setTableId(TableId.of(flowRule().tableId()))
+ .build();
+
+ return fm;
+ }
+
+ private List<OFAction> buildActions(List<Instruction> treatments) {
+ if (treatment == null) {
+ return Collections.emptyList();
+ }
+
+ boolean tableFound = false;
+ List<OFAction> actions = new LinkedList<>();
+ for (Instruction i : treatments) {
+ switch (i.type()) {
+ case DROP:
+ return Collections.emptyList();
+ case L0MODIFICATION:
+ actions.add(buildL0Modification(i));
+ break;
+ case L2MODIFICATION:
+ actions.add(buildL2Modification(i));
+ break;
+ case L3MODIFICATION:
+ actions.add(buildL3Modification(i));
+ break;
+ case L4MODIFICATION:
+ actions.add(buildL4Modification(i));
+ break;
+ case OUTPUT:
+ OutputInstruction out = (OutputInstruction) i;
+ OFActionOutput.Builder action = factory().actions().buildOutput()
+ .setPort(OFPort.of((int) out.port().toLong()));
+ if (out.port().equals(PortNumber.CONTROLLER)) {
+ action.setMaxLen(OFPCML_NO_BUFFER);
+ }
+ actions.add(action.build());
+ break;
+ case GROUP:
+ GroupInstruction group = (GroupInstruction) i;
+ OFActionGroup.Builder groupBuilder = factory().actions().buildGroup()
+ .setGroup(OFGroup.of(group.groupId().id()));
+ actions.add(groupBuilder.build());
+ break;
+ case TABLE:
+ //FIXME: should not occur here.
+ tableFound = true;
+ break;
+ default:
+ log.warn("Instruction type {} not yet implemented.", i.type());
+ }
+ }
+ if (tableFound && actions.isEmpty()) {
+ // handles the case where there are no actions, but there is
+ // a goto instruction for the next table
+ return Collections.emptyList();
+ }
+ return actions;
+ }
+
+ private OFInstruction buildTableGoto(Instructions.TableTypeTransition i) {
+ OFInstruction instruction = factory().instructions().gotoTable(
+ TableId.of(i.tableId()));
+ return instruction;
+ }
+
+ private OFInstruction buildMetadata(Instructions.MetadataInstruction m) {
+ OFInstruction instruction = factory().instructions().writeMetadata(
+ U64.of(m.metadata()), U64.of(m.metadataMask()));
+ return instruction;
+ }
+
+ private OFInstruction buildMeter(Instructions.MeterInstruction metered) {
+ return factory().instructions().meter(metered.meterId().id());
+ }
+
+
+ private OFAction buildL0Modification(Instruction i) {
+ L0ModificationInstruction l0m = (L0ModificationInstruction) i;
+ switch (l0m.subtype()) {
+ case LAMBDA:
+ return buildModLambdaInstruction((ModLambdaInstruction) i);
+ case OCH:
+ try {
+ return buildModOchSignalInstruction((ModOchSignalInstruction) i);
+ } catch (NoMappingFoundException e) {
+ log.warn(e.getMessage());
+ break;
+ }
+ default:
+ log.warn("Unimplemented action type {}.", l0m.subtype());
+ break;
+ }
+ return null;
+ }
+
+ private OFAction buildModLambdaInstruction(ModLambdaInstruction instruction) {
+ return factory().actions().circuit(factory().oxms().ochSigidBasic(
+ new CircuitSignalID((byte) 1, (byte) 2, instruction.lambda(), (short) 1)));
+ }
+
+ private OFAction buildModOchSignalInstruction(ModOchSignalInstruction instruction) {
+ OchSignal signal = instruction.lambda();
+ byte gridType = OpenFlowValueMapper.lookupGridType(signal.gridType());
+ byte channelSpacing = OpenFlowValueMapper.lookupChannelSpacing(signal.channelSpacing());
+
+ return factory().actions().circuit(factory().oxms().ochSigidBasic(
+ new CircuitSignalID(gridType, channelSpacing,
+ (short) signal.spacingMultiplier(), (short) signal.slotGranularity())
+ ));
+ }
+
+ private OFAction buildL2Modification(Instruction i) {
+ L2ModificationInstruction l2m = (L2ModificationInstruction) i;
+ ModEtherInstruction eth;
+ OFOxm<?> oxm = null;
+ switch (l2m.subtype()) {
+ case ETH_DST:
+ eth = (ModEtherInstruction) l2m;
+ oxm = factory().oxms().ethDst(MacAddress.of(eth.mac().toLong()));
+ break;
+ case ETH_SRC:
+ eth = (ModEtherInstruction) l2m;
+ oxm = factory().oxms().ethSrc(MacAddress.of(eth.mac().toLong()));
+ break;
+ case VLAN_ID:
+ ModVlanIdInstruction vlanId = (ModVlanIdInstruction) l2m;
+ oxm = factory().oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort()));
+ break;
+ case VLAN_PCP:
+ ModVlanPcpInstruction vlanPcp = (ModVlanPcpInstruction) l2m;
+ oxm = factory().oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp()));
+ break;
+ case MPLS_PUSH:
+ PushHeaderInstructions pushHeaderInstructions =
+ (PushHeaderInstructions) l2m;
+ return factory().actions().pushMpls(EthType.of(pushHeaderInstructions
+ .ethernetType().toShort()));
+ case MPLS_POP:
+ PushHeaderInstructions popHeaderInstructions =
+ (PushHeaderInstructions) l2m;
+ return factory().actions().popMpls(EthType.of(popHeaderInstructions
+ .ethernetType().toShort()));
+ case MPLS_LABEL:
+ ModMplsLabelInstruction mplsLabel =
+ (ModMplsLabelInstruction) l2m;
+ oxm = factory().oxms().mplsLabel(U32.of(mplsLabel.mplsLabel().toInt()));
+ break;
+ case MPLS_BOS:
+ ModMplsBosInstruction mplsBos = (ModMplsBosInstruction) l2m;
+ oxm = factory().oxms()
+ .mplsBos(mplsBos.mplsBos() ? OFBooleanValue.TRUE
+ : OFBooleanValue.FALSE);
+ break;
+ case DEC_MPLS_TTL:
+ return factory().actions().decMplsTtl();
+ case VLAN_POP:
+ return factory().actions().popVlan();
+ case VLAN_PUSH:
+ PushHeaderInstructions pushVlanInstruction = (PushHeaderInstructions) l2m;
+ return factory().actions().pushVlan(
+ EthType.of(pushVlanInstruction.ethernetType().toShort()));
+ case TUNNEL_ID:
+ ModTunnelIdInstruction tunnelId = (ModTunnelIdInstruction) l2m;
+ oxm = factory().oxms().tunnelId(U64.of(tunnelId.tunnelId()));
+ break;
+ default:
+ log.warn("Unimplemented action type {}.", l2m.subtype());
+ break;
+ }
+
+ if (oxm != null) {
+ return factory().actions().buildSetField().setField(oxm).build();
+ }
+ return null;
+ }
+
+ private OFAction buildL3Modification(Instruction i) {
+ L3ModificationInstruction l3m = (L3ModificationInstruction) i;
+ ModIPInstruction ip;
+ Ip4Address ip4;
+ Ip6Address ip6;
+ OFOxm<?> oxm = null;
+ switch (l3m.subtype()) {
+ case IPV4_SRC:
+ ip = (ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ oxm = factory().oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+ break;
+ case IPV4_DST:
+ ip = (ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ oxm = factory().oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
+ break;
+ case IPV6_SRC:
+ ip = (ModIPInstruction) i;
+ ip6 = ip.ip().getIp6Address();
+ oxm = factory().oxms().ipv6Src(IPv6Address.of(ip6.toOctets()));
+ break;
+ case IPV6_DST:
+ ip = (ModIPInstruction) i;
+ ip6 = ip.ip().getIp6Address();
+ oxm = factory().oxms().ipv6Dst(IPv6Address.of(ip6.toOctets()));
+ break;
+ case IPV6_FLABEL:
+ ModIPv6FlowLabelInstruction flowLabelInstruction =
+ (ModIPv6FlowLabelInstruction) i;
+ int flowLabel = flowLabelInstruction.flowLabel();
+ oxm = factory().oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
+ break;
+ case DEC_TTL:
+ return factory().actions().decNwTtl();
+ case TTL_IN:
+ return factory().actions().copyTtlIn();
+ case TTL_OUT:
+ return factory().actions().copyTtlOut();
+ default:
+ log.warn("Unimplemented action type {}.", l3m.subtype());
+ break;
+ }
+
+ if (oxm != null) {
+ return factory().actions().buildSetField().setField(oxm).build();
+ }
+ return null;
+ }
+
+ private OFAction buildL4Modification(Instruction i) {
+ L4ModificationInstruction l4m = (L4ModificationInstruction) i;
+ ModTransportPortInstruction tp;
+ OFOxm<?> oxm = null;
+ switch (l4m.subtype()) {
+ case TCP_SRC:
+ tp = (ModTransportPortInstruction) l4m;
+ oxm = factory().oxms().tcpSrc(TransportPort.of(tp.port().toInt()));
+ break;
+ case TCP_DST:
+ tp = (ModTransportPortInstruction) l4m;
+ oxm = factory().oxms().tcpDst(TransportPort.of(tp.port().toInt()));
+ break;
+ case UDP_SRC:
+ tp = (ModTransportPortInstruction) l4m;
+ oxm = factory().oxms().udpSrc(TransportPort.of(tp.port().toInt()));
+ break;
+ case UDP_DST:
+ tp = (ModTransportPortInstruction) l4m;
+ oxm = factory().oxms().udpDst(TransportPort.of(tp.port().toInt()));
+ break;
+ default:
+ log.warn("Unimplemented action type {}.", l4m.subtype());
+ break;
+ }
+
+ if (oxm != null) {
+ return factory().actions().buildSetField().setField(oxm).build();
+ }
+ return null;
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java
new file mode 100644
index 00000000..c4c81afa
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowStatsCollector.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014 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.of.flow.impl;
+
+import org.onlab.util.SharedExecutors;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.TableId;
+import org.slf4j.Logger;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Collects flow statistics for the specified switch.
+ */
+class FlowStatsCollector {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final int SECONDS = 1000;
+
+ private final OpenFlowSwitch sw;
+ private Timer timer;
+ private TimerTask task;
+
+ private int pollInterval;
+
+ /**
+ * Creates a new collector for the given switch and poll frequency.
+ *
+ * @param timer timer to use for scheduling
+ * @param sw switch to pull
+ * @param pollInterval poll frequency in seconds
+ */
+ FlowStatsCollector(Timer timer, OpenFlowSwitch sw, int pollInterval) {
+ this.timer = timer;
+ this.sw = sw;
+ this.pollInterval = pollInterval;
+ }
+
+ /**
+ * Adjusts poll frequency.
+ *
+ * @param pollInterval poll frequency in seconds
+ */
+ synchronized void adjustPollInterval(int pollInterval) {
+ this.pollInterval = pollInterval;
+ task.cancel();
+ task = new InternalTimerTask();
+ timer.scheduleAtFixedRate(task, pollInterval * SECONDS, pollInterval * 1000);
+ }
+
+ private class InternalTimerTask extends TimerTask {
+ @Override
+ public void run() {
+ if (sw.getRole() == RoleState.MASTER) {
+ log.trace("Collecting stats for {}", sw.getStringId());
+ OFFlowStatsRequest request = sw.factory().buildFlowStatsRequest()
+ .setMatch(sw.factory().matchWildcardAll())
+ .setTableId(TableId.ALL)
+ .setOutPort(OFPort.NO_MASK)
+ .build();
+ sw.sendMsg(request);
+ }
+ }
+ }
+
+ public synchronized void start() {
+ // Initially start polling quickly. Then drop down to configured value
+ log.debug("Starting Stats collection thread for {}", sw.getStringId());
+ task = new InternalTimerTask();
+ SharedExecutors.getTimer().scheduleAtFixedRate(task, 1 * SECONDS,
+ pollInterval * SECONDS);
+ }
+
+ public synchronized void stop() {
+ log.debug("Stopping Stats collection thread for {}", sw.getStringId());
+ task.cancel();
+ task = null;
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.java
new file mode 100644
index 00000000..898b286d
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/NoMappingFoundException.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.of.flow.impl;
+
+/**
+ * Thrown to indicate that no mapping for the input value is found.
+ */
+public class NoMappingFoundException extends RuntimeException {
+ /**
+ * Creates an instance with the specified values.
+ *
+ * @param input input value of mapping causing this exception
+ * @param output the desired class which the input value is mapped to
+ */
+ public NoMappingFoundException(Object input, Class<?> output) {
+ super(String.format("No mapping found for %s when converting to %s", input, output.getName()));
+ }
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
new file mode 100644
index 00000000..de079e03
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowRuleProvider.java
@@ -0,0 +1,453 @@
+/*
+ * Copyright 2014 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.of.flow.impl;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalCause;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.flow.CompletedBatchOperation;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleBatchEntry;
+import org.onosproject.net.flow.FlowRuleBatchOperation;
+import org.onosproject.net.flow.FlowRuleExtPayLoad;
+import org.onosproject.net.flow.FlowRuleProvider;
+import org.onosproject.net.flow.FlowRuleProviderRegistry;
+import org.onosproject.net.flow.FlowRuleProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.net.statistic.DefaultLoad;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.onosproject.openflow.controller.ThirdPartyMessage;
+import org.osgi.service.component.ComponentContext;
+import org.projectfloodlight.openflow.protocol.OFBadRequestCode;
+import org.projectfloodlight.openflow.protocol.OFBarrierRequest;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFFlowMod;
+import org.projectfloodlight.openflow.protocol.OFFlowRemoved;
+import org.projectfloodlight.openflow.protocol.OFFlowStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.errormsg.OFBadRequestErrorMsg;
+import org.projectfloodlight.openflow.protocol.errormsg.OFFlowModFailedErrorMsg;
+import org.slf4j.Logger;
+
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.Timer;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to detect network end-station
+ * hosts.
+ */
+@Component(immediate = true)
+public class OpenFlowRuleProvider extends AbstractProvider
+ implements FlowRuleProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ private static final int DEFAULT_POLL_FREQUENCY = 10;
+ @Property(name = "flowPollFrequency", intValue = DEFAULT_POLL_FREQUENCY,
+ label = "Frequency (in seconds) for polling flow statistics")
+ private int flowPollFrequency = DEFAULT_POLL_FREQUENCY;
+
+ private FlowRuleProviderService providerService;
+
+ private final InternalFlowProvider listener = new InternalFlowProvider();
+
+ private Cache<Long, InternalCacheEntry> pendingBatches;
+
+ private final Timer timer = new Timer("onos-openflow-collector");
+ private final Map<Dpid, FlowStatsCollector> collectors = Maps.newHashMap();
+
+ /**
+ * Creates an OpenFlow host provider.
+ */
+ public OpenFlowRuleProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.openflow"));
+ }
+
+ @Activate
+ public void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
+ providerService = providerRegistry.register(this);
+ controller.addListener(listener);
+ controller.addEventListener(listener);
+
+ pendingBatches = createBatchCache();
+ createCollectors();
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ stopCollectors();
+ providerRegistry.unregister(this);
+ providerService = null;
+
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ int newFlowPollFrequency;
+ try {
+ String s = get(properties, "flowPollFrequency");
+ newFlowPollFrequency = isNullOrEmpty(s) ? flowPollFrequency : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException | ClassCastException e) {
+ newFlowPollFrequency = flowPollFrequency;
+ }
+
+ if (newFlowPollFrequency != flowPollFrequency) {
+ flowPollFrequency = newFlowPollFrequency;
+ adjustRate();
+ }
+
+ log.info("Settings: flowPollFrequency={}", flowPollFrequency);
+ }
+
+ private Cache<Long, InternalCacheEntry> createBatchCache() {
+ return CacheBuilder.newBuilder()
+ .expireAfterWrite(10, TimeUnit.SECONDS)
+ .removalListener((RemovalNotification<Long, InternalCacheEntry> notification) -> {
+ if (notification.getCause() == RemovalCause.EXPIRED) {
+ providerService.batchOperationCompleted(notification.getKey(),
+ notification.getValue().failedCompletion());
+ }
+ }).build();
+ }
+
+ private void createCollectors() {
+ controller.getSwitches().forEach(this::createCollector);
+ }
+
+ private void createCollector(OpenFlowSwitch sw) {
+ FlowStatsCollector fsc = new FlowStatsCollector(timer, sw, flowPollFrequency);
+ fsc.start();
+ collectors.put(new Dpid(sw.getId()), fsc);
+ }
+
+ private void stopCollectors() {
+ collectors.values().forEach(FlowStatsCollector::stop);
+ collectors.clear();
+ }
+
+ private void adjustRate() {
+ DefaultLoad.setPollInterval(flowPollFrequency);
+ collectors.values().forEach(fsc -> fsc.adjustPollInterval(flowPollFrequency));
+ }
+
+ @Override
+ public void applyFlowRule(FlowRule... flowRules) {
+ for (FlowRule flowRule : flowRules) {
+ applyRule(flowRule);
+ }
+ }
+
+ private void applyRule(FlowRule flowRule) {
+ OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
+ .uri()));
+ FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
+ if (hasPayload(flowRuleExtPayLoad)) {
+ OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
+ sw.sendMsg(msg);
+ return;
+ }
+ sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+ Optional.empty()).buildFlowAdd());
+ }
+
+ @Override
+ public void removeFlowRule(FlowRule... flowRules) {
+ for (FlowRule flowRule : flowRules) {
+ removeRule(flowRule);
+ }
+ }
+
+ private void removeRule(FlowRule flowRule) {
+ OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(flowRule.deviceId()
+ .uri()));
+ FlowRuleExtPayLoad flowRuleExtPayLoad = flowRule.payLoad();
+ if (hasPayload(flowRuleExtPayLoad)) {
+ OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
+ sw.sendMsg(msg);
+ return;
+ }
+ sw.sendMsg(FlowModBuilder.builder(flowRule, sw.factory(),
+ Optional.empty()).buildFlowDel());
+ }
+
+ @Override
+ public void removeRulesById(ApplicationId id, FlowRule... flowRules) {
+ // TODO: optimize using the ApplicationId
+ removeFlowRule(flowRules);
+ }
+
+ @Override
+ public void executeBatch(FlowRuleBatchOperation batch) {
+
+ pendingBatches.put(batch.id(), new InternalCacheEntry(batch));
+
+ OpenFlowSwitch sw = controller.getSwitch(Dpid.dpid(batch.deviceId()
+ .uri()));
+ OFFlowMod mod;
+ for (FlowRuleBatchEntry fbe : batch.getOperations()) {
+ // flow is the third party privacy flow
+
+ FlowRuleExtPayLoad flowRuleExtPayLoad = fbe.target().payLoad();
+ if (hasPayload(flowRuleExtPayLoad)) {
+ OFMessage msg = new ThirdPartyMessage(flowRuleExtPayLoad.payLoad());
+ sw.sendMsg(msg);
+ continue;
+ }
+ FlowModBuilder builder = FlowModBuilder.builder(fbe.target(), sw
+ .factory(), Optional.of(batch.id()));
+ switch (fbe.operator()) {
+ case ADD:
+ mod = builder.buildFlowAdd();
+ break;
+ case REMOVE:
+ mod = builder.buildFlowDel();
+ break;
+ case MODIFY:
+ mod = builder.buildFlowMod();
+ break;
+ default:
+ log.error("Unsupported batch operation {}; skipping flowmod {}",
+ fbe.operator(), fbe);
+ continue;
+ }
+ sw.sendMsg(mod);
+ }
+ OFBarrierRequest.Builder builder = sw.factory().buildBarrierRequest()
+ .setXid(batch.id());
+ sw.sendMsg(builder.build());
+ }
+
+ private boolean hasPayload(FlowRuleExtPayLoad flowRuleExtPayLoad) {
+ return flowRuleExtPayLoad != null &&
+ flowRuleExtPayLoad.payLoad() != null &&
+ flowRuleExtPayLoad.payLoad().length > 0;
+ }
+
+ private class InternalFlowProvider
+ implements OpenFlowSwitchListener, OpenFlowEventListener {
+
+ @Override
+ public void switchAdded(Dpid dpid) {
+ createCollector(controller.getSwitch(dpid));
+ }
+
+ @Override
+ public void switchRemoved(Dpid dpid) {
+ FlowStatsCollector collector = collectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ }
+
+ @Override
+ public void switchChanged(Dpid dpid) {
+ }
+
+ @Override
+ public void portChanged(Dpid dpid, OFPortStatus status) {
+ // TODO: Decide whether to evict flows internal store.
+ }
+
+ @Override
+ public void handleMessage(Dpid dpid, OFMessage msg) {
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ switch (msg.getType()) {
+ case FLOW_REMOVED:
+ OFFlowRemoved removed = (OFFlowRemoved) msg;
+
+ FlowEntry fr = new FlowEntryBuilder(dpid, removed).build();
+ providerService.flowRemoved(fr);
+ break;
+ case STATS_REPLY:
+ if (((OFStatsReply) msg).getStatsType() == OFStatsType.FLOW) {
+ pushFlowMetrics(dpid, (OFFlowStatsReply) msg);
+ }
+ break;
+ case BARRIER_REPLY:
+ try {
+ InternalCacheEntry entry = pendingBatches.getIfPresent(msg.getXid());
+ if (entry != null) {
+ providerService
+ .batchOperationCompleted(msg.getXid(),
+ entry.completed());
+ } else {
+ log.warn("Received unknown Barrier Reply: {}",
+ msg.getXid());
+ }
+ } finally {
+ pendingBatches.invalidate(msg.getXid());
+ }
+ break;
+ case ERROR:
+ // TODO: This needs to get suppressed in a better way.
+ if (msg instanceof OFBadRequestErrorMsg &&
+ ((OFBadRequestErrorMsg) msg).getCode() == OFBadRequestCode.BAD_TYPE) {
+ log.debug("Received error message {} from {}", msg, dpid);
+ } else {
+ log.warn("Received error message {} from {}", msg, dpid);
+ }
+
+ OFErrorMsg error = (OFErrorMsg) msg;
+ if (error.getErrType() == OFErrorType.FLOW_MOD_FAILED) {
+ OFFlowModFailedErrorMsg fmFailed = (OFFlowModFailedErrorMsg) error;
+ if (fmFailed.getData().getParsedMessage().isPresent()) {
+ OFMessage m = fmFailed.getData().getParsedMessage().get();
+ OFFlowMod fm = (OFFlowMod) m;
+ InternalCacheEntry entry =
+ pendingBatches.getIfPresent(msg.getXid());
+ if (entry != null) {
+ entry.appendFailure(new FlowEntryBuilder(dpid, fm).build());
+ } else {
+ log.error("No matching batch for this error: {}", error);
+ }
+ } else {
+ // FIXME: Potentially add flowtracking to avoid this message.
+ log.error("Flow installation failed but switch didn't"
+ + " tell us which one.");
+ }
+ }
+ break;
+ default:
+ log.debug("Unhandled message type: {}", msg.getType());
+ }
+
+ }
+
+ @Override
+ public void receivedRoleReply(Dpid dpid, RoleState requested,
+ RoleState response) {
+ // Do nothing here for now.
+ }
+
+ private void pushFlowMetrics(Dpid dpid, OFFlowStatsReply replies) {
+
+ DeviceId did = DeviceId.deviceId(Dpid.uri(dpid));
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+
+ List<FlowEntry> flowEntries = replies.getEntries().stream()
+ .map(entry -> new FlowEntryBuilder(dpid, entry).build())
+ .collect(Collectors.toList());
+
+ providerService.pushFlowMetrics(did, flowEntries);
+ }
+ }
+
+ /**
+ * The internal cache entry holding the original request as well as
+ * accumulating the any failures along the way.
+ * <p/>
+ * If this entry is evicted from the cache then the entire operation is
+ * considered failed. Otherwise, only the failures reported by the device
+ * will be propagated up.
+ */
+ private class InternalCacheEntry {
+
+ private final FlowRuleBatchOperation operation;
+ private final Set<FlowRule> failures = Sets.newConcurrentHashSet();
+
+ public InternalCacheEntry(FlowRuleBatchOperation operation) {
+ this.operation = operation;
+ }
+
+ /**
+ * Appends a failed rule to the set of failed items.
+ *
+ * @param rule the failed rule
+ */
+ public void appendFailure(FlowRule rule) {
+ failures.add(rule);
+ }
+
+ /**
+ * Fails the entire batch and returns the failed operation.
+ *
+ * @return the failed operation
+ */
+ public CompletedBatchOperation failedCompletion() {
+ Set<FlowRule> fails = operation.getOperations().stream()
+ .map(op -> op.target()).collect(Collectors.toSet());
+ return new CompletedBatchOperation(false,
+ Collections
+ .unmodifiableSet(fails),
+ operation.deviceId());
+ }
+
+ /**
+ * Returns the completed operation and whether the batch suceeded.
+ *
+ * @return the completed operation
+ */
+ public CompletedBatchOperation completed() {
+ return new CompletedBatchOperation(
+ failures.isEmpty(),
+ Collections
+ .unmodifiableSet(failures),
+ operation.deviceId());
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java
new file mode 100644
index 00000000..2f0831c6
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/OpenFlowValueMapper.java
@@ -0,0 +1,152 @@
+/*
+ * 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.of.flow.impl;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.EnumHashBiMap;
+import org.onosproject.net.ChannelSpacing;
+import org.onosproject.net.GridType;
+import org.onosproject.net.OchSignalType;
+
+/**
+ * Collection of helper methods to convert protocol agnostic models to values used in OpenFlow spec.
+ */
+final class OpenFlowValueMapper {
+
+ // prohibit instantiation
+ private OpenFlowValueMapper() {}
+
+ private static final BiMap<GridType, Byte> GRID_TYPES = EnumHashBiMap.create(GridType.class);
+ static {
+ // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values
+ GRID_TYPES.put(GridType.DWDM, (byte) 1); // OFPGRIDT_DWDM of enum ofp_grid_type
+ GRID_TYPES.put(GridType.CWDM, (byte) 2); // OFPGRIDT_CWDM of enum ofp_grid_type
+ GRID_TYPES.put(GridType.FLEX, (byte) 3); // OFPGRIDT_FLEX of enum ofp_grid_type
+ }
+
+ private static final BiMap<ChannelSpacing, Byte> CHANNEL_SPACING = EnumHashBiMap.create(ChannelSpacing.class);
+ static {
+ // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values
+ CHANNEL_SPACING.put(ChannelSpacing.CHL_100GHZ, (byte) 1); // OFPCS_100GHZ of enum ofp_chl_spacing
+ CHANNEL_SPACING.put(ChannelSpacing.CHL_50GHZ, (byte) 2); // OFPCS_50GHZ of enum ofp_chl_spacing
+ CHANNEL_SPACING.put(ChannelSpacing.CHL_25GHZ, (byte) 3); // OFPCS_25GHZ of enum ofp_chl_spacing
+ CHANNEL_SPACING.put(ChannelSpacing.CHL_12P5GHZ, (byte) 4); // OFPCS_12P5GHZ of enum ofp_chl_spacing
+ CHANNEL_SPACING.put(ChannelSpacing.CHL_6P25GHZ, (byte) 5); // OFPCS_6P25GHZ of enum ofp_chl_spacing
+ }
+
+ private static final BiMap<OchSignalType, Byte> OCH_SIGNAL_TYPES = EnumHashBiMap.create(OchSignalType.class);
+ static {
+ // See ONF "Optical Transport Protocol Extensions Version 1.0" for the following values
+ OCH_SIGNAL_TYPES.put(OchSignalType.FIXED_GRID, (byte) 1); // OFPOCHT_FIX_GRID of enum ofp_och_signal_type
+ OCH_SIGNAL_TYPES.put(OchSignalType.FLEX_GRID, (byte) 2); // OFPOCHT_FLEX_GRID of enum ofp_och_signal_type
+ }
+
+ /**
+ * Looks up the specified input value to the corresponding value with the specified map.
+ *
+ * @param map bidirectional mapping
+ * @param input input value
+ * @param cls class of output value
+ * @param <I> type of input value
+ * @param <O> type of output value
+ * @return the corresponding value stored in the specified map
+ * @throws NoMappingFoundException if no corresponding value is found
+ */
+ private static <I, O> O lookup(BiMap<I, O> map, I input, Class<O> cls) {
+ if (!map.containsKey(input)) {
+ throw new NoMappingFoundException(input, cls);
+ }
+
+ return map.get(input);
+ }
+
+ /**
+ * Looks up the corresponding byte value defined in
+ * ONF "Optical Transport Protocol Extensions Version 1.0"
+ * from the specified {@link GridType} instance.
+ *
+ * @param type grid type
+ * @return the byte value corresponding to the specified grid type
+ * @throws NoMappingFoundException if the specified grid type is not found
+ */
+ static byte lookupGridType(GridType type) {
+ return lookup(GRID_TYPES, type, Byte.class);
+ }
+
+ /**
+ * Looks up the corresponding {@link GridType} instance
+ * from the specified byte value for grid type
+ * defined in ONF "Optical Transport Protocol Extensions Version 1.0".
+ *
+ * @param type byte value as grid type defined the spec
+ * @return the corresponding GridType instance
+ */
+ static GridType lookupGridType(byte type) {
+ return lookup(GRID_TYPES.inverse(), type, GridType.class);
+ }
+
+ /**
+ * Looks up the corresponding byte value for channel spacing defined in
+ * ONF "Optical Transport Protocol Extensions Version 1.0"
+ * from the specified {@link ChannelSpacing} instance.
+ *
+ * @param spacing channel spacing
+ * @return byte value corresponding to the specified channel spacing
+ * @throws NoMappingFoundException if the specified channel spacing is not found
+ */
+ static byte lookupChannelSpacing(ChannelSpacing spacing) {
+ return lookup(CHANNEL_SPACING, spacing, Byte.class);
+ }
+
+ /**
+ * Looks up the corresponding {@link ChannelSpacing} instance
+ * from the specified byte value for channel spacing
+ * defined in ONF "Optical Transport Protocol Extensions Version 1.0".
+ *
+ * @param spacing byte value as channel spacing defined the spec
+ * @return the corresponding ChannelSpacing instance
+ * @throws NoMappingFoundException if the specified channel spacing is not found
+ */
+ static ChannelSpacing lookupChannelSpacing(byte spacing) {
+ return lookup(CHANNEL_SPACING.inverse(), spacing, ChannelSpacing.class);
+ }
+
+ /**
+ * Looks up the corresponding byte value for Och signal type defined in
+ * ONF "Optical Transport Protocol Extensions Version 1.0"
+ * from the specified {@link OchSignalType} instance.
+ *
+ * @param signalType optical signal type
+ * @return byte value corresponding to the specified OCh signal type
+ * @throws NoMappingFoundException if the specified Och signal type is not found
+ */
+ static byte lookupOchSignalType(OchSignalType signalType) {
+ return lookup(OCH_SIGNAL_TYPES, signalType, Byte.class);
+ }
+
+ /**
+ * Looks up the the corresponding {@link OchSignalType} instance
+ * from the specified byte value for Och signal type defined in
+ * ONF "Optical Transport Protocol Extensions Version 1.0".
+ *
+ * @param signalType byte value as Och singal type defined the spec
+ * @return the corresponding OchSignalType instance
+ * @throws NoMappingFoundException if the specified Och signal type is not found
+ */
+ static OchSignalType lookupOchSignalType(byte signalType) {
+ return lookup(OCH_SIGNAL_TYPES.inverse(), signalType, OchSignalType.class);
+ }
+}
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java
new file mode 100644
index 00000000..2acc1510
--- /dev/null
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2014 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 OpenFlow controller as a means of ending and receiving flow information.
+ */
+package org.onosproject.provider.of.flow.impl;
diff --git a/framework/src/onos/providers/openflow/group/pom.xml b/framework/src/onos/providers/openflow/group/pom.xml
new file mode 100644
index 00000000..97ac5ffe
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/pom.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+<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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-provider-group</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS OpenFlow protocol group provider</description>
+
+</project> \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java
new file mode 100644
index 00000000..b9de7c0f
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupBucketEntryBuilder.java
@@ -0,0 +1,343 @@
+/*
+ * 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.of.group.impl;
+
+import com.google.common.collect.Lists;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.MplsLabel;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.Lambda;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
+import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlIn;
+import org.projectfloodlight.openflow.protocol.action.OFActionCopyTtlOut;
+import org.projectfloodlight.openflow.protocol.action.OFActionDecMplsTtl;
+import org.projectfloodlight.openflow.protocol.action.OFActionDecNwTtl;
+import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
+import org.projectfloodlight.openflow.protocol.action.OFActionPushMpls;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetField;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwDst;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetNwSrc;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanPcp;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetVlanVid;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxmOchSigidBasic;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.U8;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Builder for GroupBucketEntry.
+ */
+public class GroupBucketEntryBuilder {
+
+ private List<OFBucket> ofBuckets;
+ private OFGroupType type;
+
+ private final Logger log = getLogger(getClass());
+
+ /**
+ * Creates a builder.
+ *
+ * @param ofBuckets list of OFBucket
+ * @param type Group type
+ */
+ public GroupBucketEntryBuilder(List<OFBucket> ofBuckets, OFGroupType type) {
+ this.ofBuckets = ofBuckets;
+ this.type = type;
+ }
+
+ /**
+ * Builds a GroupBuckets.
+ *
+ * @return GroupBuckets object, a list of GroupBuckets
+ */
+ public GroupBuckets build() {
+ List<GroupBucket> bucketList = Lists.newArrayList();
+
+ for (OFBucket bucket: ofBuckets) {
+ TrafficTreatment treatment = buildTreatment(bucket.getActions());
+ // TODO: Use GroupBucketEntry
+ GroupBucket groupBucket = null;
+ switch (type) {
+ case INDIRECT:
+ groupBucket =
+ DefaultGroupBucket.createIndirectGroupBucket(treatment);
+ break;
+ case SELECT:
+ groupBucket =
+ DefaultGroupBucket.createSelectGroupBucket(treatment);
+ break;
+ case FF:
+ PortNumber port =
+ PortNumber.portNumber(bucket.getWatchPort().getPortNumber());
+ GroupId groupId =
+ new DefaultGroupId(bucket.getWatchGroup().getGroupNumber());
+ groupBucket =
+ DefaultGroupBucket.createFailoverGroupBucket(treatment,
+ port, groupId);
+ break;
+ default:
+ log.error("Unsupported Group type : {}", type);
+ }
+ if (groupBucket != null) {
+ bucketList.add(groupBucket);
+ }
+ }
+ return new GroupBuckets(bucketList);
+ }
+
+
+ private TrafficTreatment buildTreatment(List<OFAction> actions) {
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ // If this is a drop rule
+ if (actions.size() == 0) {
+ builder.drop();
+ return builder.build();
+ }
+ for (OFAction act : actions) {
+ switch (act.getType()) {
+ case OUTPUT:
+ OFActionOutput out = (OFActionOutput) act;
+ builder.setOutput(
+ PortNumber.portNumber(out.getPort().getPortNumber()));
+ break;
+ case SET_VLAN_VID:
+ OFActionSetVlanVid vlan = (OFActionSetVlanVid) act;
+ builder.setVlanId(VlanId.vlanId(vlan.getVlanVid().getVlan()));
+ break;
+ case SET_VLAN_PCP:
+ OFActionSetVlanPcp pcp = (OFActionSetVlanPcp) act;
+ builder.setVlanPcp(pcp.getVlanPcp().getValue());
+ break;
+ case POP_VLAN:
+ builder.popVlan();
+ break;
+ case PUSH_VLAN:
+ builder.pushVlan();
+ break;
+ case SET_DL_DST:
+ OFActionSetDlDst dldst = (OFActionSetDlDst) act;
+ builder.setEthDst(
+ MacAddress.valueOf(dldst.getDlAddr().getLong()));
+ break;
+ case SET_DL_SRC:
+ OFActionSetDlSrc dlsrc = (OFActionSetDlSrc) act;
+ builder.setEthSrc(
+ MacAddress.valueOf(dlsrc.getDlAddr().getLong()));
+
+ break;
+ case SET_NW_DST:
+ OFActionSetNwDst nwdst = (OFActionSetNwDst) act;
+ IPv4Address di = nwdst.getNwAddr();
+ builder.setIpDst(Ip4Address.valueOf(di.getInt()));
+ break;
+ case SET_NW_SRC:
+ OFActionSetNwSrc nwsrc = (OFActionSetNwSrc) act;
+ IPv4Address si = nwsrc.getNwAddr();
+ builder.setIpSrc(Ip4Address.valueOf(si.getInt()));
+ break;
+ case EXPERIMENTER:
+ OFActionExperimenter exp = (OFActionExperimenter) act;
+ if (exp.getExperimenter() == 0x80005A06 ||
+ exp.getExperimenter() == 0x748771) {
+ OFActionCircuit ct = (OFActionCircuit) exp;
+ short lambda = ((OFOxmOchSigidBasic) ct.getField()).getValue().getChannelNumber();
+ builder.add(Instructions.modL0Lambda(Lambda.indexedLambda(lambda)));
+ } else {
+ log.warn("Unsupported OFActionExperimenter {}", exp.getExperimenter());
+ }
+ break;
+ case SET_FIELD:
+ OFActionSetField setField = (OFActionSetField) act;
+ handleSetField(builder, setField.getField());
+ break;
+ case POP_MPLS:
+ OFActionPopMpls popMpls = (OFActionPopMpls) act;
+ builder.popMpls((short) popMpls.getEthertype().getValue());
+ break;
+ case PUSH_MPLS:
+ OFActionPushMpls pushMpls = (OFActionPushMpls) act;
+ builder.pushMpls();
+ break;
+ case COPY_TTL_IN:
+ OFActionCopyTtlIn copyTtlIn = (OFActionCopyTtlIn) act;
+ builder.copyTtlIn();
+ break;
+ case COPY_TTL_OUT:
+ OFActionCopyTtlOut copyTtlOut = (OFActionCopyTtlOut) act;
+ builder.copyTtlOut();
+ break;
+ case DEC_MPLS_TTL:
+ OFActionDecMplsTtl decMplsTtl = (OFActionDecMplsTtl) act;
+ builder.decMplsTtl();
+ break;
+ case DEC_NW_TTL:
+ OFActionDecNwTtl decNwTtl = (OFActionDecNwTtl) act;
+ builder.decNwTtl();
+ break;
+ case GROUP:
+ OFActionGroup grp = (OFActionGroup) act;
+ builder.group(new DefaultGroupId(grp.getGroup().getGroupNumber()));
+ break;
+ case SET_TP_DST:
+ case SET_TP_SRC:
+ case POP_PBB:
+ case PUSH_PBB:
+ case SET_MPLS_LABEL:
+ case SET_MPLS_TC:
+ case SET_MPLS_TTL:
+ case SET_NW_ECN:
+ case SET_NW_TOS:
+ case SET_NW_TTL:
+ case SET_QUEUE:
+ case STRIP_VLAN:
+ case ENQUEUE:
+ default:
+ log.warn("Action type {} not yet implemented.", act.getType());
+ }
+ }
+
+ return builder.build();
+ }
+
+ private void handleSetField(TrafficTreatment.Builder builder, OFOxm<?> oxm) {
+ switch (oxm.getMatchField().id) {
+ case VLAN_PCP:
+ @SuppressWarnings("unchecked")
+ OFOxm<VlanPcp> vlanpcp = (OFOxm<VlanPcp>) oxm;
+ builder.setVlanPcp(vlanpcp.getValue().getValue());
+ break;
+ case VLAN_VID:
+ @SuppressWarnings("unchecked")
+ OFOxm<OFVlanVidMatch> vlanvid = (OFOxm<OFVlanVidMatch>) oxm;
+ builder.setVlanId(VlanId.vlanId(vlanvid.getValue().getVlan()));
+ break;
+ case ETH_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethdst =
+ (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+ builder.setEthDst(MacAddress.valueOf(ethdst.getValue().getLong()));
+ break;
+ case ETH_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<org.projectfloodlight.openflow.types.MacAddress> ethsrc =
+ (OFOxm<org.projectfloodlight.openflow.types.MacAddress>) oxm;
+ builder.setEthSrc(MacAddress.valueOf(ethsrc.getValue().getLong()));
+ break;
+ case IPV4_DST:
+ @SuppressWarnings("unchecked")
+ OFOxm<IPv4Address> ip4dst = (OFOxm<IPv4Address>) oxm;
+ builder.setIpDst(Ip4Address.valueOf(ip4dst.getValue().getInt()));
+ break;
+ case IPV4_SRC:
+ @SuppressWarnings("unchecked")
+ OFOxm<IPv4Address> ip4src = (OFOxm<IPv4Address>) oxm;
+ builder.setIpSrc(Ip4Address.valueOf(ip4src.getValue().getInt()));
+ break;
+ case MPLS_LABEL:
+ @SuppressWarnings("unchecked")
+ OFOxm<U32> labelId = (OFOxm<U32>) oxm;
+ builder.setMpls(MplsLabel.mplsLabel((int) labelId.getValue().getValue()));
+ break;
+ case MPLS_BOS:
+ @SuppressWarnings("unchecked")
+ OFOxm<U8> mplsBos = (OFOxm<U8>) oxm;
+ builder.setMplsBos(mplsBos.getValue() == U8.ZERO ? false : true);
+ break;
+ case ARP_OP:
+ case ARP_SHA:
+ case ARP_SPA:
+ case ARP_THA:
+ case ARP_TPA:
+ case BSN_EGR_PORT_GROUP_ID:
+ case BSN_GLOBAL_VRF_ALLOWED:
+ case BSN_IN_PORTS_128:
+ case BSN_L3_DST_CLASS_ID:
+ case BSN_L3_INTERFACE_CLASS_ID:
+ case BSN_L3_SRC_CLASS_ID:
+ case BSN_LAG_ID:
+ case BSN_TCP_FLAGS:
+ case BSN_UDF0:
+ case BSN_UDF1:
+ case BSN_UDF2:
+ case BSN_UDF3:
+ case BSN_UDF4:
+ case BSN_UDF5:
+ case BSN_UDF6:
+ case BSN_UDF7:
+ case BSN_VLAN_XLATE_PORT_GROUP_ID:
+ case BSN_VRF:
+ case ETH_TYPE:
+ case ICMPV4_CODE:
+ case ICMPV4_TYPE:
+ case ICMPV6_CODE:
+ case ICMPV6_TYPE:
+ case IN_PHY_PORT:
+ case IN_PORT:
+ case IPV6_DST:
+ case IPV6_FLABEL:
+ case IPV6_ND_SLL:
+ case IPV6_ND_TARGET:
+ case IPV6_ND_TLL:
+ case IPV6_SRC:
+ case IP_DSCP:
+ case IP_ECN:
+ case IP_PROTO:
+ case METADATA:
+ case MPLS_TC:
+ case OCH_SIGID:
+ case OCH_SIGID_BASIC:
+ case OCH_SIGTYPE:
+ case OCH_SIGTYPE_BASIC:
+ case SCTP_DST:
+ case SCTP_SRC:
+ case TCP_DST:
+ case TCP_SRC:
+ case TUNNEL_ID:
+ case UDP_DST:
+ case UDP_SRC:
+ default:
+ log.warn("Set field type {} not yet implemented.", oxm.getMatchField().id);
+ break;
+ }
+ }
+}
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
new file mode 100644
index 00000000..d5804f44
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupModBuilder.java
@@ -0,0 +1,376 @@
+/*
+ * 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.of.group.impl;
+
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.flow.instructions.L0ModificationInstruction;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
+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.projectfloodlight.openflow.protocol.OFBucket;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFGroupAdd;
+import org.projectfloodlight.openflow.protocol.OFGroupDelete;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
+import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
+import org.projectfloodlight.openflow.types.CircuitSignalID;
+import org.projectfloodlight.openflow.types.EthType;
+import org.projectfloodlight.openflow.types.IPv4Address;
+import org.projectfloodlight.openflow.types.IPv6Address;
+import org.projectfloodlight.openflow.types.IPv6FlowLabel;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBooleanValue;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.projectfloodlight.openflow.types.OFVlanVidMatch;
+import org.projectfloodlight.openflow.types.U32;
+import org.projectfloodlight.openflow.types.VlanPcp;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Builder for GroupMod.
+ */
+public final class GroupModBuilder {
+
+ private GroupBuckets buckets;
+ private GroupId groupId;
+ private GroupDescription.Type type;
+ private OFFactory factory;
+ private Long xid;
+
+ private final Logger log = getLogger(getClass());
+
+ private static final int OFPCML_NO_BUFFER = 0xffff;
+
+ private GroupModBuilder(GroupBuckets buckets, GroupId groupId,
+ GroupDescription.Type type, OFFactory factory,
+ Optional<Long> xid) {
+ this.buckets = buckets;
+ this.groupId = groupId;
+ this.type = type;
+ this.factory = factory;
+ this.xid = xid.orElse((long) 0);
+ }
+
+ /**
+ * Creates a builder for GroupMod.
+ *
+ * @param buckets GroupBuckets object
+ * @param groupId Group Id to create
+ * @param type Group type
+ * @param factory OFFactory object
+ * @param xid transaction ID
+ * @return GroupModBuilder object
+ */
+ public static GroupModBuilder builder(GroupBuckets buckets, GroupId groupId,
+ GroupDescription.Type type, OFFactory factory,
+ Optional<Long> xid) {
+
+ return new GroupModBuilder(buckets, groupId, type, factory, xid);
+ }
+
+ /**
+ * Builds the GroupAdd OF message.
+ *
+ * @return GroupAdd OF message
+ */
+ public OFGroupAdd buildGroupAdd() {
+
+ List<OFBucket> ofBuckets = new ArrayList<OFBucket>();
+ for (GroupBucket bucket: buckets.buckets()) {
+ List<OFAction> actions = buildActions(bucket.treatment());
+
+ OFBucket.Builder bucketBuilder = factory.buildBucket();
+ bucketBuilder.setActions(actions);
+ if (type == GroupDescription.Type.SELECT) {
+ bucketBuilder.setWeight(1);
+ }
+ bucketBuilder.setWatchGroup(OFGroup.ANY);
+ bucketBuilder.setWatchPort(OFPort.ANY);
+ OFBucket ofBucket = bucketBuilder.build();
+ ofBuckets.add(ofBucket);
+ }
+
+ OFGroupAdd groupMsg = factory.buildGroupAdd()
+ .setGroup(OFGroup.of(groupId.id()))
+ .setBuckets(ofBuckets)
+ .setGroupType(getOFGroupType(type))
+ .setXid(xid)
+ .build();
+
+ return groupMsg;
+ }
+
+ /**
+ * Builds the GroupMod OF message.
+ *
+ * @return GroupMod OF message
+ */
+ public OFGroupMod buildGroupMod() {
+ List<OFBucket> ofBuckets = new ArrayList<OFBucket>();
+ for (GroupBucket bucket: buckets.buckets()) {
+ List<OFAction> actions = buildActions(bucket.treatment());
+
+ OFBucket.Builder bucketBuilder = factory.buildBucket();
+ bucketBuilder.setActions(actions);
+ if (type == GroupDescription.Type.SELECT) {
+ bucketBuilder.setWeight(1);
+ }
+ bucketBuilder.setWatchGroup(OFGroup.ANY);
+ bucketBuilder.setWatchPort(OFPort.ANY);
+ OFBucket ofBucket = bucketBuilder.build();
+ ofBuckets.add(ofBucket);
+ }
+
+ OFGroupMod groupMsg = factory.buildGroupModify()
+ .setGroup(OFGroup.of(groupId.id()))
+ .setBuckets(ofBuckets)
+ .setGroupType(getOFGroupType(type))
+ .setXid(xid)
+ .build();
+
+ return groupMsg;
+ }
+
+ /**
+ * Builds the GroupDel OF message.
+ *
+ * @return GroupDel OF message
+ */
+ public OFGroupDelete buildGroupDel() {
+
+ OFGroupDelete groupMsg = factory.buildGroupDelete()
+ .setGroup(OFGroup.of(groupId.id()))
+ .setGroupType(OFGroupType.SELECT)
+ .setXid(xid)
+ .build();
+
+ return groupMsg;
+ }
+
+ private List<OFAction> buildActions(TrafficTreatment treatment) {
+ if (treatment == null) {
+ return Collections.emptyList();
+ }
+
+ List<OFAction> actions = new LinkedList<>();
+ for (Instruction i : treatment.allInstructions()) {
+ switch (i.type()) {
+ case DROP:
+ log.warn("Saw drop action; assigning drop action");
+ return Collections.emptyList();
+ case L0MODIFICATION:
+ actions.add(buildL0Modification(i));
+ break;
+ case L2MODIFICATION:
+ actions.add(buildL2Modification(i));
+ break;
+ case L3MODIFICATION:
+ actions.add(buildL3Modification(i));
+ break;
+ case OUTPUT:
+ Instructions.OutputInstruction out =
+ (Instructions.OutputInstruction) i;
+ OFActionOutput.Builder action = factory.actions().buildOutput()
+ .setPort(OFPort.of((int) out.port().toLong()));
+ if (out.port().equals(PortNumber.CONTROLLER)) {
+ action.setMaxLen(OFPCML_NO_BUFFER);
+ }
+ actions.add(action.build());
+ break;
+ case GROUP:
+ Instructions.GroupInstruction grp =
+ (Instructions.GroupInstruction) i;
+ OFActionGroup.Builder actgrp = factory.actions().buildGroup()
+ .setGroup(OFGroup.of(grp.groupId().id()));
+ actions.add(actgrp.build());
+ break;
+ default:
+ log.warn("Instruction type {} not yet implemented.", i.type());
+ }
+ }
+
+ return actions;
+ }
+
+ private OFAction buildL0Modification(Instruction i) {
+ L0ModificationInstruction l0m = (L0ModificationInstruction) i;
+ switch (l0m.subtype()) {
+ case LAMBDA:
+ L0ModificationInstruction.ModLambdaInstruction ml =
+ (L0ModificationInstruction.ModLambdaInstruction) i;
+ return factory.actions().circuit(factory.oxms().ochSigidBasic(
+ new CircuitSignalID((byte) 1, (byte) 2, ml.lambda(), (short) 1)));
+ default:
+ log.warn("Unimplemented action type {}.", l0m.subtype());
+ break;
+ }
+ return null;
+ }
+
+ private OFAction buildL2Modification(Instruction i) {
+ L2ModificationInstruction l2m = (L2ModificationInstruction) i;
+ L2ModificationInstruction.ModEtherInstruction eth;
+ OFOxm<?> oxm = null;
+ switch (l2m.subtype()) {
+ case ETH_DST:
+ eth = (L2ModificationInstruction.ModEtherInstruction) l2m;
+ oxm = factory.oxms().ethDst(MacAddress.of(eth.mac().toLong()));
+ break;
+ case ETH_SRC:
+ eth = (L2ModificationInstruction.ModEtherInstruction) l2m;
+ oxm = factory.oxms().ethSrc(MacAddress.of(eth.mac().toLong()));
+ break;
+ case VLAN_ID:
+ L2ModificationInstruction.ModVlanIdInstruction vlanId =
+ (L2ModificationInstruction.ModVlanIdInstruction) l2m;
+ oxm = factory.oxms().vlanVid(OFVlanVidMatch.ofVlan(vlanId.vlanId().toShort()));
+ break;
+ case VLAN_PCP:
+ L2ModificationInstruction.ModVlanPcpInstruction vlanPcp =
+ (L2ModificationInstruction.ModVlanPcpInstruction) l2m;
+ oxm = factory.oxms().vlanPcp(VlanPcp.of(vlanPcp.vlanPcp()));
+ break;
+ case VLAN_POP:
+ return factory.actions().popVlan();
+ case VLAN_PUSH:
+ L2ModificationInstruction.PushHeaderInstructions pushVlanInstruction
+ = (L2ModificationInstruction.PushHeaderInstructions) l2m;
+ return factory.actions().pushVlan(
+ EthType.of(pushVlanInstruction.ethernetType().toShort()));
+ case MPLS_PUSH:
+ L2ModificationInstruction.PushHeaderInstructions pushHeaderInstructions =
+ (L2ModificationInstruction.PushHeaderInstructions) l2m;
+ return factory.actions().pushMpls(EthType.of(pushHeaderInstructions
+ .ethernetType().toShort()));
+ case MPLS_POP:
+ L2ModificationInstruction.PushHeaderInstructions popHeaderInstructions =
+ (L2ModificationInstruction.PushHeaderInstructions) l2m;
+ return factory.actions().popMpls(EthType.of(popHeaderInstructions
+ .ethernetType().toShort()));
+ case MPLS_LABEL:
+ L2ModificationInstruction.ModMplsLabelInstruction mplsLabel =
+ (L2ModificationInstruction.ModMplsLabelInstruction) l2m;
+ oxm = factory.oxms().mplsLabel(U32.of(mplsLabel.mplsLabel().toInt()));
+ break;
+ case MPLS_BOS:
+ L2ModificationInstruction.ModMplsBosInstruction mplsBos =
+ (L2ModificationInstruction.ModMplsBosInstruction) l2m;
+ oxm = factory.oxms()
+ .mplsBos(mplsBos.mplsBos() ? OFBooleanValue.TRUE
+ : OFBooleanValue.FALSE);
+ break;
+ case DEC_MPLS_TTL:
+ return factory.actions().decMplsTtl();
+ default:
+ log.warn("Unimplemented action type {}.", l2m.subtype());
+ break;
+ }
+
+ if (oxm != null) {
+ return factory.actions().buildSetField().setField(oxm).build();
+ }
+ return null;
+ }
+
+ private OFAction buildL3Modification(Instruction i) {
+ L3ModificationInstruction l3m = (L3ModificationInstruction) i;
+ L3ModificationInstruction.ModIPInstruction ip;
+ Ip4Address ip4;
+ Ip6Address ip6;
+ OFOxm<?> oxm = null;
+ switch (l3m.subtype()) {
+ case IPV4_SRC:
+ ip = (L3ModificationInstruction.ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ oxm = factory.oxms().ipv4Src(IPv4Address.of(ip4.toInt()));
+ break;
+ case IPV4_DST:
+ ip = (L3ModificationInstruction.ModIPInstruction) i;
+ ip4 = ip.ip().getIp4Address();
+ oxm = factory.oxms().ipv4Dst(IPv4Address.of(ip4.toInt()));
+ break;
+ case IPV6_SRC:
+ ip = (L3ModificationInstruction.ModIPInstruction) i;
+ ip6 = ip.ip().getIp6Address();
+ oxm = factory.oxms().ipv6Src(IPv6Address.of(ip6.toOctets()));
+ break;
+ case IPV6_DST:
+ ip = (L3ModificationInstruction.ModIPInstruction) i;
+ ip6 = ip.ip().getIp6Address();
+ oxm = factory.oxms().ipv6Dst(IPv6Address.of(ip6.toOctets()));
+ break;
+ case IPV6_FLABEL:
+ L3ModificationInstruction.ModIPv6FlowLabelInstruction flowLabelInstruction =
+ (L3ModificationInstruction.ModIPv6FlowLabelInstruction) i;
+ int flowLabel = flowLabelInstruction.flowLabel();
+ oxm = factory.oxms().ipv6Flabel(IPv6FlowLabel.of(flowLabel));
+ break;
+ case DEC_TTL:
+ return factory.actions().decNwTtl();
+ case TTL_IN:
+ return factory.actions().copyTtlIn();
+ case TTL_OUT:
+ return factory.actions().copyTtlOut();
+ default:
+ log.warn("Unimplemented action type {}.", l3m.subtype());
+ break;
+ }
+
+ if (oxm != null) {
+ return factory.actions().buildSetField().setField(oxm).build();
+ }
+ return null;
+ }
+
+ private OFGroupType getOFGroupType(GroupDescription.Type groupType) {
+ switch (groupType) {
+ case INDIRECT:
+ return OFGroupType.INDIRECT;
+ case SELECT:
+ return OFGroupType.SELECT;
+ case FAILOVER:
+ return OFGroupType.FF;
+ case ALL:
+ return OFGroupType.ALL;
+ default:
+ log.error("Unsupported group type : {}", groupType);
+ break;
+ }
+ return null;
+ }
+}
+
diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
new file mode 100644
index 00000000..9816426b
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/GroupStatsCollector.java
@@ -0,0 +1,111 @@
+/*
+ * 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.of.group.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsRequest;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsRequest;
+import org.projectfloodlight.openflow.types.OFGroup;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Sends Group Stats Request and collect the group statistics with a time interval.
+ */
+public class GroupStatsCollector implements TimerTask {
+
+ private final HashedWheelTimer timer = Timer.getTimer();
+ private final OpenFlowSwitch sw;
+ private final Logger log = getLogger(getClass());
+ private final int refreshInterval;
+
+ private Timeout timeout;
+
+ private boolean stopTimer = false;
+
+ /**
+ * Creates a GroupStatsCollector object.
+ *
+ * @param sw Open Flow switch
+ * @param interval time interval for collecting group statistic
+ */
+ public GroupStatsCollector(OpenFlowSwitch sw, int interval) {
+ this.sw = sw;
+ this.refreshInterval = interval;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ log.trace("Collecting stats for {}", sw.getStringId());
+
+ sendGroupStatistic();
+
+ if (!this.stopTimer) {
+ log.trace("Scheduling stats collection in {} seconds for {}",
+ this.refreshInterval, this.sw.getStringId());
+ timeout.getTimer().newTimeout(this, refreshInterval,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ private void sendGroupStatistic() {
+ if (log.isTraceEnabled()) {
+ log.trace("sendGroupStatistics {}:{}", sw.getStringId(), sw.getRole());
+ }
+ if (sw.getRole() != RoleState.MASTER) {
+ return;
+ }
+ Long statsXid = OpenFlowGroupProvider.getXidAndAdd(2);
+ OFGroupStatsRequest statsRequest = sw.factory().buildGroupStatsRequest()
+ .setGroup(OFGroup.ALL)
+ .setXid(statsXid)
+ .build();
+ sw.sendMsg(statsRequest);
+
+ Long descXid = statsXid + 1;
+ OFGroupDescStatsRequest descStatsRequest =
+ sw.factory().buildGroupDescStatsRequest()
+ .setXid(descXid)
+ .build();
+ sw.sendMsg(descStatsRequest);
+ }
+
+ /**
+ * Starts the collector.
+ */
+ public void start() {
+ log.info("Starting Group Stats collection thread for {}", sw.getStringId());
+ timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the collector.
+ */
+ public void stop() {
+ log.info("Stopping Group Stats collection thread for {}", sw.getStringId());
+ this.stopTimer = true;
+ timeout.cancel();
+ }
+}
diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
new file mode 100644
index 00000000..78650fe6
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
@@ -0,0 +1,366 @@
+/*
+ * 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.of.group.impl;
+
+import com.google.common.collect.Maps;
+
+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.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.group.DefaultGroup;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProvider;
+import org.onosproject.net.group.GroupProviderRegistry;
+import org.onosproject.net.group.GroupProviderService;
+import org.onosproject.net.group.StoredGroupBucketEntry;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFBucketCounter;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsEntry;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to handle Group.
+ */
+@Component(immediate = true)
+public class OpenFlowGroupProvider extends AbstractProvider implements GroupProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected GroupProviderRegistry providerRegistry;
+
+ private GroupProviderService providerService;
+
+ static final int POLL_INTERVAL = 10;
+
+ private final InternalGroupProvider listener = new InternalGroupProvider();
+
+ private static final AtomicLong XID_COUNTER = new AtomicLong(1);
+ private final Map<Dpid, GroupStatsCollector> collectors = Maps.newHashMap();
+ private final Map<Long, OFStatsReply> groupStats = Maps.newConcurrentMap();
+ private final Map<GroupId, GroupOperation> pendingGroupOperations =
+ Maps.newConcurrentMap();
+
+ /* Map<Group ID, Transaction ID> */
+ private final Map<GroupId, Long> pendingXidMaps = Maps.newConcurrentMap();
+
+ /**
+ * Creates a OpenFlow group provider.
+ */
+ public OpenFlowGroupProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.group"));
+ }
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ controller.addListener(listener);
+ controller.addEventListener(listener);
+
+ for (OpenFlowSwitch sw : controller.getSwitches()) {
+ if (isGroupSupported(sw)) {
+ GroupStatsCollector gsc = new GroupStatsCollector(sw, POLL_INTERVAL);
+ gsc.start();
+ collectors.put(new Dpid(sw.getId()), gsc);
+ }
+ }
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ providerService = null;
+
+ log.info("Stopped");
+ }
+
+ @Override
+ public void performGroupOperation(DeviceId deviceId, GroupOperations groupOps) {
+ Map<OFGroupMod, OpenFlowSwitch> mods = Maps.newIdentityHashMap();
+ final Dpid dpid = Dpid.dpid(deviceId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ for (GroupOperation groupOperation: groupOps.operations()) {
+ if (sw == null) {
+ log.error("SW {} is not found", dpid);
+ return;
+ }
+ final Long groupModXid = XID_COUNTER.getAndIncrement();
+ GroupModBuilder builder =
+ GroupModBuilder.builder(groupOperation.buckets(),
+ groupOperation.groupId(),
+ groupOperation.groupType(),
+ sw.factory(),
+ Optional.of(groupModXid));
+ OFGroupMod groupMod = null;
+ switch (groupOperation.opType()) {
+ case ADD:
+ groupMod = builder.buildGroupAdd();
+ break;
+ case MODIFY:
+ groupMod = builder.buildGroupMod();
+ break;
+ case DELETE:
+ groupMod = builder.buildGroupDel();
+ break;
+ default:
+ log.error("Unsupported Group operation");
+ }
+ sw.sendMsg(groupMod);
+ GroupId groudId = new DefaultGroupId(groupMod.getGroup().getGroupNumber());
+ pendingGroupOperations.put(groudId, groupOperation);
+ pendingXidMaps.put(groudId, groupModXid);
+ }
+ }
+
+ private void pushGroupMetrics(Dpid dpid, OFStatsReply statsReply) {
+ DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
+
+ OFGroupStatsReply groupStatsReply = null;
+ OFGroupDescStatsReply groupDescStatsReply = null;
+
+ synchronized (groupStats) {
+ if (statsReply.getStatsType() == OFStatsType.GROUP) {
+ OFStatsReply reply = groupStats.get(statsReply.getXid() + 1);
+ if (reply != null) {
+ groupStatsReply = (OFGroupStatsReply) statsReply;
+ groupDescStatsReply = (OFGroupDescStatsReply) reply;
+ groupStats.remove(statsReply.getXid() + 1);
+ } else {
+ groupStats.put(statsReply.getXid(), statsReply);
+ }
+ } else if (statsReply.getStatsType() == OFStatsType.GROUP_DESC) {
+ OFStatsReply reply = groupStats.get(statsReply.getXid() - 1);
+ if (reply != null) {
+ groupStatsReply = (OFGroupStatsReply) reply;
+ groupDescStatsReply = (OFGroupDescStatsReply) statsReply;
+ groupStats.remove(statsReply.getXid() - 1);
+ } else {
+ groupStats.put(statsReply.getXid(), statsReply);
+ }
+ }
+ }
+
+ if (groupStatsReply != null && groupDescStatsReply != null) {
+ Collection<Group> groups = buildGroupMetrics(deviceId,
+ groupStatsReply, groupDescStatsReply);
+ providerService.pushGroupMetrics(deviceId, groups);
+ for (Group group: groups) {
+ pendingGroupOperations.remove(group.id());
+ pendingXidMaps.remove(group.id());
+ }
+ }
+ }
+
+ private Collection<Group> buildGroupMetrics(DeviceId deviceId,
+ OFGroupStatsReply groupStatsReply,
+ OFGroupDescStatsReply groupDescStatsReply) {
+
+ Map<Integer, Group> groups = Maps.newHashMap();
+
+
+ for (OFGroupDescStatsEntry entry: groupDescStatsReply.getEntries()) {
+ int id = entry.getGroup().getGroupNumber();
+ GroupId groupId = new DefaultGroupId(id);
+ GroupDescription.Type type = getGroupType(entry.getGroupType());
+ GroupBuckets buckets = new GroupBucketEntryBuilder(entry.getBuckets(),
+ entry.getGroupType()).build();
+ DefaultGroup group = new DefaultGroup(groupId, deviceId, type, buckets);
+ groups.put(id, group);
+ }
+
+ for (OFGroupStatsEntry entry: groupStatsReply.getEntries()) {
+ int groupId = entry.getGroup().getGroupNumber();
+ DefaultGroup group = (DefaultGroup) groups.get(groupId);
+ if (group != null) {
+ group.setBytes(entry.getByteCount().getValue());
+ group.setLife(entry.getDurationSec());
+ group.setPackets(entry.getPacketCount().getValue());
+ group.setReferenceCount(entry.getRefCount());
+ int bucketIndex = 0;
+ for (OFBucketCounter bucketStats:entry.getBucketStats()) {
+ ((StoredGroupBucketEntry) group.buckets().buckets()
+ .get(bucketIndex))
+ .setPackets(bucketStats
+ .getPacketCount().getValue());
+ ((StoredGroupBucketEntry) group.buckets().buckets()
+ .get(bucketIndex))
+ .setBytes(entry.getBucketStats()
+ .get(bucketIndex)
+ .getByteCount().getValue());
+ bucketIndex++;
+ }
+ }
+ }
+
+ return groups.values();
+ }
+
+ private GroupDescription.Type getGroupType(OFGroupType type) {
+ switch (type) {
+ case ALL:
+ return GroupDescription.Type.ALL;
+ case INDIRECT:
+ return GroupDescription.Type.INDIRECT;
+ case SELECT:
+ return GroupDescription.Type.SELECT;
+ case FF:
+ return GroupDescription.Type.FAILOVER;
+ default:
+ log.error("Unsupported OF group type : {}", type);
+ break;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a transaction ID for entire group operations and increases
+ * the counter by the number given.
+ *
+ * @param increase the amount to increase the counter by
+ * @return a transaction ID
+ */
+ public static long getXidAndAdd(int increase) {
+ return XID_COUNTER.getAndAdd(increase);
+ }
+
+ private boolean isGroupSupported(OpenFlowSwitch sw) {
+ if (sw.factory().getVersion() == OFVersion.OF_10 ||
+ sw.factory().getVersion() == OFVersion.OF_11 ||
+ sw.factory().getVersion() == OFVersion.OF_12) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private class InternalGroupProvider
+ implements OpenFlowSwitchListener, OpenFlowEventListener {
+
+ @Override
+ public void handleMessage(Dpid dpid, OFMessage msg) {
+ switch (msg.getType()) {
+ case STATS_REPLY:
+ pushGroupMetrics(dpid, (OFStatsReply) msg);
+ break;
+ case ERROR:
+ OFErrorMsg errorMsg = (OFErrorMsg) msg;
+ if (errorMsg.getErrType() == OFErrorType.GROUP_MOD_FAILED) {
+ GroupId pendingGroupId = null;
+ for (Map.Entry<GroupId, Long> entry: pendingXidMaps.entrySet()) {
+ if (entry.getValue() == errorMsg.getXid()) {
+ pendingGroupId = entry.getKey();
+ break;
+ }
+ }
+ if (pendingGroupId == null) {
+ log.warn("Error for unknown group operation: {}",
+ errorMsg.getXid());
+ } else {
+ GroupOperation operation =
+ pendingGroupOperations.get(pendingGroupId);
+ DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
+ if (operation != null) {
+ providerService.groupOperationFailed(deviceId,
+ operation);
+ pendingGroupOperations.remove(pendingGroupId);
+ pendingXidMaps.remove(pendingGroupId);
+ log.warn("Received an group mod error {}", msg);
+ } else {
+ log.error("Cannot find pending group operation with group ID: {}",
+ pendingGroupId);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void switchAdded(Dpid dpid) {
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (isGroupSupported(sw)) {
+ GroupStatsCollector gsc = new GroupStatsCollector(
+ controller.getSwitch(dpid), POLL_INTERVAL);
+ gsc.start();
+ collectors.put(dpid, gsc);
+ }
+ }
+
+ @Override
+ public void switchRemoved(Dpid dpid) {
+ GroupStatsCollector collector = collectors.remove(dpid);
+ if (collector != null) {
+ collector.stop();
+ }
+ }
+
+ @Override
+ public void switchChanged(Dpid dpid) {
+ }
+
+ @Override
+ public void portChanged(Dpid dpid, OFPortStatus status) {
+ }
+
+ @Override
+ public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java
new file mode 100644
index 00000000..9fda4a31
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 OpenFlow controller as a means of device port group management.
+ */
+package org.onosproject.provider.of.group.impl; \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java
new file mode 100644
index 00000000..d66ba090
--- /dev/null
+++ b/framework/src/onos/providers/openflow/group/src/test/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProviderTest.java
@@ -0,0 +1,397 @@
+package org.onosproject.provider.of.group.impl;
+
+import com.google.common.collect.Lists;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.core.GroupId;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.group.DefaultGroupBucket;
+import org.onosproject.net.group.Group;
+import org.onosproject.net.group.GroupBucket;
+import org.onosproject.net.group.GroupBuckets;
+import org.onosproject.net.group.GroupDescription;
+import org.onosproject.net.group.GroupOperation;
+import org.onosproject.net.group.GroupOperations;
+import org.onosproject.net.group.GroupProvider;
+import org.onosproject.net.group.GroupProviderRegistry;
+import org.onosproject.net.group.GroupProviderService;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.PacketListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFactories;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupMod;
+import org.projectfloodlight.openflow.protocol.OFGroupModFailedCode;
+import org.projectfloodlight.openflow.protocol.OFGroupStatsReply;
+import org.projectfloodlight.openflow.protocol.OFGroupType;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.errormsg.OFGroupModFailedErrorMsg;
+import org.projectfloodlight.openflow.types.OFGroup;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+public class OpenFlowGroupProviderTest {
+
+ OpenFlowGroupProvider provider = new OpenFlowGroupProvider();
+ private final OpenFlowController controller = new TestController();
+ GroupProviderRegistry providerRegistry = new TestGroupProviderRegistry();
+ GroupProviderService providerService;
+
+ private DeviceId deviceId = DeviceId.deviceId("of:0000000000000001");
+ private Dpid dpid1 = Dpid.dpid(deviceId.uri());
+
+ @Before
+ public void setUp() {
+ provider.controller = controller;
+ provider.providerRegistry = providerRegistry;
+ provider.activate();
+ }
+
+ @Test
+ public void basics() {
+ assertNotNull("registration expected", providerService);
+ assertEquals("incorrect provider", provider, providerService.provider());
+ }
+
+ @Test
+ public void addGroup() {
+
+ GroupId groupId = new DefaultGroupId(1);
+
+ List<GroupBucket> bucketList = Lists.newArrayList();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(PortNumber.portNumber(1));
+ GroupBucket bucket =
+ DefaultGroupBucket.createSelectGroupBucket(builder.build());
+ bucketList.add(bucket);
+ GroupBuckets buckets = new GroupBuckets(bucketList);
+
+ List<GroupOperation> operationList = Lists.newArrayList();
+ GroupOperation operation = GroupOperation.createAddGroupOperation(groupId,
+ GroupDescription.Type.SELECT, buckets);
+ operationList.add(operation);
+ GroupOperations operations = new GroupOperations(operationList);
+
+ provider.performGroupOperation(deviceId, operations);
+
+ final Dpid dpid = Dpid.dpid(deviceId.uri());
+ TestOpenFlowSwitch sw = (TestOpenFlowSwitch) controller.getSwitch(dpid);
+ assertNotNull("Switch should not be nul", sw);
+ assertNotNull("OFGroupMsg should not be null", sw.msg);
+
+ }
+
+
+ @Test
+ public void groupModFailure() {
+ TestOpenFlowGroupProviderService testProviderService =
+ (TestOpenFlowGroupProviderService) providerService;
+
+ GroupId groupId = new DefaultGroupId(1);
+ List<GroupBucket> bucketList = Lists.newArrayList();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ builder.setOutput(PortNumber.portNumber(1));
+ GroupBucket bucket =
+ DefaultGroupBucket.createSelectGroupBucket(builder.build());
+ bucketList.add(bucket);
+ GroupBuckets buckets = new GroupBuckets(bucketList);
+ List<GroupOperation> operationList = Lists.newArrayList();
+ GroupOperation operation = GroupOperation.createAddGroupOperation(groupId,
+ GroupDescription.Type.SELECT, buckets);
+ operationList.add(operation);
+ GroupOperations operations = new GroupOperations(operationList);
+
+ provider.performGroupOperation(deviceId, operations);
+
+ OFGroupModFailedErrorMsg.Builder errorBuilder =
+ OFFactories.getFactory(OFVersion.OF_13).errorMsgs().buildGroupModFailedErrorMsg();
+ OFGroupMod.Builder groupBuilder = OFFactories.getFactory(OFVersion.OF_13).buildGroupModify();
+ groupBuilder.setGroupType(OFGroupType.ALL);
+ groupBuilder.setGroup(OFGroup.of(1));
+ errorBuilder.setCode(OFGroupModFailedCode.GROUP_EXISTS);
+ errorBuilder.setXid(provider.getXidAndAdd(0) - 1);
+
+ controller.processPacket(dpid1, errorBuilder.build());
+
+ assertNotNull("Operation failed should not be null",
+ testProviderService.failedOperation);
+ }
+
+
+ @Test
+ public void groupStatsEvent() {
+ TestOpenFlowGroupProviderService testProviderService =
+ (TestOpenFlowGroupProviderService) providerService;
+
+ OFGroupStatsReply.Builder rep1 =
+ OFFactories.getFactory(OFVersion.OF_13).buildGroupStatsReply();
+ rep1.setXid(1);
+ controller.processPacket(dpid1, rep1.build());
+ OFGroupDescStatsReply.Builder rep2 =
+ OFFactories.getFactory(OFVersion.OF_13).buildGroupDescStatsReply();
+ assertNull("group entries is not set yet", testProviderService.getGroupEntries());
+
+ rep2.setXid(2);
+ controller.processPacket(dpid1, rep2.build());
+ assertNotNull("group entries should be set", testProviderService.getGroupEntries());
+ }
+
+
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.providerRegistry = null;
+ provider.controller = null;
+ }
+
+ private class TestOpenFlowGroupProviderService
+ extends AbstractProviderService<GroupProvider>
+ implements GroupProviderService {
+
+ Collection<Group> groups = null;
+ GroupOperation failedOperation = null;
+
+ protected TestOpenFlowGroupProviderService(GroupProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void groupOperationFailed(DeviceId deviceId, GroupOperation operation) {
+ this.failedOperation = operation;
+ }
+
+ @Override
+ public void pushGroupMetrics(DeviceId deviceId, Collection<Group> groupEntries) {
+ this.groups = groupEntries;
+ }
+
+ public Collection<Group> getGroupEntries() {
+ return groups;
+ }
+ }
+
+ private class TestController implements OpenFlowController {
+
+ OpenFlowEventListener eventListener = null;
+ List<OpenFlowSwitch> switches = Lists.newArrayList();
+
+ public TestController() {
+ OpenFlowSwitch testSwitch = new TestOpenFlowSwitch();
+ switches.add(testSwitch);
+ }
+
+ @Override
+ public void addListener(OpenFlowSwitchListener listener) {
+ }
+
+ @Override
+ public void removeListener(OpenFlowSwitchListener listener) {
+
+ }
+
+ @Override
+ public void addPacketListener(int priority, PacketListener listener) {
+
+ }
+
+ @Override
+ public void removePacketListener(PacketListener listener) {
+
+ }
+
+ @Override
+ public void addEventListener(OpenFlowEventListener listener) {
+ this.eventListener = listener;
+ }
+
+ @Override
+ public void removeEventListener(OpenFlowEventListener listener) {
+
+ }
+
+ @Override
+ public void write(Dpid dpid, OFMessage msg) {
+
+ }
+
+ @Override
+ public void processPacket(Dpid dpid, OFMessage msg) {
+ eventListener.handleMessage(dpid, msg);
+ }
+
+ @Override
+ public void setRole(Dpid dpid, RoleState role) {
+
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getSwitches() {
+ return switches;
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getMasterSwitches() {
+ return null;
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getEqualSwitches() {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getSwitch(Dpid dpid) {
+ return switches.get(0);
+ }
+
+ @Override
+ public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
+ return null;
+ }
+
+ }
+
+ private class TestGroupProviderRegistry implements GroupProviderRegistry {
+
+ @Override
+ public GroupProviderService register(GroupProvider provider) {
+ providerService = new TestOpenFlowGroupProviderService(provider);
+ return providerService;
+ }
+
+ @Override
+ public void unregister(GroupProvider provider) {
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+ }
+
+ private class TestOpenFlowSwitch implements OpenFlowSwitch {
+
+ OFMessage msg = null;
+
+ @Override
+ public void sendMsg(OFMessage msg) {
+ this.msg = msg;
+ }
+
+ @Override
+ public void sendMsg(List<OFMessage> msgs) {
+
+ }
+
+ @Override
+ public void handleMessage(OFMessage fromSwitch) {
+
+ }
+
+ @Override
+ public void setRole(RoleState role) {
+
+ }
+
+ @Override
+ public RoleState getRole() {
+ return null;
+ }
+
+ @Override
+ public List<OFPortDesc> getPorts() {
+ return null;
+ }
+
+ @Override
+ public OFFactory factory() {
+ return OFFactories.getFactory(OFVersion.OF_13);
+ }
+
+ @Override
+ public String getStringId() {
+ return null;
+ }
+
+ @Override
+ public long getId() {
+ return 0;
+ }
+
+ @Override
+ public String manufacturerDescription() {
+ return null;
+ }
+
+ @Override
+ public String datapathDescription() {
+ return null;
+ }
+
+ @Override
+ public String hardwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String softwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String serialNumber() {
+ return null;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return false;
+ }
+
+ @Override
+ public void disconnectSwitch() {
+
+ }
+
+ @Override
+ public void returnRoleReply(RoleState requested, RoleState response) {
+
+ }
+
+ @Override
+ public Device.Type deviceType() {
+ return Device.Type.SWITCH;
+ }
+
+ @Override
+ public String channelId() {
+ return null;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/meter/pom.xml b/framework/src/onos/providers/openflow/meter/pom.xml
new file mode 100644
index 00000000..9de5c1b0
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/pom.xml
@@ -0,0 +1,34 @@
+<?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.
+ -->
+<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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-provider-meter</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS OpenFlow protocol meter provider</description>
+
+</project> \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java
new file mode 100644
index 00000000..c07354b6
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterModBuilder.java
@@ -0,0 +1,159 @@
+/*
+ * 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.of.meter.impl;
+
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterId;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMeterFlags;
+import org.projectfloodlight.openflow.protocol.OFMeterMod;
+import org.projectfloodlight.openflow.protocol.OFMeterModCommand;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBand;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDrop;
+import org.projectfloodlight.openflow.protocol.meterband.OFMeterBandDscpRemark;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Builder for a meter modification.
+ */
+public final class MeterModBuilder {
+
+ private final Logger log = getLogger(getClass());
+
+ private final long xid;
+ private final OFFactory factory;
+ private Meter.Unit unit = Meter.Unit.KB_PER_SEC;
+ private boolean burst = false;
+ private Long id;
+ private Collection<Band> bands;
+
+ public MeterModBuilder(long xid, OFFactory factory) {
+ this.xid = xid;
+ this.factory = factory;
+ }
+
+ public static MeterModBuilder builder(long xid, OFFactory factory) {
+ return new MeterModBuilder(xid, factory);
+ }
+
+ public MeterModBuilder withRateUnit(Meter.Unit unit) {
+ this.unit = unit;
+ return this;
+ }
+
+ public MeterModBuilder burst() {
+ this.burst = true;
+ return this;
+ }
+
+ public MeterModBuilder withId(MeterId meterId) {
+ this.id = meterId.id();
+ return this;
+ }
+
+ public MeterModBuilder withBands(Collection<Band> bands) {
+ this.bands = bands;
+ return this;
+ }
+
+ public OFMeterMod add() {
+ validate();
+ OFMeterMod.Builder builder = builderMeterMod();
+ builder.setCommand(OFMeterModCommand.ADD.ordinal());
+ return builder.build();
+ }
+
+ public OFMeterMod remove() {
+ validate();
+ OFMeterMod.Builder builder = builderMeterMod();
+ builder.setCommand(OFMeterModCommand.DELETE.ordinal());
+ return builder.build();
+ }
+
+ public OFMeterMod modify() {
+ validate();
+ OFMeterMod.Builder builder = builderMeterMod();
+ builder.setCommand(OFMeterModCommand.MODIFY.ordinal());
+ return builder.build();
+ }
+
+ private OFMeterMod.Builder builderMeterMod() {
+ OFMeterMod.Builder builder = factory.buildMeterMod();
+ int flags = 0;
+ if (burst) {
+ // covering loxi short comings.
+ flags |= 1 << OFMeterFlags.BURST.ordinal();
+ }
+ switch (unit) {
+ case PKTS_PER_SEC:
+ flags |= 1 << OFMeterFlags.PKTPS.ordinal();
+ break;
+ case KB_PER_SEC:
+ flags |= 1 << OFMeterFlags.KBPS.ordinal();
+ break;
+ default:
+ log.warn("Unknown unit type {}", unit);
+ }
+ //FIXME: THIS WILL CHANGE IN OF1.4 to setBands.
+ builder.setMeters(buildBands());
+ builder.setFlags(flags)
+ .setMeterId(id)
+ .setXid(xid);
+ return builder;
+ }
+
+ private List<OFMeterBand> buildBands() {
+ return bands.stream().map(b -> {
+ switch (b.type()) {
+ case DROP:
+ OFMeterBandDrop.Builder dropBuilder =
+ factory.meterBands().buildDrop();
+ if (burst) {
+ dropBuilder.setBurstSize(b.burst());
+ }
+ dropBuilder.setRate(b.rate());
+ return dropBuilder.build();
+ case REMARK:
+ OFMeterBandDscpRemark.Builder remarkBand =
+ factory.meterBands().buildDscpRemark();
+ if (burst) {
+ remarkBand.setBurstSize(b.burst());
+ }
+ remarkBand.setRate(b.rate());
+ remarkBand.setPrecLevel(b.dropPrecedence());
+ return remarkBand.build();
+ default:
+ log.warn("Unknown band type {}", b.type());
+ return null;
+ }
+ }).filter(value -> value != null).collect(Collectors.toList());
+ }
+
+ private void validate() {
+ checkNotNull(id, "id cannot be null");
+ checkNotNull(bands, "Must have bands");
+ checkArgument(bands.size() > 0, "Must have at lease one band");
+ }
+}
diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java
new file mode 100644
index 00000000..83dc6458
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/MeterStatsCollector.java
@@ -0,0 +1,103 @@
+/*
+ * 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.of.meter.impl;
+
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFMeterStatsRequest;
+import org.slf4j.Logger;
+
+import java.util.concurrent.TimeUnit;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/*
+ * Sends Meter Stats Request and collect the Meter statistics with a time interval.
+ */
+public class MeterStatsCollector implements TimerTask {
+
+ private final HashedWheelTimer timer = Timer.getTimer();
+ private final OpenFlowSwitch sw;
+ private final Logger log = getLogger(getClass());
+ private final int refreshInterval;
+
+ private Timeout timeout;
+
+ private boolean stopTimer = false;
+
+ /**
+ * Creates a GroupStatsCollector object.
+ *
+ * @param sw Open Flow switch
+ * @param interval time interval for collecting group statistic
+ */
+ public MeterStatsCollector(OpenFlowSwitch sw, int interval) {
+ this.sw = sw;
+ this.refreshInterval = interval;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ log.trace("Collecting stats for {}", sw.getStringId());
+
+ sendMeterStatistic();
+
+ if (!this.stopTimer) {
+ log.trace("Scheduling stats collection in {} seconds for {}",
+ this.refreshInterval, this.sw.getStringId());
+ timeout.getTimer().newTimeout(this, refreshInterval,
+ TimeUnit.SECONDS);
+ }
+ }
+
+ private void sendMeterStatistic() {
+ if (log.isTraceEnabled()) {
+ log.trace("sendMeterStatistics {}:{}", sw.getStringId(), sw.getRole());
+ }
+ if (sw.getRole() != RoleState.MASTER) {
+ return;
+ }
+
+ OFMeterStatsRequest.Builder builder =
+ sw.factory().buildMeterStatsRequest();
+ builder.setXid(0).setMeterId(0xFFFFFFFF);
+
+ sw.sendMsg(builder.build());
+
+ }
+
+ /**
+ * Starts the collector.
+ */
+ public void start() {
+ log.info("Starting Meter Stats collection thread for {}", sw.getStringId());
+ timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the collector.
+ */
+ public void stop() {
+ log.info("Stopping Meter Stats collection thread for {}", sw.getStringId());
+ this.stopTimer = true;
+ timeout.cancel();
+ }
+}
diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java
new file mode 100644
index 00000000..f5a777be
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProvider.java
@@ -0,0 +1,393 @@
+/*
+ * 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.of.meter.impl;
+
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.RemovalCause;
+import com.google.common.cache.RemovalNotification;
+import com.google.common.collect.Maps;
+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.core.CoreService;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterFailReason;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterOperation;
+import org.onosproject.net.meter.MeterOperations;
+import org.onosproject.net.meter.MeterProvider;
+import org.onosproject.net.meter.MeterProviderRegistry;
+import org.onosproject.net.meter.MeterProviderService;
+import org.onosproject.net.meter.MeterState;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFErrorMsg;
+import org.projectfloodlight.openflow.protocol.OFErrorType;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFMeterBandStats;
+import org.projectfloodlight.openflow.protocol.OFMeterConfigStatsReply;
+import org.projectfloodlight.openflow.protocol.OFMeterStats;
+import org.projectfloodlight.openflow.protocol.OFMeterStatsReply;
+import org.projectfloodlight.openflow.protocol.OFPortStatus;
+import org.projectfloodlight.openflow.protocol.OFStatsReply;
+import org.projectfloodlight.openflow.protocol.OFStatsType;
+import org.projectfloodlight.openflow.protocol.OFVersion;
+import org.projectfloodlight.openflow.protocol.errormsg.OFMeterModFailedErrorMsg;
+import org.slf4j.Logger;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provider which uses an OpenFlow controller to handle meters.
+ */
+@Component(immediate = true, enabled = true)
+public class OpenFlowMeterProvider extends AbstractProvider implements MeterProvider {
+
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MeterProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+
+ private MeterProviderService providerService;
+
+ private static final AtomicLong XID_COUNTER = new AtomicLong(1);
+
+ static final int POLL_INTERVAL = 10;
+ static final long TIMEOUT = 30;
+
+ private Cache<Long, MeterOperation> pendingOperations;
+
+
+ private InternalMeterListener listener = new InternalMeterListener();
+ private Map<Dpid, MeterStatsCollector> collectors = Maps.newHashMap();
+
+ /**
+ * Creates a OpenFlow meter provider.
+ */
+ public OpenFlowMeterProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.meter"));
+ }
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+
+ pendingOperations = CacheBuilder.newBuilder()
+ .expireAfterWrite(TIMEOUT, TimeUnit.SECONDS)
+ .removalListener((RemovalNotification<Long, MeterOperation> notification) -> {
+ if (notification.getCause() == RemovalCause.EXPIRED) {
+ providerService.meterOperationFailed(notification.getValue(),
+ MeterFailReason.TIMEOUT);
+ }
+ }).build();
+
+ controller.addEventListener(listener);
+ controller.addListener(listener);
+
+ controller.getSwitches().forEach((sw -> createStatsCollection(sw)));
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ controller.removeEventListener(listener);
+ controller.removeListener(listener);
+ providerService = null;
+ }
+
+ @Override
+ public void performMeterOperation(DeviceId deviceId, MeterOperations meterOps) {
+ Dpid dpid = Dpid.dpid(deviceId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (sw == null) {
+ log.error("Unknown device {}", deviceId);
+ meterOps.operations().forEach(op ->
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_DEVICE)
+ );
+ return;
+ }
+
+ meterOps.operations().forEach(op -> performOperation(sw, op));
+ }
+
+ @Override
+ public void performMeterOperation(DeviceId deviceId, MeterOperation meterOp) {
+ Dpid dpid = Dpid.dpid(deviceId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (sw == null) {
+ log.error("Unknown device {}", deviceId);
+ providerService.meterOperationFailed(meterOp,
+ MeterFailReason.UNKNOWN_DEVICE);
+ return;
+ }
+
+ performOperation(sw, meterOp);
+
+ }
+
+ private void performOperation(OpenFlowSwitch sw, MeterOperation op) {
+
+ pendingOperations.put(op.meter().id().id(), op);
+
+
+ Meter meter = op.meter();
+ MeterModBuilder builder = MeterModBuilder.builder(meter.id().id(), sw.factory());
+ if (meter.isBurst()) {
+ builder.burst();
+ }
+ builder.withBands(meter.bands())
+ .withId(meter.id())
+ .withRateUnit(meter.unit());
+
+ switch (op.type()) {
+ case ADD:
+ sw.sendMsg(builder.add());
+ break;
+ case REMOVE:
+ sw.sendMsg(builder.remove());
+ break;
+ case MODIFY:
+ sw.sendMsg(builder.modify());
+ break;
+ default:
+ log.warn("Unknown Meter command {}; not sending anything",
+ op.type());
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_COMMAND);
+ }
+
+ }
+
+ private void createStatsCollection(OpenFlowSwitch sw) {
+ if (isMeterSupported(sw)) {
+ MeterStatsCollector msc = new MeterStatsCollector(sw, POLL_INTERVAL);
+ msc.start();
+ collectors.put(new Dpid(sw.getId()), msc);
+ }
+ }
+
+ private boolean isMeterSupported(OpenFlowSwitch sw) {
+ if (sw.factory().getVersion() == OFVersion.OF_10 ||
+ sw.factory().getVersion() == OFVersion.OF_11 ||
+ sw.factory().getVersion() == OFVersion.OF_12) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private void pushMeterStats(Dpid dpid, OFStatsReply msg) {
+ DeviceId deviceId = DeviceId.deviceId(Dpid.uri(dpid));
+
+ if (msg.getStatsType() == OFStatsType.METER) {
+ OFMeterStatsReply reply = (OFMeterStatsReply) msg;
+ Collection<Meter> meters = buildMeters(deviceId, reply.getEntries());
+ //TODO do meter accounting here.
+ providerService.pushMeterMetrics(deviceId, meters);
+ } else if (msg.getStatsType() == OFStatsType.METER_CONFIG) {
+ OFMeterConfigStatsReply reply = (OFMeterConfigStatsReply) msg;
+ // FIXME: Map<Long, Meter> meters = collectMeters(deviceId, reply);
+ }
+
+ }
+
+ private Map<Long, Meter> collectMeters(DeviceId deviceId,
+ OFMeterConfigStatsReply reply) {
+ return Maps.newHashMap();
+ //TODO: Needs a fix to be applied to loxi MeterConfig stat is incorrect
+ }
+
+ private Collection<Meter> buildMeters(DeviceId deviceId,
+ List<OFMeterStats> entries) {
+ return entries.stream().map(stat -> {
+ DefaultMeter.Builder builder = DefaultMeter.builder();
+ Collection<Band> bands = buildBands(stat.getBandStats());
+ builder.forDevice(deviceId)
+ .withId(MeterId.meterId(stat.getMeterId()))
+ //FIXME: need to encode appId in meter id, but that makes
+ // things a little annoying for debugging
+ .fromApp(coreService.getAppId("org.onosproject.core"))
+ .withBands(bands);
+ DefaultMeter meter = builder.build();
+ meter.setState(MeterState.ADDED);
+ meter.setLife(stat.getDurationSec());
+ meter.setProcessedBytes(stat.getByteInCount().getValue());
+ meter.setProcessedPackets(stat.getPacketInCount().getValue());
+ meter.setReferenceCount(stat.getFlowCount());
+
+ // marks the meter as seen on the dataplane
+ pendingOperations.invalidate(stat.getMeterId());
+ return meter;
+ }).collect(Collectors.toSet());
+ }
+
+ private Collection<Band> buildBands(List<OFMeterBandStats> bandStats) {
+ return bandStats.stream().map(stat -> {
+ DefaultBand band = DefaultBand.builder().build();
+ band.setBytes(stat.getByteBandCount().getValue());
+ band.setPackets(stat.getPacketBandCount().getValue());
+ return band;
+ }).collect(Collectors.toSet());
+ }
+
+ private void signalMeterError(OFMeterModFailedErrorMsg meterError,
+ MeterOperation op) {
+ switch (meterError.getCode()) {
+ case UNKNOWN:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_DEVICE);
+ break;
+ case METER_EXISTS:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.EXISTING_METER);
+ break;
+ case INVALID_METER:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.INVALID_METER);
+ break;
+ case UNKNOWN_METER:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN);
+ break;
+ case BAD_COMMAND:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_COMMAND);
+ break;
+ case BAD_FLAGS:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN_FLAGS);
+ break;
+ case BAD_RATE:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.BAD_RATE);
+ break;
+ case BAD_BURST:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.BAD_BURST);
+ break;
+ case BAD_BAND:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.BAD_BAND);
+ break;
+ case BAD_BAND_VALUE:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.BAD_BAND_VALUE);
+ break;
+ case OUT_OF_METERS:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.OUT_OF_METERS);
+ break;
+ case OUT_OF_BANDS:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.OUT_OF_BANDS);
+ break;
+ default:
+ providerService.meterOperationFailed(op,
+ MeterFailReason.UNKNOWN);
+ }
+ }
+
+ private class InternalMeterListener
+ implements OpenFlowSwitchListener, OpenFlowEventListener {
+ @Override
+ public void handleMessage(Dpid dpid, OFMessage msg) {
+ switch (msg.getType()) {
+ case STATS_REPLY:
+ pushMeterStats(dpid, (OFStatsReply) msg);
+ break;
+ case ERROR:
+ OFErrorMsg error = (OFErrorMsg) msg;
+ if (error.getErrType() == OFErrorType.METER_MOD_FAILED) {
+ MeterOperation op =
+ pendingOperations.getIfPresent(error.getXid());
+ pendingOperations.invalidate(error.getXid());
+ if (op == null) {
+ log.warn("Unknown Meter operation failed {}", error);
+ } else {
+ OFMeterModFailedErrorMsg meterError =
+ (OFMeterModFailedErrorMsg) error;
+ signalMeterError(meterError, op);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ @Override
+ public void switchAdded(Dpid dpid) {
+ createStatsCollection(controller.getSwitch(dpid));
+ }
+
+ @Override
+ public void switchRemoved(Dpid dpid) {
+ MeterStatsCollector msc = collectors.remove(dpid);
+ if (msc != null) {
+ msc.stop();
+ }
+ }
+
+ @Override
+ public void switchChanged(Dpid dpid) {
+
+ }
+
+ @Override
+ public void portChanged(Dpid dpid, OFPortStatus status) {
+
+ }
+
+ @Override
+ public void receivedRoleReply(Dpid dpid, RoleState requested, RoleState response) {
+
+ }
+ }
+
+
+
+}
diff --git a/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java
new file mode 100644
index 00000000..9515712c
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/src/main/java/org/onosproject/provider/of/meter/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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 OpenFlow controller as a means of device metering management.
+ */
+package org.onosproject.provider.of.meter.impl; \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java b/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java
new file mode 100644
index 00000000..0c5d5389
--- /dev/null
+++ b/framework/src/onos/providers/openflow/meter/src/test/java/org/onosproject/provider/of/meter/impl/OpenFlowMeterProviderTest.java
@@ -0,0 +1,22 @@
+/*
+ * 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.of.meter.impl;
+
+
+public class OpenFlowMeterProviderTest {
+
+
+} \ No newline at end of file
diff --git a/framework/src/onos/providers/openflow/packet/pom.xml b/framework/src/onos/providers/openflow/packet/pom.xml
new file mode 100644
index 00000000..f7f62d8e
--- /dev/null
+++ b/framework/src/onos/providers/openflow/packet/pom.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-provider-packet</artifactId>
+ <packaging>bundle</packaging>
+
+ <description>ONOS OpenFlow protocol packet provider</description>
+
+</project>
diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java
new file mode 100644
index 00000000..6d153103
--- /dev/null
+++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowCorePacketContext.java
@@ -0,0 +1,99 @@
+/*
+ * 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.of.packet.impl;
+
+import org.onlab.packet.DeserializationException;
+import org.onlab.packet.Ethernet;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instruction.Type;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.List;
+
+/**
+ * Packet context used with the OpenFlow providers.
+ */
+public class OpenFlowCorePacketContext extends DefaultPacketContext {
+
+ private static final Logger log = LoggerFactory.getLogger(OpenFlowCorePacketContext.class);
+
+ private final OpenFlowPacketContext ofPktCtx;
+
+ /**
+ * Creates a new OpenFlow core packet context.
+ *
+ * @param time creation time
+ * @param inPkt inbound packet
+ * @param outPkt outbound packet
+ * @param block whether the context is blocked or not
+ * @param ofPktCtx OpenFlow packet context
+ */
+ protected OpenFlowCorePacketContext(long time, InboundPacket inPkt,
+ OutboundPacket outPkt, boolean block,
+ OpenFlowPacketContext ofPktCtx) {
+ super(time, inPkt, outPkt, block);
+ this.ofPktCtx = ofPktCtx;
+ }
+
+ @Override
+ public void send() {
+ if (!this.block()) {
+ if (outPacket() == null) {
+ sendPacket(null);
+ } else {
+ try {
+ Ethernet eth = Ethernet.deserializer()
+ .deserialize(outPacket().data().array(), 0,
+ outPacket().data().array().length);
+ sendPacket(eth);
+ } catch (DeserializationException e) {
+ log.warn("Unable to deserialize packet");
+ }
+ }
+ }
+ }
+
+ private void sendPacket(Ethernet eth) {
+ List<Instruction> ins = treatmentBuilder().build().allInstructions();
+ OFPort p = null;
+ //TODO: support arbitrary list of treatments must be supported in ofPacketContext
+ for (Instruction i : ins) {
+ if (i.type() == Type.OUTPUT) {
+ p = buildPort(((OutputInstruction) i).port());
+ break; //for now...
+ }
+ }
+ if (eth == null) {
+ ofPktCtx.build(p);
+ } else {
+ ofPktCtx.build(eth, p);
+ }
+ ofPktCtx.send();
+ }
+
+ private OFPort buildPort(PortNumber port) {
+ return OFPort.of((int) port.toLong());
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java
new file mode 100644
index 00000000..dc79feff
--- /dev/null
+++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProvider.java
@@ -0,0 +1,176 @@
+/*
+ * 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.of.packet.impl;
+
+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.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.PacketListener;
+import org.projectfloodlight.openflow.protocol.OFPacketOut;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.action.OFAction;
+import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+import org.slf4j.Logger;
+
+import java.nio.ByteBuffer;
+import java.util.Collections;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+
+/**
+ * Provider which uses an OpenFlow controller to detect network
+ * infrastructure links.
+ */
+@Component(immediate = true)
+public class OpenFlowPacketProvider extends AbstractProvider implements PacketProvider {
+
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenFlowController controller;
+
+ private PacketProviderService providerService;
+
+ private final InternalPacketProvider listener = new InternalPacketProvider();
+
+ /**
+ * Creates an OpenFlow link provider.
+ */
+ public OpenFlowPacketProvider() {
+ super(new ProviderId("of", "org.onosproject.provider.openflow"));
+ }
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ controller.addPacketListener(20, listener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ controller.removePacketListener(listener);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ @Override
+ public void emit(OutboundPacket packet) {
+ DeviceId devId = packet.sendThrough();
+ String scheme = devId.toString().split(":")[0];
+
+ if (!scheme.equals(this.id().scheme())) {
+ throw new IllegalArgumentException(
+ "Don't know how to handle Device with scheme " + scheme);
+ }
+
+ Dpid dpid = Dpid.dpid(devId.uri());
+ OpenFlowSwitch sw = controller.getSwitch(dpid);
+ if (sw == null) {
+ log.warn("Device {} isn't available?", devId);
+ return;
+ }
+
+ //Ethernet eth = new Ethernet();
+ //eth.deserialize(packet.data().array(), 0, packet.data().array().length);
+ OFPortDesc p = null;
+ for (Instruction inst : packet.treatment().allInstructions()) {
+ if (inst.type().equals(Instruction.Type.OUTPUT)) {
+ p = portDesc(((OutputInstruction) inst).port());
+ OFPacketOut po = packetOut(sw, packet.data().array(), p.getPortNo());
+ sw.sendMsg(po);
+ }
+ }
+
+ }
+
+ private OFPortDesc portDesc(PortNumber port) {
+ OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc();
+ builder.setPortNo(OFPort.of((int) port.toLong()));
+
+ return builder.build();
+ }
+
+ private OFPacketOut packetOut(OpenFlowSwitch sw, byte[] eth, OFPort out) {
+ OFPacketOut.Builder builder = sw.factory().buildPacketOut();
+ OFAction act = sw.factory().actions()
+ .buildOutput()
+ .setPort(out)
+ .build();
+ return builder
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setInPort(OFPort.CONTROLLER)
+ .setActions(Collections.singletonList(act))
+ .setData(eth)
+ .build();
+ }
+
+ /**
+ * Internal Packet Provider implementation.
+ *
+ */
+ private class InternalPacketProvider implements PacketListener {
+
+ @Override
+ public void handlePacket(OpenFlowPacketContext pktCtx) {
+ DeviceId id = DeviceId.deviceId(Dpid.uri(pktCtx.dpid().value()));
+
+ DefaultInboundPacket inPkt = new DefaultInboundPacket(
+ new ConnectPoint(id, PortNumber.portNumber(pktCtx.inPort())),
+ pktCtx.parsed(), ByteBuffer.wrap(pktCtx.unparsed()));
+
+ DefaultOutboundPacket outPkt = null;
+ if (!pktCtx.isBuffered()) {
+ outPkt = new DefaultOutboundPacket(id, null,
+ ByteBuffer.wrap(pktCtx.unparsed()));
+ }
+
+ OpenFlowCorePacketContext corePktCtx =
+ new OpenFlowCorePacketContext(System.currentTimeMillis(),
+ inPkt, outPkt, pktCtx.isHandled(), pktCtx);
+ providerService.processPacket(corePktCtx);
+ }
+
+ }
+
+
+}
diff --git a/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java
new file mode 100644
index 00000000..dd1130c0
--- /dev/null
+++ b/framework/src/onos/providers/openflow/packet/src/main/java/org/onosproject/provider/of/packet/impl/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2014 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 OpenFlow controller as a means of intercepting and
+ * emitting packets.
+ */
+package org.onosproject.provider.of.packet.impl;
diff --git a/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java b/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java
new file mode 100644
index 00000000..5fded926
--- /dev/null
+++ b/framework/src/onos/providers/openflow/packet/src/test/java/org/onosproject/provider/of/packet/impl/OpenFlowPacketProviderTest.java
@@ -0,0 +1,431 @@
+/*
+ * 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.of.packet.impl;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.flow.instructions.Instruction;
+import org.onosproject.net.flow.instructions.Instructions;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProvider;
+import org.onosproject.net.packet.PacketProviderRegistry;
+import org.onosproject.net.packet.PacketProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openflow.controller.DefaultOpenFlowPacketContext;
+import org.onosproject.openflow.controller.Dpid;
+import org.onosproject.openflow.controller.OpenFlowController;
+import org.onosproject.openflow.controller.OpenFlowEventListener;
+import org.onosproject.openflow.controller.OpenFlowPacketContext;
+import org.onosproject.openflow.controller.OpenFlowSwitch;
+import org.onosproject.openflow.controller.OpenFlowSwitchListener;
+import org.onosproject.openflow.controller.PacketListener;
+import org.onosproject.openflow.controller.RoleState;
+import org.projectfloodlight.openflow.protocol.OFFactory;
+import org.projectfloodlight.openflow.protocol.OFMessage;
+import org.projectfloodlight.openflow.protocol.OFPacketIn;
+import org.projectfloodlight.openflow.protocol.OFPacketInReason;
+import org.projectfloodlight.openflow.protocol.OFPortDesc;
+import org.projectfloodlight.openflow.protocol.ver10.OFFactoryVer10;
+import org.projectfloodlight.openflow.types.MacAddress;
+import org.projectfloodlight.openflow.types.OFBufferId;
+import org.projectfloodlight.openflow.types.OFPort;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import static org.junit.Assert.*;
+
+
+public class OpenFlowPacketProviderTest {
+
+ private static final int PN1 = 100;
+ private static final int PN2 = 200;
+ private static final int PN3 = 300;
+ private static final short VLANID = (short) 100;
+
+ private static final DeviceId DID = DeviceId.deviceId("of:1");
+ private static final DeviceId DID_MISSING = DeviceId.deviceId("of:2");
+ private static final DeviceId DID_WRONG = DeviceId.deviceId("test:1");
+ private static final PortNumber P1 = PortNumber.portNumber(PN1);
+ private static final PortNumber P2 = PortNumber.portNumber(PN2);
+ private static final PortNumber P3 = PortNumber.portNumber(PN3);
+
+ private static final Instruction INST1 = Instructions.createOutput(P1);
+ private static final Instruction INST2 = Instructions.createOutput(P2);
+ private static final Instruction INST3 = Instructions.createOutput(P3);
+
+ private static final OFPortDesc PD1 = portDesc(PN1);
+ private static final OFPortDesc PD2 = portDesc(PN2);
+
+ private static final List<OFPortDesc> PLIST = Lists.newArrayList(PD1, PD2);
+ private static final TrafficTreatment TR = treatment(INST1, INST2);
+ private static final TrafficTreatment TR_MISSING = treatment(INST1, INST3);
+
+ private static final byte[] ANY = new byte [] {0, 0, 0, 0};
+
+ private final OpenFlowPacketProvider provider = new OpenFlowPacketProvider();
+ private final TestPacketRegistry registry = new TestPacketRegistry();
+ private final TestController controller = new TestController();
+
+ private final TestOpenFlowSwitch sw = new TestOpenFlowSwitch();
+
+ @Before
+ public void startUp() {
+ provider.providerRegistry = registry;
+ provider.controller = controller;
+ provider.activate();
+ assertNotNull("listener should be registered", registry.listener);
+ }
+
+ @After
+ public void teardown() {
+ provider.deactivate();
+ assertNull("listeners shouldn't be registered", registry.listener);
+ provider.controller = null;
+ provider.providerRegistry = null;
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void wrongScheme() {
+ sw.setRole(RoleState.MASTER);
+ OutboundPacket schemeFailPkt = outPacket(DID_WRONG, TR, null);
+ provider.emit(schemeFailPkt);
+ assertEquals("message sent incorrectly", 0, sw.sent.size());
+ }
+
+ @Test
+ public void emit() {
+
+ MacAddress mac1 = MacAddress.of("00:00:00:11:00:01");
+ MacAddress mac2 = MacAddress.of("00:00:00:22:00:02");
+
+ ARP arp = new ARP();
+ arp.setSenderProtocolAddress(ANY)
+ .setSenderHardwareAddress(mac1.getBytes())
+ .setTargetHardwareAddress(mac2.getBytes())
+ .setTargetProtocolAddress(ANY)
+ .setHardwareType((short) 0)
+ .setProtocolType((short) 0)
+ .setHardwareAddressLength((byte) 6)
+ .setProtocolAddressLength((byte) 4)
+ .setOpCode((byte) 0);
+
+ Ethernet eth = new Ethernet();
+ eth.setVlanID(VLANID)
+ .setEtherType(Ethernet.TYPE_ARP)
+ .setSourceMACAddress("00:00:00:11:00:01")
+ .setDestinationMACAddress("00:00:00:22:00:02")
+ .setPayload(arp);
+
+ //the should-be working setup.
+ OutboundPacket passPkt = outPacket(DID, TR, eth);
+ sw.setRole(RoleState.MASTER);
+ provider.emit(passPkt);
+ assertEquals("invalid switch", sw, controller.current);
+ assertEquals("message not sent", PLIST.size(), sw.sent.size());
+ sw.sent.clear();
+
+ //wrong Role
+ //sw.setRole(RoleState.SLAVE);
+ //provider.emit(passPkt);
+ //assertEquals("invalid switch", sw, controller.current);
+ //assertEquals("message sent incorrectly", 0, sw.sent.size());
+
+ //sw.setRole(RoleState.MASTER);
+
+ //missing switch
+ OutboundPacket swFailPkt = outPacket(DID_MISSING, TR, eth);
+ provider.emit(swFailPkt);
+ assertNull("invalid switch", controller.current);
+ assertEquals("message sent incorrectly", 0, sw.sent.size());
+
+ //to missing port
+ //OutboundPacket portFailPkt = outPacket(DID, TR_MISSING, eth);
+ //provider.emit(portFailPkt);
+ //assertEquals("extra message sent", 1, sw.sent.size());
+
+ }
+
+ @Test
+ public void handlePacket() {
+ OFPacketIn pkt = sw.factory().buildPacketIn()
+ .setBufferId(OFBufferId.NO_BUFFER)
+ .setInPort(OFPort.NO_MASK)
+ .setReason(OFPacketInReason.INVALID_TTL)
+ .build();
+
+ controller.processPacket(null, pkt);
+ assertNotNull("message unprocessed", registry.ctx);
+
+ }
+
+ private static OFPortDesc portDesc(int port) {
+ OFPortDesc.Builder builder = OFFactoryVer10.INSTANCE.buildPortDesc();
+ builder.setPortNo(OFPort.of(port));
+
+ return builder.build();
+ }
+
+ private static TrafficTreatment treatment(Instruction ... insts) {
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
+ for (Instruction i : insts) {
+ builder.add(i);
+ }
+ return builder.build();
+ }
+
+ private static OutboundPacket outPacket(
+ DeviceId d, TrafficTreatment t, Ethernet e) {
+ ByteBuffer buf = null;
+ if (e != null) {
+ buf = ByteBuffer.wrap(e.serialize());
+ }
+ return new DefaultOutboundPacket(d, t, buf);
+ }
+
+ private class TestPacketRegistry implements PacketProviderRegistry {
+
+ PacketProvider listener = null;
+ PacketContext ctx = null;
+
+ @Override
+ public PacketProviderService register(PacketProvider provider) {
+ listener = provider;
+ return new TestPacketProviderService();
+ }
+
+ @Override
+ public void unregister(PacketProvider provider) {
+ listener = null;
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return Sets.newHashSet(listener.id());
+ }
+
+ private class TestPacketProviderService implements PacketProviderService {
+
+ @Override
+ public PacketProvider provider() {
+ return null;
+ }
+
+ @Override
+ public void processPacket(PacketContext context) {
+ ctx = context;
+ }
+
+ }
+ }
+
+ private class TestController implements OpenFlowController {
+
+ PacketListener pktListener;
+ OpenFlowSwitch current;
+
+ @Override
+ public Iterable<OpenFlowSwitch> getSwitches() {
+ return null;
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getMasterSwitches() {
+ return null;
+ }
+
+ @Override
+ public Iterable<OpenFlowSwitch> getEqualSwitches() {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getSwitch(Dpid dpid) {
+ if (dpid.equals(Dpid.dpid(DID.uri()))) {
+ current = sw;
+ } else {
+ current = null;
+ }
+ return current;
+ }
+
+ @Override
+ public OpenFlowSwitch getMasterSwitch(Dpid dpid) {
+ return null;
+ }
+
+ @Override
+ public OpenFlowSwitch getEqualSwitch(Dpid dpid) {
+ return null;
+ }
+
+ @Override
+ public void addListener(OpenFlowSwitchListener listener) {
+ }
+
+ @Override
+ public void removeListener(OpenFlowSwitchListener listener) {
+ }
+
+ @Override
+ public void addPacketListener(int priority, PacketListener listener) {
+ pktListener = listener;
+ }
+
+ @Override
+ public void removePacketListener(PacketListener listener) {
+ }
+
+ @Override
+ public void addEventListener(OpenFlowEventListener listener) {
+ }
+
+ @Override
+ public void removeEventListener(OpenFlowEventListener listener) {
+ }
+
+ @Override
+ public void write(Dpid dpid, OFMessage msg) {
+ }
+
+ @Override
+ public void processPacket(Dpid dpid, OFMessage msg) {
+ OpenFlowPacketContext pktCtx =
+ DefaultOpenFlowPacketContext.
+ packetContextFromPacketIn(sw, (OFPacketIn) msg);
+ pktListener.handlePacket(pktCtx);
+ }
+
+ @Override
+ public void setRole(Dpid dpid, RoleState role) {
+ }
+
+ }
+
+ private class TestOpenFlowSwitch implements OpenFlowSwitch {
+
+ RoleState state;
+ List<OFMessage> sent = new ArrayList<OFMessage>();
+ OFFactory factory = OFFactoryVer10.INSTANCE;
+
+ @Override
+ public void sendMsg(OFMessage msg) {
+ sent.add(msg);
+ }
+
+ @Override
+ public void sendMsg(List<OFMessage> msgs) {
+ }
+
+ @Override
+ public void handleMessage(OFMessage fromSwitch) {
+ }
+
+ @Override
+ public void setRole(RoleState role) {
+ state = role;
+ }
+
+ @Override
+ public RoleState getRole() {
+ return state;
+ }
+
+ @Override
+ public List<OFPortDesc> getPorts() {
+ return PLIST;
+ }
+
+ @Override
+ public OFFactory factory() {
+ return factory;
+ }
+
+ @Override
+ public String getStringId() {
+ return null;
+ }
+
+ @Override
+ public long getId() {
+ return 0;
+ }
+
+ @Override
+ public String manufacturerDescription() {
+ return null;
+ }
+
+ @Override
+ public String datapathDescription() {
+ return null;
+ }
+
+ @Override
+ public String hardwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String softwareDescription() {
+ return null;
+ }
+
+ @Override
+ public String serialNumber() {
+ return null;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return true;
+ }
+
+ @Override
+ public void disconnectSwitch() {
+ }
+
+ @Override
+ public void returnRoleReply(RoleState requested, RoleState reponse) {
+ }
+ @Override
+ public Device.Type deviceType() {
+ return Device.Type.SWITCH;
+ }
+
+ @Override
+ public String channelId() {
+ return "1.2.3.4:1";
+ }
+
+
+ }
+
+}
diff --git a/framework/src/onos/providers/openflow/pom.xml b/framework/src/onos/providers/openflow/pom.xml
new file mode 100644
index 00000000..99ff6649
--- /dev/null
+++ b/framework/src/onos/providers/openflow/pom.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-of-providers</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS OpenFlow protocol adapters</description>
+
+ <modules>
+ <module>device</module>
+ <module>packet</module>
+ <module>flow</module>
+ <module>group</module>
+ <module>meter</module>
+ <module>app</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-of-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-of-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/app/app.xml b/framework/src/onos/providers/ovsdb/app/app.xml
new file mode 100644
index 00000000..c3334994
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/app/app.xml
@@ -0,0 +1,30 @@
+<?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.ovsdb" 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-ovsdb-rfc/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-ovsdb-api/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-ovsdb-ctl/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-drivers/${project.version}</artifact>
+
+ <artifact>mvn:${project.groupId}/onos-ovsdb-provider-device/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-ovsdb-provider-host/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-ovsdb-provider-tunnel/${project.version}</artifact>
+</app>
diff --git a/framework/src/onos/providers/ovsdb/app/features.xml b/framework/src/onos/providers/ovsdb/app/features.xml
new file mode 100644
index 00000000..19fab4cd
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/app/features.xml
@@ -0,0 +1,35 @@
+<?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}">
+ <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>
+ <bundle>mvn:io.netty/netty-common/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-buffer/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-transport/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-handler/4.0.23.Final</bundle>
+ <bundle>mvn:io.netty/netty-codec/4.0.23.Final</bundle>
+ <bundle>mvn:${project.groupId}/onos-ovsdb-rfc/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-ovsdb-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-ovsdb-ctl/${project.version}</bundle>
+
+ <bundle>mvn:${project.groupId}/onos-ovsdb-provider-device/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-ovsdb-provider-host/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-ovsdb-provider-tunnel/${project.version}</bundle>
+ </feature>
+</features>
diff --git a/framework/src/onos/providers/ovsdb/app/pom.xml b/framework/src/onos/providers/ovsdb/app/pom.xml
new file mode 100644
index 00000000..7bb506ba
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/app/pom.xml
@@ -0,0 +1,70 @@
+<?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.
+ -->
+<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-ovsdb-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-ovsdatabase</artifactId>
+ <packaging>pom</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-rfc</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-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-ovsdb-provider-device</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-provider-host</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-provider-tunnel</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/device/pom.xml b/framework/src/onos/providers/ovsdb/device/pom.xml
new file mode 100644
index 00000000..e5010fa6
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/device/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!--
+ ~ 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
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-ovsdb-provider-device</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProvider.java b/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProvider.java
new file mode 100644
index 00000000..10e745e3
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProvider.java
@@ -0,0 +1,134 @@
+/*
+ * 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.ovsdb.providers.device;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.URI;
+
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.ChassisId;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.SparseAnnotations;
+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.device.DeviceService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.onosproject.ovsdb.controller.OvsdbNodeListener;
+import org.slf4j.Logger;
+
+/**
+ * Provider which uses an ovsdb controller to detect device.
+ */
+@Component(immediate = true)
+@Service
+public class OvsdbDeviceProvider extends AbstractProvider
+ implements DeviceProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OvsdbController controller;
+
+ private DeviceProviderService providerService;
+ private OvsdbNodeListener innerNodeListener = new InnerOvsdbNodeListener();
+ protected static final String ISNOTNULL = "OvsdbNodeId is not null";
+ private static final String UNKNOWN = "unknown";
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ controller.addNodeListener(innerNodeListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ public OvsdbDeviceProvider() {
+ super(new ProviderId("ovsdb", "org.onosproject.ovsdb.provider.device"));
+ }
+
+ @Override
+ public void triggerProbe(DeviceId deviceId) {
+ // TODO: This will be implemented later.
+ log.info("Triggering probe on device {}", deviceId);
+ }
+
+ @Override
+ public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public boolean isReachable(DeviceId deviceId) {
+ return true;
+ }
+
+ private class InnerOvsdbNodeListener implements OvsdbNodeListener {
+
+ @Override
+ public void nodeAdded(OvsdbNodeId nodeId) {
+ checkNotNull(nodeId, ISNOTNULL);
+ DeviceId deviceId = DeviceId.deviceId(nodeId.toString());
+ URI uri = URI.create(nodeId.toString());
+ ChassisId cid = new ChassisId();
+ String ipAddress = nodeId.getIpAddress();
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("ipaddress", ipAddress).build();
+ DeviceDescription deviceDescription = new DefaultDeviceDescription(
+ uri,
+ Device.Type.CONTROLLER,
+ UNKNOWN, UNKNOWN,
+ UNKNOWN, UNKNOWN,
+ cid,
+ annotations);
+ providerService.deviceConnected(deviceId, deviceDescription);
+
+ }
+
+ @Override
+ public void nodeRemoved(OvsdbNodeId nodeId) {
+ checkNotNull(nodeId, ISNOTNULL);
+ DeviceId deviceId = DeviceId.deviceId(nodeId.toString());
+ providerService.deviceDisconnected(deviceId);
+
+ }
+ }
+}
diff --git a/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/package-info.java b/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/package-info.java
new file mode 100644
index 00000000..1636371a
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/device/src/main/java/org/onosproject/ovsdb/providers/device/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 ovsdb controller as a means of infrastructure device discovery.
+ *
+ */
+package org.onosproject.ovsdb.providers.device; \ No newline at end of file
diff --git a/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java
new file mode 100644
index 00000000..5e4c5677
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/device/src/test/java/org/onosproject/ovsdb/providers/device/OvsdbDeviceProviderTest.java
@@ -0,0 +1,198 @@
+/*
+ * 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.ovsdb.providers.device;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+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;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbEventListener;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.onosproject.ovsdb.controller.OvsdbNodeListener;
+
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.Multimap;
+
+/**
+ * Test for ovsdb device provider.
+ */
+public class OvsdbDeviceProviderTest {
+ private final OvsdbDeviceProvider provider = new OvsdbDeviceProvider();
+ private final TestDeviceRegistry registry = new TestDeviceRegistry();
+ private final TestController controller = new TestController();
+
+ @Before
+ public void startUp() {
+ provider.providerRegistry = registry;
+ provider.controller = controller;
+ provider.activate();
+ assertNotNull("provider should be registered", registry.provider);
+ }
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.controller = null;
+ provider.providerRegistry = null;
+ }
+
+ @Test
+ public void triggerProbe() {
+
+ }
+
+ @Test
+ public void testNodeAdded() {
+ controller.listener.nodeAdded(new OvsdbNodeId(IpAddress
+ .valueOf("192.168.202.36"), 5000));
+ assertEquals("ovsdb node added", 1, registry.connected.size());
+ }
+
+ @Test
+ public void testNodeRemoved() {
+ controller.listener.nodeAdded(new OvsdbNodeId(IpAddress
+ .valueOf("192.168.202.36"), 5000));
+ controller.listener.nodeRemoved(new OvsdbNodeId(IpAddress
+ .valueOf("192.168.202.36"), 5000));
+ assertEquals("ovsdb node removded", 0, registry.connected.size());
+ }
+
+ private class TestDeviceRegistry implements DeviceProviderRegistry {
+ DeviceProvider provider;
+
+ Set<DeviceId> connected = new HashSet<>();
+ Multimap<DeviceId, PortDescription> ports = HashMultimap.create();
+ PortDescription descr = null;
+
+ @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) {
+ connected.add(deviceId);
+ }
+
+ @Override
+ public void deviceDisconnected(DeviceId deviceId) {
+ connected.remove(deviceId);
+ ports.removeAll(deviceId);
+ }
+
+ @Override
+ public void updatePorts(DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+ for (PortDescription p : portDescriptions) {
+ ports.put(deviceId, p);
+ }
+ }
+
+ @Override
+ public void portStatusChanged(DeviceId deviceId,
+ PortDescription portDescription) {
+ ports.put(deviceId, portDescription);
+ descr = portDescription;
+ }
+
+ @Override
+ public void receivedRoleReply(DeviceId deviceId,
+ MastershipRole requested,
+ MastershipRole response) {
+ }
+
+ @Override
+ public void updatePortStatistics(DeviceId deviceId,
+ Collection<PortStatistics> portStatistics) {
+
+ }
+
+ }
+ }
+
+ private class TestController implements OvsdbController {
+ OvsdbNodeListener listener = null;
+
+ @Override
+ public void addNodeListener(OvsdbNodeListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public void removeNodeListener(OvsdbNodeListener listener) {
+ this.listener = null;
+ }
+
+ @Override
+ public void addOvsdbEventListener(OvsdbEventListener listener) {
+
+ }
+
+ @Override
+ public void removeOvsdbEventListener(OvsdbEventListener listener) {
+
+ }
+
+ @Override
+ public List<OvsdbNodeId> getNodeIds() {
+ return null;
+ }
+
+ @Override
+ public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
+ return null;
+ }
+
+ }
+
+}
diff --git a/framework/src/onos/providers/ovsdb/host/pom.xml b/framework/src/onos/providers/ovsdb/host/pom.xml
new file mode 100644
index 00000000..0bd88555
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/host/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!--
+ ~ 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
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-ovsdb-provider-host</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/OvsdbHostProvider.java b/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/OvsdbHostProvider.java
new file mode 100644
index 00000000..8d2fd142
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/OvsdbHostProvider.java
@@ -0,0 +1,148 @@
+/*
+ * 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.ovsdb.provider.host;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.util.Tools.toHex;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.ovsdb.controller.EventSubject;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbEvent;
+import org.onosproject.ovsdb.controller.OvsdbEventListener;
+import org.onosproject.ovsdb.controller.OvsdbEventSubject;
+import org.slf4j.Logger;
+
+/**
+ * Provider which uses an ovsdb controller to detect host.
+ */
+@Component(immediate = true)
+@Service
+public class OvsdbHostProvider extends AbstractProvider implements HostProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected HostService hostService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected CoreService coreService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OvsdbController controller;
+
+ private HostProviderService providerService;
+ private OvsdbEventListener innerEventListener = new InnerOvsdbEventListener();
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ controller.addOvsdbEventListener(innerEventListener);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ public OvsdbHostProvider() {
+ super(new ProviderId("ovsdb", "org.onosproject.ovsdb.provider.host"));
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ log.info("Triggering probe on host {}", host);
+ }
+
+ private class InnerOvsdbEventListener implements OvsdbEventListener {
+
+ @Override
+ public void handle(OvsdbEvent<EventSubject> event) {
+ OvsdbEventSubject subject = null;
+ if (event.subject() instanceof OvsdbEventSubject) {
+ subject = (OvsdbEventSubject) event.subject();
+ }
+ checkNotNull(subject, "EventSubject is not null");
+ // If ifaceid is null,it indicates this is not a vm port.
+ if (subject.ifaceid() == null) {
+ return;
+ }
+ switch (event.type()) {
+ case PORT_ADDED:
+ HostId hostId = HostId.hostId(subject.hwAddress(), null);
+ DeviceId deviceId = DeviceId.deviceId(uri(subject.dpid().value()));
+ PortNumber portNumber = PortNumber.portNumber(subject
+ .portNumber().value(), subject.portName().value());
+ HostLocation loaction = new HostLocation(deviceId, portNumber,
+ 0L);
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("ifaceid", subject.ifaceid().value()).build();
+ HostDescription hostDescription = new DefaultHostDescription(
+ subject.hwAddress(),
+ VlanId.vlanId(),
+ loaction,
+ annotations);
+ providerService.hostDetected(hostId, hostDescription);
+ break;
+ case PORT_REMOVED:
+ HostId host = HostId.hostId(subject.hwAddress(), null);
+ providerService.hostVanished(host);
+ break;
+ default:
+ break;
+ }
+
+ }
+
+ }
+
+ public URI uri(String value) {
+ try {
+ return new URI("of", toHex(Long.valueOf(value)), null);
+ } catch (URISyntaxException e) {
+ return null;
+ }
+ }
+}
diff --git a/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/package-info.java b/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/package-info.java
new file mode 100644
index 00000000..895ae2ec
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/host/src/main/java/org/onosproject/ovsdb/provider/host/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 ovsdb controller as a means of infrastructure host discovery.
+ *
+ */
+package org.onosproject.ovsdb.provider.host; \ No newline at end of file
diff --git a/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java
new file mode 100644
index 00000000..bf18606c
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/host/src/test/java/org/onosproject/ovsdb/provider/host/OvsdbHostProviderTest.java
@@ -0,0 +1,199 @@
+/*
+ * 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.ovsdb.provider.host;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.HostId;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.ovsdb.controller.DefaultEventSubject;
+import org.onosproject.ovsdb.controller.EventSubject;
+import org.onosproject.ovsdb.controller.OvsdbClientService;
+import org.onosproject.ovsdb.controller.OvsdbController;
+import org.onosproject.ovsdb.controller.OvsdbDatapathId;
+import org.onosproject.ovsdb.controller.OvsdbEvent;
+import org.onosproject.ovsdb.controller.OvsdbEventListener;
+import org.onosproject.ovsdb.controller.OvsdbIfaceId;
+import org.onosproject.ovsdb.controller.OvsdbNodeId;
+import org.onosproject.ovsdb.controller.OvsdbNodeListener;
+import org.onosproject.ovsdb.controller.OvsdbPortName;
+import org.onosproject.ovsdb.controller.OvsdbPortNumber;
+import org.onosproject.ovsdb.controller.OvsdbPortType;
+
+/**
+ * Test for ovsdb host provider.
+ */
+public class OvsdbHostProviderTest {
+ private static final MacAddress MAC = MacAddress
+ .valueOf("00:00:11:00:00:01");
+ private final OvsdbHostProvider provider = new OvsdbHostProvider();
+ private final TestHostRegistry hostRegistry = new TestHostRegistry();
+ protected OvsdbControllerTest controller = new OvsdbControllerTest();
+ private TestHostProviderService providerService;
+
+ @Before
+ public void setUp() {
+ provider.providerRegistry = hostRegistry;
+ provider.controller = controller;
+ provider.activate();
+ }
+
+ @Test
+ public void basics() {
+ assertNotNull("registration expected", providerService);
+ assertEquals("incorrect provider", provider, providerService.provider());
+ }
+
+ @Test
+ public void portAdded() {
+ DefaultEventSubject eventSubject = new DefaultEventSubject(MAC, null,
+ new OvsdbPortName("portName"),
+ new OvsdbPortNumber(0L),
+ new OvsdbDatapathId("10002"),
+ new OvsdbPortType("vxlan"),
+ new OvsdbIfaceId("102345"));
+ controller.ovsdbEventListener
+ .handle(new OvsdbEvent<EventSubject>(
+ OvsdbEvent.Type.PORT_ADDED,
+ eventSubject));
+ assertNotNull("never went throught the provider service",
+ providerService.added);
+
+ }
+
+ @Test
+ public void portRemoved() {
+ DefaultEventSubject eventSubject = new DefaultEventSubject(MAC, null,
+ new OvsdbPortName("portName"),
+ new OvsdbPortNumber(0L),
+ new OvsdbDatapathId("10002"),
+ new OvsdbPortType("vxlan"),
+ new OvsdbIfaceId("102345"));
+ controller.ovsdbEventListener
+ .handle(new OvsdbEvent<EventSubject>(
+ OvsdbEvent.Type.PORT_REMOVED,
+ eventSubject));
+ assertEquals("port status unhandled", 1, providerService.removeCount);
+ }
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.coreService = null;
+ provider.providerRegistry = null;
+ }
+
+ private class TestHostRegistry implements HostProviderRegistry {
+
+ @Override
+ public HostProviderService register(HostProvider provider) {
+ providerService = new TestHostProviderService(provider);
+ return providerService;
+ }
+
+ @Override
+ public void unregister(HostProvider provider) {
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+
+ }
+
+ private class TestHostProviderService
+ extends AbstractProviderService<HostProvider>
+ implements HostProviderService {
+
+ DeviceId added = null;
+ DeviceId moved = null;
+ DeviceId spine = null;
+ public int removeCount;
+
+ protected TestHostProviderService(HostProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public void hostDetected(HostId hostId, HostDescription hostDescription) {
+ DeviceId descr = hostDescription.location().deviceId();
+ if (added == null) {
+ added = descr;
+ } else if ((moved == null) && !descr.equals(added)) {
+ moved = descr;
+ } else {
+ spine = descr;
+ }
+ }
+
+ @Override
+ public void hostVanished(HostId hostId) {
+ removeCount++;
+ }
+
+ }
+
+ private class OvsdbControllerTest implements OvsdbController {
+ private OvsdbEventListener ovsdbEventListener = null;
+
+ @Override
+ public void addNodeListener(OvsdbNodeListener listener) {
+
+ }
+
+ @Override
+ public void removeNodeListener(OvsdbNodeListener listener) {
+
+ }
+
+ @Override
+ public void addOvsdbEventListener(OvsdbEventListener listener) {
+ ovsdbEventListener = listener;
+
+ }
+
+ @Override
+ public void removeOvsdbEventListener(OvsdbEventListener listener) {
+ ovsdbEventListener = null;
+
+ }
+
+ @Override
+ public List<OvsdbNodeId> getNodeIds() {
+ return null;
+ }
+
+ @Override
+ public OvsdbClientService getOvsdbClient(OvsdbNodeId nodeId) {
+ return null;
+ }
+ }
+}
diff --git a/framework/src/onos/providers/ovsdb/pom.xml b/framework/src/onos/providers/ovsdb/pom.xml
new file mode 100644
index 00000000..bf215bc0
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/pom.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<project
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-ovsdb-providers</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>device</module>
+ <module>host</module>
+ <module>tunnel</module>
+ <module>app</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/tunnel/pom.xml b/framework/src/onos/providers/ovsdb/tunnel/pom.xml
new file mode 100644
index 00000000..46ea585b
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/tunnel/pom.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<!--
+ ~ 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
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-ovsdb-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <artifactId>onos-ovsdb-provider-tunnel</artifactId>
+ <packaging>bundle</packaging>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProvider.java b/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProvider.java
new file mode 100644
index 00000000..244e6fe0
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProvider.java
@@ -0,0 +1,123 @@
+/*
+ * 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.ovsdb.provider.tunnel;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+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.apache.felix.scr.annotations.Service;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelDescription;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelProvider;
+import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
+import org.onosproject.incubator.net.tunnel.TunnelProviderService;
+import org.onosproject.incubator.net.tunnel.TunnelService;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Path;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.slf4j.Logger;
+
+/**
+ * Provider which uses when tunnel added/removed.
+ */
+@Component(immediate = true)
+@Service
+public class OvsdbTunnelProvider extends AbstractProvider
+ implements TunnelProvider {
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelProviderRegistry providerRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelService tunnelService;
+
+ private TunnelProviderService providerService;
+
+ @Activate
+ public void activate() {
+ providerService = providerRegistry.register(this);
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ providerRegistry.unregister(this);
+ providerService = null;
+ log.info("Stopped");
+ }
+
+ public OvsdbTunnelProvider() {
+ super(new ProviderId("ovsdb", "org.onosproject.ovsdb.provider.tunnel"));
+ }
+
+ @Override
+ public void setupTunnel(Tunnel tunnel, Path path) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public void releaseTunnel(Tunnel tunnel) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public void updateTunnel(Tunnel tunnel, Path path) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
+ // TODO: This will be implemented later.
+ }
+
+ @Override
+ public TunnelId tunnelAdded(TunnelDescription tunnel) {
+ return providerService.tunnelAdded(tunnel);
+ }
+
+ @Override
+ public void tunnelRemoved(TunnelDescription tunnel) {
+ providerService.tunnelRemoved(tunnel);
+ }
+
+ @Override
+ public void tunnelUpdated(TunnelDescription tunnel) {
+ providerService.tunnelUpdated(tunnel);
+ }
+
+ @Override
+ public Tunnel tunnelQueryById(TunnelId tunnelId) {
+ // TODO: This will be implemented later.
+ return null;
+ }
+}
diff --git a/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/package-info.java b/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/package-info.java
new file mode 100644
index 00000000..4f8a1095
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/tunnel/src/main/java/org/onosproject/ovsdb/provider/tunnel/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * 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 ovsdb controller as a means of infrastructure tunnel discovery.
+ *
+ */
+package org.onosproject.ovsdb.provider.tunnel; \ No newline at end of file
diff --git a/framework/src/onos/providers/ovsdb/tunnel/src/test/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProviderTest.java b/framework/src/onos/providers/ovsdb/tunnel/src/test/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProviderTest.java
new file mode 100644
index 00000000..3d1549e5
--- /dev/null
+++ b/framework/src/onos/providers/ovsdb/tunnel/src/test/java/org/onosproject/ovsdb/provider/tunnel/OvsdbTunnelProviderTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.ovsdb.provider.tunnel;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultTunnelDescription;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelDescription;
+import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.incubator.net.tunnel.TunnelProvider;
+import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
+import org.onosproject.incubator.net.tunnel.TunnelProviderService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.Link;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.provider.AbstractProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+/**
+ * Test for ovsdb tunnel provider.
+ */
+public class OvsdbTunnelProviderTest {
+ private final OvsdbTunnelProvider provider = new OvsdbTunnelProvider();
+ private final TestTunnelRegistry tunnelRegistry = new TestTunnelRegistry();
+ private TestTunnelProviderService providerService;
+
+ @Before
+ public void setUp() {
+ provider.providerRegistry = tunnelRegistry;
+ provider.activate();
+ }
+
+ @Test
+ public void basics() {
+ assertNotNull("registration expected", providerService);
+ assertEquals("incorrect provider", provider, providerService.provider());
+ }
+
+ @Test
+ public void testTunnelAdded() {
+ TunnelEndPoint src = IpTunnelEndPoint.ipTunnelPoint(IpAddress
+ .valueOf("192.168.1.1"));
+ TunnelEndPoint dst = IpTunnelEndPoint.ipTunnelPoint(IpAddress
+ .valueOf("192.168.1.3"));
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("bandwidth", "1024").build();
+ Link link = new DefaultLink(
+ this.provider.id(),
+ ConnectPoint.deviceConnectPoint("192.168.2.3/20"),
+ ConnectPoint.deviceConnectPoint("192.168.2.4/30"),
+ Link.Type.DIRECT);
+ List<Link> links = new ArrayList<Link>();
+ links.add(link);
+ TunnelDescription tunnel = new DefaultTunnelDescription(
+ TunnelId.valueOf("1234"),
+ src,
+ dst,
+ Tunnel.Type.VXLAN,
+ new DefaultGroupId(0),
+ this.provider.id(),
+ TunnelName.tunnelName("tunnel12"),
+ new DefaultPath(this.provider.id(), links, 0.3),
+ annotations);
+ provider.tunnelAdded(tunnel);
+ assertEquals(1, providerService.tunnelSet.size());
+ }
+
+ @Test
+ public void testTunnelRemoved() {
+ TunnelEndPoint src = IpTunnelEndPoint.ipTunnelPoint(IpAddress
+ .valueOf("192.168.1.1"));
+ TunnelEndPoint dst = IpTunnelEndPoint.ipTunnelPoint(IpAddress
+ .valueOf("192.168.1.3"));
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("bandwidth", "1024").build();
+ Link link = new DefaultLink(
+ this.provider.id(),
+ ConnectPoint.deviceConnectPoint("192.168.2.3/20"),
+ ConnectPoint.deviceConnectPoint("192.168.2.4/30"),
+ Link.Type.DIRECT);
+ List<Link> links = new ArrayList<Link>();
+ links.add(link);
+ TunnelDescription tunnel = new DefaultTunnelDescription(
+ TunnelId.valueOf("1234"),
+ src,
+ dst,
+ Tunnel.Type.VXLAN,
+ new DefaultGroupId(0),
+ this.provider.id(),
+ TunnelName.tunnelName("tunnel1"),
+ new DefaultPath(this.provider.id(), links, 0.3),
+ annotations);
+ provider.tunnelRemoved(tunnel);
+ assertEquals(0, providerService.tunnelSet.size());
+ }
+
+ @After
+ public void tearDown() {
+ provider.deactivate();
+ provider.providerRegistry = null;
+ }
+
+ private class TestTunnelRegistry implements TunnelProviderRegistry {
+
+ @Override
+ public TunnelProviderService register(TunnelProvider provider) {
+ providerService = new TestTunnelProviderService(provider);
+ return providerService;
+ }
+
+ @Override
+ public void unregister(TunnelProvider provider) {
+
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+
+ }
+
+ private class TestTunnelProviderService
+ extends AbstractProviderService<TunnelProvider>
+ implements TunnelProviderService {
+ Set<TunnelDescription> tunnelSet = new HashSet<TunnelDescription>();
+
+ protected TestTunnelProviderService(TunnelProvider provider) {
+ super(provider);
+ }
+
+ @Override
+ public TunnelId tunnelAdded(TunnelDescription tunnel) {
+ tunnelSet.add(tunnel);
+ return null;
+ }
+
+ @Override
+ public void tunnelRemoved(TunnelDescription tunnel) {
+ tunnelSet.remove(tunnel);
+ }
+
+ @Override
+ public void tunnelUpdated(TunnelDescription tunnel) {
+
+ }
+
+ @Override
+ public Tunnel tunnelQueryById(TunnelId tunnelId) {
+ return null;
+ }
+
+ }
+}
diff --git a/framework/src/onos/providers/pcep/app/app.xml b/framework/src/onos/providers/pcep/app/app.xml
new file mode 100644
index 00000000..ea3d1d17
--- /dev/null
+++ b/framework/src/onos/providers/pcep/app/app.xml
@@ -0,0 +1,27 @@
+<?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.pcep" 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-app-pcep-api/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-pcepio/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-pcep-controller-api/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-pcep-controller-impl/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-pcep-provider-topology/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/onos-pcep-provider-tunnel/${project.version}</artifact>
+</app>
diff --git a/framework/src/onos/providers/pcep/app/features.xml b/framework/src/onos/providers/pcep/app/features.xml
new file mode 100644
index 00000000..65a221bb
--- /dev/null
+++ b/framework/src/onos/providers/pcep/app/features.xml
@@ -0,0 +1,29 @@
+<?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}">
+ <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>
+ <bundle>mvn:${project.groupId}/onos-app-pcep-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-pcepio/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-pcep-controller-api/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-pcep-controller-impl/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-pcep-provider-topology/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/onos-pcep-provider-tunnel/${project.version}</bundle>
+ </feature>
+</features>
diff --git a/framework/src/onos/providers/pcep/app/pom.xml b/framework/src/onos/providers/pcep/app/pom.xml
new file mode 100644
index 00000000..bce88d99
--- /dev/null
+++ b/framework/src/onos/providers/pcep/app/pom.xml
@@ -0,0 +1,47 @@
+<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-pcep-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-pcep</artifactId>
+ <packaging>pom</packaging>
+ <description>PCEP protocol southbound providers</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-pcep-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcepio</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcep-controller-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcep-controller-impl</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcep-provider-topology</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcep-provider-tunnel</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ </dependencies>
+</project>
diff --git a/framework/src/onos/providers/pcep/pom.xml b/framework/src/onos/providers/pcep/pom.xml
new file mode 100644
index 00000000..66a71141
--- /dev/null
+++ b/framework/src/onos/providers/pcep/pom.xml
@@ -0,0 +1,17 @@
+<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.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <artifactId>onos-pcep-providers</artifactId>
+ <packaging>pom</packaging>
+ <description>PCEP protocol providers root</description>
+ <modules>
+ <module>topology</module>
+ <module>tunnel</module>
+ <module>app</module>
+ </modules>
+</project> \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/topology/pom.xml b/framework/src/onos/providers/pcep/topology/pom.xml
new file mode 100644
index 00000000..cd8589b9
--- /dev/null
+++ b/framework/src/onos/providers/pcep/topology/pom.xml
@@ -0,0 +1,18 @@
+<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-pcep-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <artifactId>onos-pcep-provider-topology</artifactId>
+ <packaging>bundle</packaging>
+ <description>PCEP topology provider</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-pcep-api</artifactId>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/PcepTopologyProvider.java b/framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/PcepTopologyProvider.java
new file mode 100644
index 00000000..38eaf1fe
--- /dev/null
+++ b/framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/PcepTopologyProvider.java
@@ -0,0 +1,321 @@
+/*
+ * 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.pcep.topology.impl;
+
+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.onlab.packet.ChassisId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.mastership.MastershipAdminService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Link.Type;
+import org.onosproject.net.MastershipRole;
+import org.onosproject.net.OchPort;
+import org.onosproject.net.OduCltPort;
+import org.onosproject.net.OmsPort;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DefaultDeviceDescription;
+import org.onosproject.net.device.DefaultPortDescription;
+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.DeviceService;
+import org.onosproject.net.device.OchPortDescription;
+import org.onosproject.net.device.OduCltPortDescription;
+import org.onosproject.net.device.OmsPortDescription;
+import org.onosproject.net.device.PortDescription;
+import org.onosproject.net.link.DefaultLinkDescription;
+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.LinkService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.pcep.api.PcepController;
+import org.onosproject.pcep.api.PcepDpid;
+import org.onosproject.pcep.api.PcepLink;
+import org.onosproject.pcep.api.PcepLink.PortType;
+import org.onosproject.pcep.api.PcepLinkListener;
+import org.onosproject.pcep.api.PcepOperator.OperationType;
+import org.onosproject.pcep.api.PcepSwitch;
+import org.onosproject.pcep.api.PcepSwitchListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.pcep.api.PcepDpid.uri;
+
+/**
+ * Provider which uses an PCEP controller to detect network infrastructure
+ * topology.
+ */
+@Component(immediate = true)
+public class PcepTopologyProvider extends AbstractProvider
+ implements LinkProvider, DeviceProvider {
+
+ public PcepTopologyProvider() {
+ super(new ProviderId("pcep", "org.onosproject.provider.pcep"));
+ }
+
+ private static final Logger log = LoggerFactory
+ .getLogger(PcepTopologyProvider.class);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkProviderRegistry linkProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceProviderRegistry deviceProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PcepController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipAdminService mastershipAdminService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClusterService clusterService;
+
+ private DeviceProviderService deviceProviderService;
+ private LinkProviderService linkProviderService;
+
+ private HashMap<Long, List<PortDescription>> portMap = new HashMap<>();
+ private InternalLinkProvider listener = new InternalLinkProvider();
+
+ @Activate
+ public void activate() {
+ linkProviderService = linkProviderRegistry.register(this);
+ deviceProviderService = deviceProviderRegistry.register(this);
+ controller.addListener(listener);
+ controller.addLinkListener(listener);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ linkProviderRegistry.unregister(this);
+ linkProviderService = null;
+ controller.removeListener(listener);
+ controller.removeLinkListener(listener);
+ }
+
+ private List<PortDescription> buildPortDescriptions(PcepDpid dpid,
+ Port port,
+ PortType portType) {
+
+ List<PortDescription> portList;
+
+ if (portMap.containsKey(dpid.value())) {
+ portList = portMap.get(dpid.value());
+ } else {
+ portList = new ArrayList<>();
+ }
+ if (port != null && portType != null) {
+ portList.add(buildPortDescription(port, portType));
+ }
+
+ portMap.put(dpid.value(), portList);
+ return portList;
+ }
+
+ private PortDescription buildPortDescription(Port port, PortType portType) {
+ PortDescription portDescription;
+
+ switch (portType) {
+ case OCH_PORT:
+ OchPort ochp = (OchPort) port;
+ portDescription = new OchPortDescription(ochp.number(), ochp.isEnabled(),
+ ochp.signalType(), ochp.isTunable(),
+ ochp.lambda());
+ break;
+ case ODU_PORT:
+ OduCltPort odup = (OduCltPort) port;
+ portDescription = new OduCltPortDescription(odup.number(), odup.isEnabled(),
+ odup.signalType());
+ break;
+ case OMS_PORT:
+ OmsPort op = (OmsPort) port;
+ portDescription = new OmsPortDescription(op.number(), op.isEnabled(), op.minFrequency(),
+ op.maxFrequency(), op.grid());
+ break;
+ default:
+ portDescription = new DefaultPortDescription(port.number(), port.isEnabled());
+ break;
+ }
+ return portDescription;
+ }
+
+ /**
+ * Build a link description from a pcep link.
+ *
+ * @param pceLink pcep link
+ * @return LinkDescription onos link description
+ */
+ private LinkDescription buildLinkDescription(PcepLink pceLink) {
+ LinkDescription ld;
+ checkNotNull(pceLink);
+ DeviceId srcDeviceID = deviceId(uri(pceLink.linkSrcDeviceID()));
+ DeviceId dstDeviceID = deviceId(uri(pceLink.linkDstDeviceId()));
+
+ deviceProviderService
+ .updatePorts(srcDeviceID,
+ buildPortDescriptions(pceLink.linkSrcDeviceID(),
+ pceLink.linkSrcPort(), pceLink.portType()));
+
+ deviceProviderService
+ .updatePorts(dstDeviceID,
+ buildPortDescriptions(pceLink.linkDstDeviceId(),
+ pceLink.linkDstPort(), pceLink.portType()));
+
+ ConnectPoint src = new ConnectPoint(srcDeviceID, pceLink.linkSrcPort().number());
+
+ ConnectPoint dst = new ConnectPoint(dstDeviceID, pceLink.linkDstPort().number());
+
+ DefaultAnnotations extendedAttributes = DefaultAnnotations
+ .builder()
+ .set("subType", String.valueOf(pceLink.linkSubType()))
+ .set("workState", pceLink.linkState())
+ .set("distance", String.valueOf(pceLink.linkDistance()))
+ .set("capType", pceLink.linkCapacityType().toLowerCase())
+ .set("avail_" + pceLink.linkCapacityType().toLowerCase(),
+ String.valueOf(pceLink.linkAvailValue()))
+ .set("max_" + pceLink.linkCapacityType().toLowerCase(),
+ String.valueOf(pceLink.linkMaxValue())).build();
+ // construct the link
+ ld = new DefaultLinkDescription(src, dst, Type.OPTICAL, extendedAttributes);
+ return ld;
+ }
+
+ private class InternalLinkProvider
+ implements PcepSwitchListener, PcepLinkListener {
+
+ @Override
+ public void switchAdded(PcepDpid dpid) {
+ if (deviceProviderService == null) {
+ return;
+ }
+ DeviceId deviceId = deviceId(uri(dpid));
+ PcepSwitch sw = controller.getSwitch(dpid);
+ checkNotNull(sw, "device should not null.");
+ // The default device type is switch.
+ ChassisId cId = new ChassisId(dpid.value());
+ Device.Type deviceType;
+
+ switch (sw.getDeviceType()) {
+ case ROADM:
+ deviceType = Device.Type.ROADM;
+ break;
+ case OTN:
+ deviceType = Device.Type.SWITCH;
+ break;
+ case ROUTER:
+ deviceType = Device.Type.ROUTER;
+ break;
+ default:
+ deviceType = Device.Type.OTHER;
+ }
+
+ DeviceDescription description = new DefaultDeviceDescription(
+ deviceId.uri(),
+ deviceType,
+ sw.manufacturerDescription(),
+ sw.hardwareDescription(),
+ sw.softwareDescription(),
+ sw.serialNumber(),
+ cId);
+ deviceProviderService.deviceConnected(deviceId, description);
+
+ }
+
+ @Override
+ public void switchRemoved(PcepDpid dpid) {
+ if (deviceProviderService == null || linkProviderService == null) {
+ return;
+ }
+ deviceProviderService.deviceDisconnected(deviceId(uri(dpid)));
+
+ linkProviderService.linksVanished(DeviceId.deviceId(uri(dpid)));
+ }
+
+ @Override
+ public void switchChanged(PcepDpid dpid) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void handlePCEPlink(PcepLink link) {
+
+ OperationType operType = link.getOperationType();
+ LinkDescription ld = buildLinkDescription(link);
+ if (ld == null) {
+ log.error("Invalid link info.");
+ return;
+ }
+ switch (operType) {
+ case ADD:
+ case UPDATE:
+ linkProviderService.linkDetected(ld);
+ break;
+
+ case DELETE:
+ linkProviderService.linkVanished(ld);
+ break;
+
+ default:
+ break;
+
+ }
+ }
+
+ }
+
+ @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/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/package-info.java b/framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/topology/impl/package-info.java
new file mode 100644
index 00000000..c119266d
--- /dev/null
+++ b/framework/src/onos/providers/pcep/topology/src/main/java/org/onosproject/provider/pcep/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 PCEP controller as a means of infrastructure topology discovery.
+ */
+package org.onosproject.provider.pcep.topology.impl; \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/tunnel/pom.xml b/framework/src/onos/providers/pcep/tunnel/pom.xml
new file mode 100644
index 00000000..09efb8ae
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/pom.xml
@@ -0,0 +1,39 @@
+<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-pcep-providers</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+ <artifactId>onos-pcep-provider-tunnel</artifactId>
+ <packaging>bundle</packaging>
+ <description>PCEP-based tunnel provider</description>
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-app-pcep-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-pcep-controller-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-net</artifactId>
+ <version>${project.version} </version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project> \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelApiMapper.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelApiMapper.java
new file mode 100644
index 00000000..b7aa3bda
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelApiMapper.java
@@ -0,0 +1,206 @@
+/*
+ * 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.pcep.tunnel.impl;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.collections.map.MultiKeyMap;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelProviderService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Entity to provide tunnel DB and mapping for request/response between CORE to PCEP
+ * and PCEP to PCC.
+ */
+public class PcepTunnelApiMapper {
+ protected static final Logger log = LoggerFactory.getLogger(PcepTunnelApiMapper.class);
+
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+ // Map to store all the tunnel requests.
+ private Map<Integer, PcepTunnelData> tunnelRequestQueue;
+ //Map to store all core related tunnel requests.
+ private Map<TunnelId, PcepTunnelData> coreTunnelRequestQueue;
+ //Map to store all the created tunnels.
+ private Map<Integer, PcepTunnelData> tunnelDB;
+ // Map to store the tunnel ids, given by core and given by pcc.
+ private Map<TunnelId, Integer> tunnelIdMap;
+ //Map to store all the learnt tunnels.
+ private MultiKeyMap pccTunnelDB = new MultiKeyMap();
+
+ TunnelProviderService tunnelApiMapperservice;
+
+ /**
+ * Default constructor.
+ */
+ public PcepTunnelApiMapper() {
+ //TODO check if the map need to initialize
+ tunnelRequestQueue = new HashMap<Integer, PcepTunnelData>();
+ coreTunnelRequestQueue = new HashMap<TunnelId, PcepTunnelData>();
+ tunnelDB = new HashMap<Integer, PcepTunnelData>();
+ tunnelIdMap = new HashMap<TunnelId, Integer>();
+ }
+
+ /**
+ * Add tunnels to tunnel Request queues.
+ *
+ * @param srpId srp id
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void addToTunnelRequestQueue(int srpId, PcepTunnelData pcepTunnelData) {
+ tunnelRequestQueue.put(new Integer(srpId), pcepTunnelData);
+ log.debug("Tunnel Added to TunnelRequestQueue");
+ }
+
+ /**
+ * Map between Tunnel ID and pcc provided Tunnel ID.
+ *
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void addToTunnelIdMap(PcepTunnelData pcepTunnelData) {
+ int value = pcepTunnelData.statefulIpv4IndentifierTlv().getTunnelId() & 0xFFFF;
+ tunnelIdMap.put(pcepTunnelData.tunnel().tunnelId(), (new Integer(value)));
+ log.debug("Tunnel ID Added to tunnelIdMap");
+ }
+
+ /**
+ * Add tunnels to core tunnel request queue.
+ *
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void addToCoreTunnelRequestQueue(PcepTunnelData pcepTunnelData) {
+ coreTunnelRequestQueue.put(pcepTunnelData.tunnel().tunnelId(), pcepTunnelData);
+ log.debug("Tunnel Added to CoreTunnelRequestQueue");
+ }
+
+ /**
+ * Removes tunnels from the core tunnel request queue.
+ *
+ * @param tunnelId tunnel id
+ */
+ public void removeFromCoreTunnelRequestQueue(TunnelId tunnelId) {
+ coreTunnelRequestQueue.remove(tunnelId);
+ log.debug("Tunnnel create response sent to core and removed from CoreTunnelRequestQueue");
+ }
+
+ /**
+ * Handle the report which comes after initiate message.
+ *
+ * @param srpId srp id
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void handleCreateTunnelRequestQueue(int srpId, PcepTunnelData pcepTunnelData) {
+
+ int value = tunnelIdMap.get(pcepTunnelData.tunnel().tunnelId());
+ tunnelDB.put(new Integer(value), pcepTunnelData);
+ tunnelRequestQueue.remove(new Integer(srpId), pcepTunnelData);
+ log.debug("Tunnel Added to TunnelDBQueue and removed from TunnelRequestQueue. tunnel id {}"
+ + (new Integer(value)).toString());
+ }
+
+ /**
+ * Handle report which comes for update message.
+ *
+ * @param srpId srp id
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void handleUpdateTunnelRequestQueue(int srpId, PcepTunnelData pcepTunnelData) {
+ if (pcepTunnelData.rptFlag()) {
+ pcepTunnelData.setRptFlag(false);
+ int value = tunnelIdMap.get(pcepTunnelData.tunnel().tunnelId());
+ tunnelDB.put(new Integer(value), pcepTunnelData);
+ tunnelRequestQueue.remove(new Integer(srpId), pcepTunnelData);
+ log.debug("Tunnel Added to TunnelDBQueue and removed from TunnelRequestQueue. tunnel id {}" ,
+ (new Integer(value)).toString());
+ } else {
+ pcepTunnelData.setRptFlag(true);
+ tunnelRequestQueue.put(new Integer(srpId), pcepTunnelData);
+ log.debug("Tunnel updated in TunnelRequestQueue");
+ }
+ }
+
+ /**
+ * Handle report for tunnel Release request.
+ *
+ * @param srpId srp id
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void handleRemoveFromTunnelRequestQueue(int srpId, PcepTunnelData pcepTunnelData) {
+
+ int value = tunnelIdMap.get(pcepTunnelData.tunnel().tunnelId());
+ tunnelIdMap.remove(pcepTunnelData.tunnel().tunnelId());
+ tunnelDB.remove(new Integer(value));
+ tunnelRequestQueue.remove(srpId);
+ log.debug("Tunnel removed from TunnelDBQueue and TunnelRequestQueue");
+ }
+
+ /**
+ * Returns PcepTunnelData from the tunnel request queue.
+ *
+ * @param srpId srp id
+ * @return PcepTunnelData pcep tunnel data
+ */
+ public PcepTunnelData getDataFromTunnelRequestQueue(int srpId) {
+ return tunnelRequestQueue.get(new Integer(srpId));
+
+ }
+
+ /**
+ * Returns PcepTunnelData from the tunnel DB.
+ *
+ * @param tunnelId tunnel id
+ * @return PcepTunnelData pcep tunnel data
+ */
+ public PcepTunnelData getDataFromTunnelDBQueue(TunnelId tunnelId) {
+ int value = tunnelIdMap.get(tunnelId);
+ return tunnelDB.get((new Integer(value)));
+ }
+
+ /**
+ * Checks whether the tunnel exist in tunnel request queue.
+ *
+ * @param srpId srp id
+ * @return true if tunnel exist in reuest queue, false otherwise
+ */
+ public boolean checkFromTunnelRequestQueue(int srpId) {
+ boolean retValue = tunnelRequestQueue.containsKey(srpId);
+ return retValue;
+ }
+
+ /**
+ * Returns whether tunnel exist in tunnel db.
+ *
+ * @param tunnelId tunnel id
+ * @return true/false if the tunnel exists in the tunnel db
+ */
+ public boolean checkFromTunnelDBQueue(TunnelId tunnelId) {
+ int value = tunnelIdMap.get(tunnelId);
+ boolean retValue = tunnelDB.containsKey((new Integer(value)));
+ return retValue;
+ }
+
+ /**
+ * Add Learnt tunnels to pcc tunnel DB.
+ *
+ * @param pcepTunnelData pcep tunnel data
+ */
+ public void addPccTunnelDB(PcepTunnelData pcepTunnelData) {
+ pccTunnelDB.put(pcepTunnelData.statefulIpv4IndentifierTlv().getTunnelId() & 0xFFFFL,
+ pcepTunnelData.statefulIpv4IndentifierTlv().getIpv4IngressAddress(), pcepTunnelData);
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelData.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelData.java
new file mode 100644
index 00000000..f796a2de
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelData.java
@@ -0,0 +1,386 @@
+/*
+ * 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.pcep.tunnel.impl;
+
+import java.util.Objects;
+
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.Path;
+import org.onosproject.pcepio.types.StatefulIPv4LspIdentidiersTlv;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * To store all tunnel related information from Core and Path computation client.
+ */
+public class PcepTunnelData {
+
+ private Tunnel tunnel;
+ private Path path;
+ private int plspId;
+ private ElementId elementId;
+ private RequestType requestType;
+ private boolean rptFlag;
+
+ // data need to store from LSP object
+ private boolean lspAFlag;
+ private boolean lspDFlag;
+ private byte lspOFlag;
+ private short tunnelId;
+ private int extTunnelId;
+ private short lspId;
+ private StatefulIPv4LspIdentidiersTlv statefulIpv4IndentifierTlv;
+
+ /**
+ * Default constructor.
+ */
+ public PcepTunnelData() {
+ this.elementId = null;
+ this.tunnel = null;
+ this.path = null;
+ this.requestType = null;
+ this.rptFlag = false;
+ this.plspId = 0;
+ }
+
+ /**
+ * Constructor to initialize Tunnel, Path and Request type.
+ *
+ * @param tunnel mpls tunnel
+ * @param path Path in network
+ * @param requestType request type for tunnel
+ */
+ public PcepTunnelData(Tunnel tunnel, Path path, RequestType requestType) {
+ this.tunnel = tunnel;
+ this.path = path;
+ this.requestType = requestType;
+ }
+
+ /**
+ * Constructor to initialize ElemendId, Tunnel, Path and Request type.
+ *
+ * @param elementId Ip element id
+ * @param tunnel mpls tunnel
+ * @param path Path in network
+ * @param requestType request type for tunnel
+ */
+ public PcepTunnelData(ElementId elementId, Tunnel tunnel, Path path, RequestType requestType) {
+ this.elementId = elementId;
+ this.tunnel = tunnel;
+ this.path = path;
+ this.requestType = requestType;
+ }
+
+ /**
+ * Constructor to initialize Tunnel and Request type.
+ *
+ * @param tunnel Tunnel from core
+ * @param requestType request type for tunnel
+ */
+ public PcepTunnelData(Tunnel tunnel, RequestType requestType) {
+ this.tunnel = tunnel;
+ this.requestType = requestType;
+ }
+
+ /**
+ * Constructor to initialize ElementId, Tunnel and Request type.
+ *
+ * @param elementId Ip element id
+ * @param tunnel mpls tunnel
+ * @param requestType request type for tunnel
+ */
+ public PcepTunnelData(ElementId elementId, Tunnel tunnel, RequestType requestType) {
+ this.elementId = elementId;
+ this.tunnel = tunnel;
+ this.requestType = requestType;
+ }
+
+ /**
+ * Sets ip element id.
+ *
+ * @param elementId Ip element id
+ */
+ public void setElementId(ElementId elementId) {
+ this.elementId = elementId;
+ }
+
+ /**
+ * Sets tunnel.
+ *
+ * @param tunnel mpls tunnel
+ */
+ public void setTunnel(Tunnel tunnel) {
+ this.tunnel = tunnel;
+ }
+
+ /**
+ * Sets Path.
+ *
+ * @param path Path in network
+ */
+ public void setPath(Path path) {
+ this.path = path;
+ }
+
+ /**
+ * Request type for tunnel.
+ *
+ * @param requestType request type for tunnel
+ */
+ public void setRequestType(RequestType requestType) {
+ this.requestType = requestType;
+ }
+
+ /**
+ * Sets plspid generated from pcc.
+ *
+ * @param plspId plsp identifier
+ */
+ public void setPlspId(int plspId) {
+ this.plspId = plspId;
+ }
+
+ /**
+ * Sets A flag from lsp object.
+ *
+ * @param value A flag value
+ */
+ public void setLspAFlag(boolean value) {
+ this.lspAFlag = value;
+ }
+
+ /**
+ * Sets OF flag from lsp object.
+ *
+ * @param value OF flag value
+ */
+ public void setLspOFlag(byte value) {
+ this.lspOFlag = value;
+ }
+
+ /**
+ * Sets tunnel id from PCC.
+ *
+ * @param value tunnel id value
+ */
+ public void setTunnelId(short value) {
+ this.tunnelId = value;
+ }
+
+ /**
+ * Sets extended tunnel id from PCC.
+ *
+ * @param value extended tunnel id value
+ */
+ public void setExtTunnelId(int value) {
+ this.extTunnelId = value;
+ }
+
+ /**
+ * Sets lsp id from pcc.
+ *
+ * @param value lsp id
+ */
+ public void setLspId(short value) {
+ this.lspId = value;
+ }
+
+ /**
+ * Sets statefulIpv4Identifiers tlv.
+ * @param value statefulIpv4Identifiers tlv
+ */
+ public void setStatefulIpv4IndentifierTlv(StatefulIPv4LspIdentidiersTlv value) {
+ this.statefulIpv4IndentifierTlv = value;
+ }
+
+ /**
+ * Sets report flag.
+ *
+ * @param rptFlag report flag
+ */
+ public void setRptFlag(boolean rptFlag) {
+ this.rptFlag = rptFlag;
+ }
+
+ /**
+ * Sets D flag from lsp object.
+ *
+ * @param value D flag value
+ */
+ public void setLspDFlag(boolean value) {
+ this.lspDFlag = value;
+ }
+
+ /**
+ * To get Ip element id.
+ *
+ * @return Ip elemend id
+ */
+ public ElementId elementId() {
+ return this.elementId;
+ }
+
+ /**
+ * To get Tunnel.
+ *
+ * @return tunnel
+ */
+ public Tunnel tunnel() {
+ return this.tunnel;
+ }
+
+ /**
+ * To get Path.
+ *
+ * @return path
+ */
+ public Path path() {
+ return this.path;
+ }
+
+ /**
+ * To get request type.
+ *
+ * @return request type
+ */
+ public RequestType requestType() {
+ return this.requestType;
+ }
+
+ /**
+ * To get pLspId.
+ *
+ * @return pLspId
+ */
+ public int plspId() {
+ return this.plspId;
+ }
+
+ /**
+ * To get A flag.
+ *
+ * @return A flag
+ */
+ public boolean lspAFlag() {
+ return this.lspAFlag;
+ }
+
+ /**
+ * To get OF flag.
+ *
+ * @return OF flag
+ */
+ public byte lspOFlag() {
+ return this.lspOFlag;
+ }
+
+ /**
+ * To get tunnel id.
+ *
+ * @return tunnel id
+ */
+ public short tunnelId() {
+ return this.tunnelId;
+ }
+
+ /**
+ * To get extended tunnel id.
+ *
+ * @return extended tunnel id
+ */
+ public int extTunnelId() {
+ return this.extTunnelId;
+ }
+
+ /**
+ * To get pLspId.
+ *
+ * @return pLspId
+ */
+ public short lspId() {
+ return this.lspId;
+ }
+
+ /**
+ * To get D Flag.
+ *
+ * @return d flag
+ */
+ public boolean lspDFlag() {
+ return this.lspDFlag;
+ }
+
+ /**
+ * To get statefulIpv4Indentifier tlv.
+ *
+ * @return statefulIpv4Indentifier tlv
+ */
+ public StatefulIPv4LspIdentidiersTlv statefulIpv4IndentifierTlv() {
+ return this.statefulIpv4IndentifierTlv;
+ }
+
+ /**
+ * To get report flag.
+ *
+ * @return report flag
+ */
+ public boolean rptFlag() {
+ return this.rptFlag;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj instanceof PcepTunnelData) {
+ PcepTunnelData other = (PcepTunnelData) obj;
+ return Objects.equals(tunnel, other.tunnel)
+ && Objects.equals(path, other.path)
+ && Objects.equals(plspId, other.plspId)
+ && Objects.equals(elementId, other.elementId)
+ && Objects.equals(requestType, other.requestType)
+ && Objects.equals(rptFlag, other.rptFlag)
+ && Objects.equals(lspAFlag, other.lspAFlag)
+ && Objects.equals(lspDFlag, other.lspDFlag)
+ && Objects.equals(lspOFlag, other.lspOFlag)
+ && Objects.equals(tunnelId, other.tunnelId)
+ && Objects.equals(extTunnelId, other.extTunnelId)
+ && Objects.equals(lspId, other.lspId)
+ && Objects.equals(statefulIpv4IndentifierTlv, other.statefulIpv4IndentifierTlv);
+ }
+
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tunnel, path, plspId, elementId, requestType, rptFlag, lspAFlag,
+ lspDFlag, lspOFlag, tunnelId, extTunnelId, lspId, statefulIpv4IndentifierTlv);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass()).add("Tunnel", tunnel)
+ .add("Path", path).add("PlspId", plspId).add("ElementId", elementId)
+ .add("RequestType", requestType).add("RptFlag", rptFlag).add("LspAFlag", lspAFlag)
+ .add("LspDFlag", lspDFlag).add("LspOFlag", lspOFlag).add("TunnelId", tunnelId)
+ .add("ExtTunnelid", extTunnelId).add("LspId", lspId)
+ .add("StatefulIpv4IndentifierTlv", statefulIpv4IndentifierTlv).toString();
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java
new file mode 100644
index 00000000..648e500b
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProvider.java
@@ -0,0 +1,1230 @@
+/*
+ * 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.pcep.tunnel.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+import static org.onlab.util.Tools.get;
+import static org.onosproject.net.DeviceId.deviceId;
+import static org.onosproject.net.PortNumber.portNumber;
+import static org.onosproject.pcep.api.PcepDpid.uri;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Optional;
+
+import com.google.common.collect.Maps;
+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.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultOpticalTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.DefaultTunnel;
+import org.onosproject.incubator.net.tunnel.DefaultTunnelDescription;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.DefaultTunnelStatistics;
+import org.onosproject.incubator.net.tunnel.OpticalLogicId;
+import org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelDescription;
+import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.incubator.net.tunnel.TunnelProvider;
+import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
+import org.onosproject.incubator.net.tunnel.TunnelProviderService;
+import org.onosproject.incubator.net.tunnel.TunnelService;
+import org.onosproject.incubator.net.tunnel.TunnelStatistics;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.ElementId;
+import org.onosproject.net.IpElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.SparseAnnotations;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.pcep.api.PcepController;
+import org.onosproject.pcep.api.PcepDpid;
+import org.onosproject.pcep.api.PcepHopNodeDescription;
+import org.onosproject.pcep.api.PcepOperator.OperationType;
+import org.onosproject.pcep.api.PcepTunnel;
+import org.onosproject.pcep.api.PcepTunnel.PATHTYPE;
+import org.onosproject.pcep.api.PcepTunnel.PathState;
+import org.onosproject.pcep.api.PcepTunnelListener;
+import org.onosproject.pcep.api.PcepTunnelStatistics;
+import org.osgi.service.component.annotations.Modified;
+import org.onosproject.pcep.controller.PccId;
+import org.onosproject.pcep.controller.PcepClient;
+import org.onosproject.pcep.controller.PcepClientController;
+import org.onosproject.pcep.controller.PcepClientListener;
+import org.onosproject.pcep.controller.PcepEventListener;
+import org.onosproject.pcepio.exceptions.PcepParseException;
+import org.onosproject.pcepio.protocol.PcInitiatedLspRequest;
+import org.onosproject.pcepio.protocol.PcepAttribute;
+import org.onosproject.pcepio.protocol.PcepBandwidthObject;
+import org.onosproject.pcepio.protocol.PcepEndPointsObject;
+import org.onosproject.pcepio.protocol.PcepEroObject;
+import org.onosproject.pcepio.protocol.PcepInitiateMsg;
+import org.onosproject.pcepio.protocol.PcepLspObject;
+import org.onosproject.pcepio.protocol.PcepMessage;
+import org.onosproject.pcepio.protocol.PcepMsgPath;
+import org.onosproject.pcepio.protocol.PcepReportMsg;
+import org.onosproject.pcepio.protocol.PcepRroObject;
+import org.onosproject.pcepio.protocol.PcepSrpObject;
+import org.onosproject.pcepio.protocol.PcepStateReport;
+import org.onosproject.pcepio.protocol.PcepUpdateMsg;
+import org.onosproject.pcepio.protocol.PcepUpdateRequest;
+import org.onosproject.pcepio.types.IPv4SubObject;
+import org.onosproject.pcepio.types.PcepValueType;
+import org.onosproject.pcepio.types.StatefulIPv4LspIdentidiersTlv;
+import org.onosproject.pcepio.types.SymbolicPathNameTlv;
+import org.slf4j.Logger;
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * Provider which uses an PCEP controller to detect, update, create network
+ * tunnels.
+ */
+@Component(immediate = true)
+@Service
+public class PcepTunnelProvider extends AbstractProvider implements TunnelProvider {
+
+ private static final Logger log = getLogger(PcepTunnelProvider.class);
+ private static final long MAX_BANDWIDTH = 99999744;
+ private static final long MIN_BANDWIDTH = 64;
+ private static final String BANDWIDTH_UINT = "kbps";
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+
+ static final int POLL_INTERVAL = 10;
+ @Property(name = "tunnelStatsPollFrequency", intValue = POLL_INTERVAL,
+ label = "Frequency (in seconds) for polling tunnel statistics")
+ private int tunnelStatsPollFrequency = POLL_INTERVAL;
+
+ private static final String TUNNLE_NOT_NULL = "Create failed,The given port may be wrong or has been occupied.";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelProviderRegistry tunnelProviderRegistry;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PcepController controller;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PcepClientController pcepClientController;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected TunnelService tunnelService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ComponentConfigService cfgService;
+
+ TunnelProviderService service;
+
+ HashMap<String, TunnelId> tunnelMap = new HashMap<String, TunnelId>();
+ HashMap<TunnelId, TunnelStatistics> tunnelStatisticsMap = new HashMap<>();
+ private HashMap<Long, TunnelStatsCollector> collectors = Maps.newHashMap();
+
+ private InnerTunnelProvider listener = new InnerTunnelProvider();
+
+ protected PcepTunnelApiMapper pcepTunnelAPIMapper = new PcepTunnelApiMapper();
+ private static final int DEFAULT_BANDWIDTH_VALUE = 10;
+
+ /**
+ * Creates a Tunnel provider.
+ */
+ public PcepTunnelProvider() {
+ super(new ProviderId("pcep", PROVIDER_ID));
+ }
+
+ @Activate
+ public void activate() {
+ cfgService.registerProperties(getClass());
+ service = tunnelProviderRegistry.register(this);
+ controller.addTunnelListener(listener);
+ pcepClientController.addListener(listener);
+ pcepClientController.addEventListener(listener);
+ tunnelService.queryAllTunnels().forEach(tunnel -> {
+ String pcepTunnelId = getPCEPTunnelKey(tunnel.tunnelId());
+ TunnelStatsCollector tsc = new TunnelStatsCollector(pcepTunnelId, tunnelStatsPollFrequency);
+ tsc.start();
+ collectors.put(tunnel.tunnelId().id(), tsc);
+
+ });
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ tunnelProviderRegistry.unregister(this);
+ controller.removeTunnelListener(listener);
+ collectors.values().forEach(TunnelStatsCollector::stop);
+ pcepClientController.removeListener(listener);
+ log.info("Stopped");
+ }
+
+ @Modified
+ public void modified(ComponentContext context) {
+ Dictionary<?, ?> properties = context.getProperties();
+ int newTunnelStatsPollFrequency;
+ try {
+ String s = get(properties, "tunnelStatsPollFrequency");
+ newTunnelStatsPollFrequency = isNullOrEmpty(s) ? tunnelStatsPollFrequency : Integer.parseInt(s.trim());
+
+ } catch (NumberFormatException | ClassCastException e) {
+ newTunnelStatsPollFrequency = tunnelStatsPollFrequency;
+ }
+
+ if (newTunnelStatsPollFrequency != tunnelStatsPollFrequency) {
+ tunnelStatsPollFrequency = newTunnelStatsPollFrequency;
+ collectors.values().forEach(tsc -> tsc.adjustPollInterval(tunnelStatsPollFrequency));
+ log.info("New setting: tunnelStatsPollFrequency={}", tunnelStatsPollFrequency);
+ }
+
+ }
+
+ @Override
+ public void setupTunnel(Tunnel tunnel, Path path) {
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ // Get the pcc client
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpTunnelEndPoint) tunnel.src()).ip()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpTunnelEndPoint) tunnel.src()).ip().toString());
+ return;
+ }
+ pcepSetupTunnel(tunnel, path, pc);
+ }
+
+ @Override
+ public void setupTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
+
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ if (!(srcElement instanceof IpElementId)) {
+ log.error("Element id is not valid");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpElementId) srcElement).ipAddress()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpElementId) srcElement).ipAddress().toString());
+ return;
+ }
+ pcepSetupTunnel(tunnel, path, pc);
+ }
+
+ @Override
+ public void releaseTunnel(Tunnel tunnel) {
+
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpTunnelEndPoint) tunnel.src()).ip()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpTunnelEndPoint) tunnel.src()).ip().toString());
+ return;
+ }
+ pcepReleaseTunnel(tunnel, pc);
+ }
+
+ @Override
+ public void releaseTunnel(ElementId srcElement, Tunnel tunnel) {
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ if (!(srcElement instanceof IpElementId)) {
+ log.error("Element id is not valid");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpElementId) srcElement).ipAddress()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpElementId) srcElement).ipAddress().toString());
+ return;
+ }
+ pcepReleaseTunnel(tunnel, pc);
+ }
+
+ @Override
+ public void updateTunnel(Tunnel tunnel, Path path) {
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpTunnelEndPoint) tunnel.src()).ip()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpTunnelEndPoint) tunnel.src()).ip().toString());
+ return;
+ }
+ pcepUpdateTunnel(tunnel, path, pc);
+ }
+
+ @Override
+ public void updateTunnel(ElementId srcElement, Tunnel tunnel, Path path) {
+
+ if (tunnel.type() != Tunnel.Type.MPLS) {
+ log.error("Tunnel Type MPLS is only supported");
+ return;
+ }
+
+ if (!(srcElement instanceof IpElementId)) {
+ log.error("Element id is not valid");
+ return;
+ }
+
+ // check for tunnel end points
+ if (!(tunnel.src() instanceof IpTunnelEndPoint) || !(tunnel.dst() instanceof IpTunnelEndPoint)) {
+ log.error("Tunnel source or destination is not valid");
+ return;
+ }
+
+ PcepClient pc = pcepClientController.getClient(PccId.pccId(((IpElementId) srcElement).ipAddress()));
+
+ if (!(pc instanceof PcepClient)) {
+ log.error("There is no PCC connected with ip addresss {}"
+ + ((IpElementId) srcElement).ipAddress().toString());
+ return;
+ }
+ pcepUpdateTunnel(tunnel, path, pc);
+ }
+
+ @Override
+ public TunnelId tunnelAdded(TunnelDescription tunnel) {
+ if (tunnel.type() == Tunnel.Type.MPLS) {
+ pcepTunnelAPIMapper.removeFromCoreTunnelRequestQueue(tunnel.id());
+ return service.tunnelAdded(tunnel);
+ }
+
+ long bandwidth = Long
+ .parseLong(tunnel.annotations().value("bandwidth"));
+
+ if (bandwidth < MIN_BANDWIDTH || bandwidth > MAX_BANDWIDTH) {
+ error("Update failed, invalid bandwidth.");
+ return null;
+ }
+
+ // endpoints
+ OpticalTunnelEndPoint src = (org.onosproject.incubator.net.tunnel.OpticalTunnelEndPoint) tunnel
+ .src();
+ OpticalTunnelEndPoint dst = (OpticalTunnelEndPoint) tunnel.dst();
+ // devices
+ DeviceId srcId = (DeviceId) src.elementId().get();
+ DeviceId dstId = (DeviceId) dst.elementId().get();
+
+ // ports
+ long srcPort = src.portNumber().get().toLong();
+ long dstPort = dst.portNumber().get().toLong();
+
+ // type
+ if (tunnel.type() != Tunnel.Type.VLAN) {
+ error("Illegal tunnel type. Only support VLAN tunnel creation.");
+ return null;
+ }
+
+ PcepTunnel pcepTunnel = controller.applyTunnel(srcId, dstId, srcPort,
+ dstPort, bandwidth,
+ tunnel.tunnelName()
+ .value());
+
+ checkNotNull(pcepTunnel, TUNNLE_NOT_NULL);
+ TunnelDescription tunnelAdded = buildOpticalTunnel(pcepTunnel, null);
+ TunnelId tunnelId = service.tunnelAdded(tunnelAdded);
+
+ tunnelMap.put(String.valueOf(pcepTunnel.id()), tunnelId);
+ return tunnelId;
+ }
+
+ @Override
+ public void tunnelRemoved(TunnelDescription tunnel) {
+ if (tunnel.type() == Tunnel.Type.MPLS) {
+ pcepTunnelAPIMapper.removeFromCoreTunnelRequestQueue(tunnel.id());
+ service.tunnelRemoved(tunnel);
+ }
+
+ Tunnel tunnelOld = tunnelQueryById(tunnel.id());
+ checkNotNull(tunnelOld, "The tunnel id is not exsited.");
+ if (tunnelOld.type() != Tunnel.Type.VLAN) {
+ error("Illegal tunnel type. Only support VLAN tunnel deletion.");
+ return;
+ }
+ String pcepTunnelId = getPCEPTunnelKey(tunnel.id());
+ checkNotNull(pcepTunnelId, "The tunnel id is not exsited.");
+ if (!controller.deleteTunnel(pcepTunnelId)) {
+ error("Delete tunnel failed, Maybe some devices have been disconnected.");
+ return;
+ }
+ tunnelMap.remove(pcepTunnelId);
+ service.tunnelRemoved(tunnel);
+ }
+
+ @Override
+ public void tunnelUpdated(TunnelDescription tunnel) {
+ if (tunnel.type() == Tunnel.Type.MPLS) {
+ pcepTunnelAPIMapper.removeFromCoreTunnelRequestQueue(tunnel.id());
+ service.tunnelUpdated(tunnel);
+ }
+
+ Tunnel tunnelOld = tunnelQueryById(tunnel.id());
+ if (tunnelOld.type() != Tunnel.Type.VLAN) {
+ error("Illegal tunnel type. Only support VLAN tunnel update.");
+ return;
+ }
+ long bandwidth = Long
+ .parseLong(tunnel.annotations().value("bandwidth"));
+ if (bandwidth < MIN_BANDWIDTH || bandwidth > MAX_BANDWIDTH) {
+ error("Update failed, invalid bandwidth.");
+ return;
+ }
+ String pcepTunnelId = getPCEPTunnelKey(tunnel.id());
+
+ checkNotNull(pcepTunnelId, "Invalid tunnel id");
+ if (!controller.updateTunnelBandwidth(pcepTunnelId, bandwidth)) {
+
+ error("Update failed,maybe invalid bandwidth.");
+ return;
+
+ }
+ service.tunnelUpdated(tunnel);
+ }
+
+ private void error(String info) {
+ System.err.println(info);
+ }
+
+ // Short-hand for creating a connection point.
+ private ConnectPoint connectPoint(PcepDpid id, long port) {
+ return new ConnectPoint(deviceId(uri(id)), portNumber(port));
+ }
+
+ // Short-hand for creating a link.
+ private Link link(PcepDpid src, long sp, PcepDpid dst, long dp) {
+ return new DefaultLink(id(), connectPoint(src, sp), connectPoint(dst,
+ dp),
+ Link.Type.TUNNEL);
+ }
+
+ // Creates a path that leads through the given devices.
+ private Path createPath(List<PcepHopNodeDescription> hopList,
+ PATHTYPE pathtype, PathState pathState) {
+ if (hopList == null || hopList.size() == 0) {
+ return null;
+ }
+ List<Link> links = new ArrayList<>();
+ for (int i = 1; i < hopList.size() - 1; i = i + 2) {
+ links.add(link(hopList.get(i).getDeviceId(), hopList.get(i)
+ .getPortNum(), hopList.get(i + 1).getDeviceId(), hopList
+ .get(i + 1).getPortNum()));
+ }
+
+ int hopNum = hopList.size() - 2;
+ DefaultAnnotations extendAnnotations = DefaultAnnotations.builder()
+ .set("pathNum", String.valueOf(hopNum))
+ .set("pathState", String.valueOf(pathState))
+ .set("pathType", String.valueOf(pathtype)).build();
+ return new DefaultPath(id(), links, hopNum, extendAnnotations);
+ }
+
+ // convert the path description to a string.
+ public String pathToString(List<Link> links) {
+ StringBuilder builder = new StringBuilder();
+ builder.append("{");
+ for (Link link : links) {
+ builder.append("(Device:" + link.src().deviceId() + " Port:"
+ + link.src().port().toLong());
+ builder.append(" Device:" + link.dst().deviceId() + " Port:"
+ + link.dst().port().toLong());
+ builder.append(")");
+ }
+ builder.append("}");
+ return builder.toString();
+ }
+
+ // build a TunnelDescription.
+ private TunnelDescription buildOpticalTunnel(PcepTunnel pcepTunnel,
+ TunnelId tunnelId) {
+ TunnelEndPoint srcPoint = null;
+ TunnelEndPoint dstPoint = null;
+ Tunnel.Type tunnelType = null;
+ TunnelName name = TunnelName.tunnelName(pcepTunnel.name());
+
+ // add path after codes of tunnel's path merged
+ Path path = createPath(pcepTunnel.getHopList(),
+ pcepTunnel.getPathType(),
+ pcepTunnel.getPathState());
+
+ OpticalTunnelEndPoint.Type endPointType = null;
+ switch (pcepTunnel.type()) {
+ case OCH:
+ tunnelType = Tunnel.Type.OCH;
+ endPointType = OpticalTunnelEndPoint.Type.LAMBDA;
+ break;
+
+ case OTN:
+ tunnelType = Tunnel.Type.ODUK;
+ endPointType = OpticalTunnelEndPoint.Type.TIMESLOT;
+ break;
+
+ case UNI:
+ tunnelType = Tunnel.Type.VLAN;
+ endPointType = null;
+ break;
+
+ default:
+ break;
+ }
+ DeviceId srcDid = deviceId(uri(pcepTunnel.srcDeviceID()));
+ DeviceId dstDid = deviceId(uri(pcepTunnel.dstDeviceId()));
+ PortNumber srcPort = PortNumber.portNumber(pcepTunnel.srcPort());
+ PortNumber dstPort = PortNumber.portNumber(pcepTunnel.dstPort());
+
+ srcPoint = new DefaultOpticalTunnelEndPoint(id(), Optional.of(srcDid),
+ Optional.of(srcPort), null,
+ endPointType,
+ OpticalLogicId.logicId(0),
+ true);
+ dstPoint = new DefaultOpticalTunnelEndPoint(id(), Optional.of(dstDid),
+ Optional.of(dstPort), null,
+ endPointType,
+ OpticalLogicId.logicId(0),
+ true);
+
+ // basic annotations
+ DefaultAnnotations annotations = DefaultAnnotations
+ .builder()
+ .set("SLA", String.valueOf(pcepTunnel.getSla()))
+ .set("bandwidth",
+ String.valueOf(pcepTunnel.bandWidth()) + BANDWIDTH_UINT)
+ .set("index", String.valueOf(pcepTunnel.id())).build();
+
+ // a VLAN tunnel always carry OCH tunnel, this annotation is the index
+ // of a OCH tunnel.
+ if (pcepTunnel.underlayTunnelId() != 0) {
+ DefaultAnnotations extendAnnotations = DefaultAnnotations
+ .builder()
+ .set("underLayTunnelIndex",
+ String.valueOf(pcepTunnel.underlayTunnelId())).build();
+ annotations = DefaultAnnotations.merge(annotations,
+ extendAnnotations);
+
+ }
+ TunnelDescription tunnel = new DefaultTunnelDescription(
+ tunnelId,
+ srcPoint,
+ dstPoint,
+ tunnelType,
+ new DefaultGroupId(
+ 0),
+ id(), name,
+ path,
+ annotations);
+ return tunnel;
+
+ }
+
+ /**
+ * Get the tunnelID according to the tunnel key.
+ *
+ * @param tunnelKey tunnel key
+ * @return corresponding tunnel id of the a tunnel key.
+ */
+ private TunnelId getTunnelId(String tunnelKey) {
+ for (String key : tunnelMap.keySet()) {
+ if (key.equals(tunnelKey)) {
+ return tunnelMap.get(key);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the tunnel key according to the tunnelID.
+ *
+ * @param tunnelId tunnel id
+ * @return corresponding a tunnel key of the tunnel id.
+ */
+ private String getPCEPTunnelKey(TunnelId tunnelId) {
+ for (String key : tunnelMap.keySet()) {
+ if (tunnelMap.get(key).id() == tunnelId.id()) {
+ return key;
+ }
+ }
+ return null;
+
+ }
+
+ /**
+ * Build a DefaultTunnelStatistics from a PcepTunnelStatistics.
+ *
+ * @param statistics statistics data from a PCEP tunnel
+ * @return TunnelStatistics
+ */
+ private TunnelStatistics buildTunnelStatistics(PcepTunnelStatistics statistics) {
+ DefaultTunnelStatistics.Builder builder = new DefaultTunnelStatistics.Builder();
+ DefaultTunnelStatistics tunnelStatistics = builder.setBwUtilization(statistics.bandwidthUtilization())
+ .setPacketLossRatio(statistics.packetLossRate())
+ .setFlowDelay(statistics.flowDelay())
+ .setAlarms(statistics.alarms())
+ .build();
+ return tunnelStatistics;
+ }
+ /**
+ * Creates list of hops for ERO object from Path.
+ *
+ * @param path network path
+ * @return list of ipv4 subobjects
+ */
+ private LinkedList<PcepValueType> createPcepPath(Path path) {
+ LinkedList<PcepValueType> llSubObjects = new LinkedList<PcepValueType>();
+ List<Link> listLink = path.links();
+ ConnectPoint source = null;
+ ConnectPoint destination = null;
+ IpAddress ipDstAddress = null;
+ IpAddress ipSrcAddress = null;
+ PcepValueType subObj = null;
+
+ for (Link link : listLink) {
+ source = link.src();
+ if (!(source.equals(destination))) {
+ //set IPv4SubObject for ERO object
+ ipSrcAddress = source.ipElementId().ipAddress();
+ subObj = new IPv4SubObject(ipSrcAddress.getIp4Address().toInt());
+ llSubObjects.add(subObj);
+ }
+
+ destination = link.dst();
+ ipDstAddress = destination.ipElementId().ipAddress();
+ subObj = new IPv4SubObject(ipDstAddress.getIp4Address().toInt());
+ llSubObjects.add(subObj);
+ }
+ return llSubObjects;
+ }
+
+ /**
+ * Creates PcInitiated lsp request list for setup tunnel.
+ *
+ * @param tunnel mpls tunnel
+ * @param path network path
+ * @param pc pcep client
+ * @param srpId unique id for pcep message
+ * @return list of PcInitiatedLspRequest
+ * @throws PcepParseException while building pcep objects fails
+ */
+ LinkedList<PcInitiatedLspRequest> createPcInitiatedLspReqList(Tunnel tunnel, Path path,
+ PcepClient pc, int srpId)
+ throws PcepParseException {
+ PcepValueType tlv;
+ LinkedList<PcepValueType> llSubObjects = createPcepPath(path);
+
+ if (llSubObjects == null || llSubObjects.size() == 0) {
+ log.error("There is no link information to create tunnel");
+ return null;
+ }
+
+ //build SRP object
+ PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(false).build();
+
+ LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
+ LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<PcInitiatedLspRequest>();
+ // set LSP identifiers TLV
+ tlv = new StatefulIPv4LspIdentidiersTlv((((IpTunnelEndPoint) tunnel.src()).ip().getIp4Address().toInt()),
+ (short) 0, (short) 0, 0,
+ (((IpTunnelEndPoint) tunnel.dst()).ip().getIp4Address().toInt()));
+ llOptionalTlv.add(tlv);
+ //set SymbolicPathNameTlv of LSP object
+ tlv = new SymbolicPathNameTlv(tunnel.tunnelName().value().getBytes());
+ llOptionalTlv.add(tlv);
+
+ //build LSP object
+ PcepLspObject lspobj = pc.factory().buildLspObject().setAFlag(true).setOFlag((byte) 0).setPlspId(0)
+ .setOptionalTlv(llOptionalTlv).build();
+
+ //build ENDPOINTS object
+ PcepEndPointsObject endpointsobj = pc.factory().buildEndPointsObject()
+ .setSourceIpAddress(((IpTunnelEndPoint) tunnel.src()).ip().getIp4Address().toInt())
+ .setDestIpAddress(((IpTunnelEndPoint) tunnel.dst()).ip().getIp4Address().toInt())
+ .setPFlag(true).build();
+
+ //build ERO object
+ PcepEroObject eroobj = pc.factory().buildEroObject().setSubObjects(llSubObjects).build();
+
+ int iBandwidth = DEFAULT_BANDWIDTH_VALUE;
+ if (tunnel.annotations().value("bandwidth") != null) {
+ iBandwidth = Integer.parseInt(tunnel.annotations().value("bandwidth"));
+ }
+ // build bandwidth object
+ PcepBandwidthObject bandwidthObject = pc.factory().buildBandwidthObject().setBandwidth(iBandwidth).build();
+ // build pcep attribute
+ PcepAttribute pcepAttribute = pc.factory().buildPcepAttribute().setBandwidthObject(bandwidthObject).build();
+
+ PcInitiatedLspRequest initiateLspRequest = pc.factory().buildPcInitiatedLspRequest().setSrpObject(srpobj)
+ .setLspObject(lspobj).setEndPointsObject(endpointsobj).setEroObject(eroobj)
+ .setPcepAttribute(pcepAttribute).build();
+ llPcInitiatedLspRequestList.add(initiateLspRequest);
+ return llPcInitiatedLspRequestList;
+ }
+
+ /**
+ * To send initiate tunnel message to pcc.
+ *
+ * @param tunnel mpls tunnel info
+ * @param path explicit route for the tunnel
+ * @param pc pcep client to send message
+ */
+ private void pcepSetupTunnel(Tunnel tunnel, Path path, PcepClient pc) {
+ try {
+ int srpId = SrpIdGenerators.create();
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, path, RequestType.CREATE);
+
+ pcepTunnelAPIMapper.addToCoreTunnelRequestQueue(pcepTunnelData);
+
+ LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = createPcInitiatedLspReqList(tunnel, path,
+ pc, srpId);
+ if (llPcInitiatedLspRequestList == null || llPcInitiatedLspRequestList.size() == 0) {
+ log.error("Failed to create PcInitiatedLspRequestList");
+ return;
+ }
+
+ //build PCInitiate message
+ PcepInitiateMsg pcInitiateMsg = pc.factory().buildPcepInitiateMsg()
+ .setPcInitiatedLspRequestList(llPcInitiatedLspRequestList)
+ .build();
+
+ pc.sendMessage(Collections.singletonList(pcInitiateMsg));
+
+ pcepTunnelAPIMapper.addToTunnelRequestQueue(srpId, pcepTunnelData);
+ } catch (PcepParseException e) {
+ log.error("PcepParseException occurred while processing setup tunnel {}", e.getMessage());
+ }
+ }
+
+ /**
+ * To send Release tunnel message to pcc.
+ *
+ * @param tunnel mpls tunnel info
+ * @param pc pcep client to send message
+ */
+ private void pcepReleaseTunnel(Tunnel tunnel, PcepClient pc) {
+ try {
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, RequestType.DELETE);
+ pcepTunnelAPIMapper.addToCoreTunnelRequestQueue(pcepTunnelData);
+ int srpId = SrpIdGenerators.create();
+ TunnelId tunnelId = tunnel.tunnelId();
+ int plspId = 0;
+ StatefulIPv4LspIdentidiersTlv statefulIpv4IndentifierTlv = null;
+
+ if (!(pcepTunnelAPIMapper.checkFromTunnelDBQueue(tunnelId))) {
+ log.error("Tunnel doesnot exists. Tunnel id {}" + tunnelId.toString());
+ return;
+ } else {
+ PcepTunnelData pcepTunnelDbData = pcepTunnelAPIMapper.getDataFromTunnelDBQueue(tunnelId);
+ plspId = pcepTunnelDbData.plspId();
+ statefulIpv4IndentifierTlv = pcepTunnelDbData.statefulIpv4IndentifierTlv();
+ }
+ // build srp object
+ PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(true).build();
+
+ PcepValueType tlv;
+ LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
+ LinkedList<PcInitiatedLspRequest> llPcInitiatedLspRequestList = new LinkedList<PcInitiatedLspRequest>();
+
+ if (statefulIpv4IndentifierTlv != null) {
+ tlv = statefulIpv4IndentifierTlv;
+ } else {
+ tlv = new StatefulIPv4LspIdentidiersTlv((
+ ((IpTunnelEndPoint) tunnel.src()).ip().getIp4Address().toInt()),
+ (short) 0, (short) 0, 0,
+ (((IpTunnelEndPoint) tunnel.dst()).ip().getIp4Address().toInt()));
+ }
+ llOptionalTlv.add(tlv);
+ tlv = new SymbolicPathNameTlv(tunnel.tunnelName().value().getBytes());
+ llOptionalTlv.add(tlv);
+ // build lsp object, set r flag as false to delete the tunnel
+ PcepLspObject lspobj = pc.factory().buildLspObject().setRFlag(false).setPlspId(plspId)
+ .setOptionalTlv(llOptionalTlv).build();
+
+ PcInitiatedLspRequest releaseLspRequest = pc.factory().buildPcInitiatedLspRequest().setSrpObject(srpobj)
+ .setLspObject(lspobj).build();
+
+ llPcInitiatedLspRequestList.add(releaseLspRequest);
+
+ PcepInitiateMsg pcInitiateMsg = pc.factory().buildPcepInitiateMsg()
+ .setPcInitiatedLspRequestList(llPcInitiatedLspRequestList).build();
+
+ pc.sendMessage(Collections.singletonList(pcInitiateMsg));
+
+ pcepTunnelAPIMapper.addToTunnelRequestQueue(srpId, pcepTunnelData);
+ } catch (PcepParseException e) {
+ log.error("PcepParseException occurred while processing release tunnel {}", e.getMessage());
+ }
+ }
+
+ /**
+ * To send Update tunnel request message to pcc.
+ *
+ * @param tunnel mpls tunnel info
+ * @param path explicit route for the tunnel
+ * @param pc pcep client to send message
+ */
+ private void pcepUpdateTunnel(Tunnel tunnel, Path path, PcepClient pc) {
+ try {
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, path, RequestType.UPDATE);
+ pcepTunnelAPIMapper.addToCoreTunnelRequestQueue(pcepTunnelData);
+ int srpId = SrpIdGenerators.create();
+ TunnelId tunnelId = tunnel.tunnelId();
+ PcepValueType tlv;
+ int plspId = 0;
+
+ LinkedList<PcepValueType> llSubObjects = createPcepPath(path);
+ LinkedList<PcepValueType> llOptionalTlv = new LinkedList<PcepValueType>();
+ LinkedList<PcepUpdateRequest> llUpdateRequestList = new LinkedList<PcepUpdateRequest>();
+
+ //build SRP object
+ PcepSrpObject srpobj = pc.factory().buildSrpObject().setSrpID(srpId).setRFlag(false).build();
+
+ if (!(pcepTunnelAPIMapper.checkFromTunnelDBQueue(tunnelId))) {
+ log.error("Tunnel doesnot exists in DB");
+ return;
+ } else {
+ PcepTunnelData pcepTunnelDBData = pcepTunnelAPIMapper.getDataFromTunnelDBQueue(tunnelId);
+ plspId = pcepTunnelDBData.plspId();
+ }
+
+ tlv = new StatefulIPv4LspIdentidiersTlv((((IpTunnelEndPoint) tunnel.src()).ip().getIp4Address().toInt()),
+ (short) 0, (short) 0, 0,
+ (((IpTunnelEndPoint) tunnel.dst()).ip().getIp4Address().toInt()));
+ llOptionalTlv.add(tlv);
+
+ if (tunnel.tunnelName().value() != null) {
+ tlv = new SymbolicPathNameTlv(tunnel.tunnelName().value().getBytes());
+ llOptionalTlv.add(tlv);
+ }
+
+ // build lsp object
+ PcepLspObject lspobj = pc.factory().buildLspObject().setAFlag(true).setPlspId(plspId)
+ .setOptionalTlv(llOptionalTlv).build();
+ // build ero object
+ PcepEroObject eroobj = pc.factory().buildEroObject().setSubObjects(llSubObjects).build();
+
+ int iBandwidth = DEFAULT_BANDWIDTH_VALUE;
+ if (tunnel.annotations().value("bandwidth") != null) {
+ iBandwidth = Integer.parseInt(tunnel.annotations().value("bandwidth"));
+ }
+ // build bandwidth object
+ PcepBandwidthObject bandwidthObject = pc.factory().buildBandwidthObject().setBandwidth(iBandwidth).build();
+ // build pcep attribute
+ PcepAttribute pcepAttribute = pc.factory().buildPcepAttribute().setBandwidthObject(bandwidthObject).build();
+ // build pcep msg path
+ PcepMsgPath msgPath = pc.factory().buildPcepMsgPath().setEroObject(eroobj).setPcepAttribute(pcepAttribute)
+ .build();
+
+ PcepUpdateRequest updateRequest = pc.factory().buildPcepUpdateRequest().setSrpObject(srpobj)
+ .setLspObject(lspobj).setMsgPath(msgPath).build();
+
+ llUpdateRequestList.add(updateRequest);
+
+ PcepUpdateMsg pcUpdateMsg = pc.factory().buildUpdateMsg().setUpdateRequestList(llUpdateRequestList).build();
+
+ pc.sendMessage(Collections.singletonList(pcUpdateMsg));
+ pcepTunnelAPIMapper.addToTunnelRequestQueue(srpId, pcepTunnelData);
+ } catch (PcepParseException e) {
+ log.error("PcepParseException occurred while processing release tunnel {}", e.getMessage());
+ }
+ }
+
+
+
+ private class InnerTunnelProvider implements PcepTunnelListener, PcepEventListener, PcepClientListener {
+
+ @Override
+ public void handlePCEPTunnel(PcepTunnel pcepTunnel) {
+ TunnelDescription tunnel = null;
+ // instance and id identify a tunnel together
+ String tunnelKey = String.valueOf(pcepTunnel.getInstance())
+ + String.valueOf(pcepTunnel.id());
+
+ if (tunnelKey == null || "".equals(tunnelKey)) {
+ log.error("Invalid PCEP tunnel");
+ return;
+ }
+
+ TunnelId tunnelId = getTunnelId(tunnelKey);
+
+ tunnel = buildOpticalTunnel(pcepTunnel, tunnelId);
+
+ OperationType operType = pcepTunnel.getOperationType();
+ switch (operType) {
+ case ADD:
+ tunnelId = service.tunnelAdded(tunnel);
+ tunnelMap.put(tunnelKey, tunnelId);
+ break;
+
+ case UPDATE:
+ service.tunnelUpdated(tunnel);
+ break;
+
+ case DELETE:
+ service.tunnelRemoved(tunnel);
+ tunnelMap.remove(tunnelKey);
+ break;
+
+ default:
+ log.error("Invalid tunnel operation");
+ }
+ }
+
+ @Override
+ public void handleMessage(PccId pccId, PcepMessage msg) {
+ try {
+ log.debug("tunnel provider handle message {}", msg.getType().toString());
+ switch (msg.getType()) {
+ case REPORT:
+ int srpId = 0;
+ LinkedList<PcepStateReport> llStateReportList = null;
+ llStateReportList = ((PcepReportMsg) msg).getStateReportList();
+ ListIterator<PcepStateReport> listIterator = llStateReportList.listIterator();
+ PcepSrpObject srpObj = null;
+ PcepLspObject lspObj = null;
+ while (listIterator.hasNext()) {
+ PcepStateReport stateRpt = listIterator.next();
+ srpObj = stateRpt.getSrpObject();
+ lspObj = stateRpt.getLspObject();
+
+ if (srpObj instanceof PcepSrpObject) {
+ srpId = srpObj.getSrpID();
+ }
+
+ log.debug("Plsp ID in handle message " + lspObj.getPlspId());
+ log.debug("SRP ID in handle message " + srpId);
+
+ if (!(pcepTunnelAPIMapper.checkFromTunnelRequestQueue(srpId))) {
+
+ // Check the sync status
+ if (lspObj.getSFlag()) {
+ handleSyncReport(stateRpt);
+ } else if (!pcepClientController.getClient(pccId).isSyncComplete()) {
+ // sync is done
+ pcepClientController.getClient(pccId).setIsSyncComplete(true);
+ }
+ continue;
+ }
+
+ handleReportMessage(srpId, lspObj);
+ }
+ break;
+
+ default:
+ log.debug("Received unsupported message type {}", msg.getType().toString());
+ }
+ } catch (Exception e) {
+ log.error("Exception occured while processing report message {}", e.getMessage());
+ }
+ }
+
+ /**
+ * Handles report message for setup/update/delete tunnel request.
+ *
+ * @param srpId unique identifier for pcep message
+ * @param lspObj lsp object
+ */
+ private void handleReportMessage(int srpId, PcepLspObject lspObj) {
+ ProviderId providerId = new ProviderId("pcep", PROVIDER_ID);
+ PcepTunnelData pcepTunnelData = pcepTunnelAPIMapper.getDataFromTunnelRequestQueue(srpId);
+ SparseAnnotations annotations = (SparseAnnotations) pcepTunnelData.tunnel().annotations();
+
+ // store the values required from report message
+ pcepTunnelData.setPlspId(lspObj.getPlspId());
+ pcepTunnelData.setLspAFlag(lspObj.getAFlag());
+ pcepTunnelData.setLspOFlag(lspObj.getOFlag());
+ pcepTunnelData.setLspDFlag(lspObj.getDFlag());
+
+ StatefulIPv4LspIdentidiersTlv ipv4LspTlv = null;
+ ListIterator<PcepValueType> listTlvIterator = lspObj.getOptionalTlv().listIterator();
+ while (listTlvIterator.hasNext()) {
+ PcepValueType tlv = listTlvIterator.next();
+ if (tlv.getType() == StatefulIPv4LspIdentidiersTlv.TYPE) {
+ ipv4LspTlv = (StatefulIPv4LspIdentidiersTlv) tlv;
+ break;
+ }
+ }
+ if (ipv4LspTlv != null) {
+ pcepTunnelData.setStatefulIpv4IndentifierTlv(ipv4LspTlv);
+ }
+
+ Path path = pcepTunnelData.path();
+ Tunnel tunnel = pcepTunnelData.tunnel();
+ DefaultTunnelDescription td = new DefaultTunnelDescription(tunnel.tunnelId(), tunnel.src(),
+ tunnel.dst(), tunnel.type(), tunnel.groupId(),
+ providerId, tunnel.tunnelName(), path,
+ annotations);
+
+ if (RequestType.CREATE == pcepTunnelData.requestType()) {
+ log.debug("Report received for create request");
+
+ pcepTunnelAPIMapper.handleCreateTunnelRequestQueue(srpId, pcepTunnelData);
+ if (0 == lspObj.getOFlag()) {
+ log.warn("The tunnel is in down state");
+ }
+ tunnelAdded(td);
+ }
+ if (RequestType.DELETE == pcepTunnelData.requestType()) {
+ log.debug("Report received for delete request");
+ pcepTunnelAPIMapper.handleRemoveFromTunnelRequestQueue(srpId, pcepTunnelData);
+ tunnelRemoved(td);
+ }
+
+ if (RequestType.UPDATE == pcepTunnelData.requestType()) {
+ log.debug("Report received for update request");
+ pcepTunnelData.setRptFlag(true);
+ pcepTunnelAPIMapper.addToTunnelIdMap(pcepTunnelData);
+ pcepTunnelAPIMapper.handleUpdateTunnelRequestQueue(srpId, pcepTunnelData);
+
+ if (0 == lspObj.getOFlag()) {
+ log.warn("The tunnel is in down state");
+ }
+ if (!(pcepTunnelAPIMapper.checkFromTunnelRequestQueue(srpId))) {
+ tunnelUpdated(td);
+ }
+ }
+ }
+
+ /**
+ * Handles sync report received from pcc.
+ *
+ * @param stateRpt pcep state report
+ */
+ private void handleSyncReport(PcepStateReport stateRpt) {
+ PcepLspObject lspObj = stateRpt.getLspObject();
+ PcepStateReport.PcepMsgPath msgPath = stateRpt.getMsgPath();
+ checkNotNull(msgPath);
+ PcepRroObject rroObj = msgPath.getRroObject();
+ if (rroObj == null) {
+ log.debug("RRO object is null in sate report");
+ return;
+ }
+ int bandwidth = 0;
+
+ log.debug("Handle Sync report received from PCC.");
+
+ if (0 == lspObj.getOFlag()) {
+ log.warn("The PCC reported tunnel is in down state");
+ }
+ log.debug("Sync report received");
+
+ if (msgPath.getBandwidthObject() != null) {
+ bandwidth = msgPath.getBandwidthObject().getBandwidth();
+ }
+
+ buildAndStorePcepTunnelData(lspObj, rroObj, bandwidth);
+ }
+
+ /**
+ * To build Path in network from RRO object.
+ *
+ * @param rroObj rro object
+ * @param providerId provider id
+ * @return path object
+ */
+ private Path buildPathFromRroObj(PcepRroObject rroObj, ProviderId providerId) {
+ checkNotNull(rroObj);
+ List<Link> links = new ArrayList<Link>();
+ LinkedList<PcepValueType> llSubObj = rroObj.getSubObjects();
+ if (0 == llSubObj.size()) {
+ log.error("RRO in report message does not have hop information");
+ }
+ ListIterator<PcepValueType> tlvIterator = llSubObj.listIterator();
+
+ ConnectPoint src = null;
+ ConnectPoint dst = null;
+ boolean isSrcSet = false;
+ while (tlvIterator.hasNext()) {
+ PcepValueType subObj = tlvIterator.next();
+ switch (subObj.getType()) {
+
+ case IPv4SubObject.TYPE:
+
+ IPv4SubObject ipv4SubObj = (IPv4SubObject) subObj;
+ if (!isSrcSet) {
+ IpAddress srcIp = IpAddress.valueOf(ipv4SubObj.getIpAddress());
+ src = new ConnectPoint(IpElementId.ipElement(srcIp), PortNumber.portNumber(0));
+ isSrcSet = true;
+ } else {
+ IpAddress dstIp = IpAddress.valueOf(ipv4SubObj.getIpAddress());
+ dst = new ConnectPoint(IpElementId.ipElement(dstIp), PortNumber.portNumber(0));
+ Link link = new DefaultLink(providerId, src, dst, Link.Type.DIRECT, EMPTY);
+ links.add(link);
+ src = dst;
+ }
+ break;
+ default:
+ // the other sub objects are not required
+ }
+ }
+ return new DefaultPath(providerId, links, 0, EMPTY);
+ }
+
+ /**
+ * To build pcepTunnelData and informs core about the pcc reported tunnel.
+ *
+ * @param lspObj pcep lsp object
+ * @param rroObj pcep rro object
+ * @param bandwidth bandwidth of tunnel
+ */
+ private void buildAndStorePcepTunnelData(PcepLspObject lspObj, PcepRroObject rroObj,
+ int bandwidth) {
+
+ ProviderId providerId = new ProviderId("pcep", PROVIDER_ID);
+
+ // StatefulIPv4LspIdentidiersTlv in LSP object will have the source and destination address.
+ StatefulIPv4LspIdentidiersTlv lspIdenTlv = null;
+ SymbolicPathNameTlv pathNameTlv = null;
+ LinkedList<PcepValueType> llOptionalTlv = lspObj.getOptionalTlv();
+ ListIterator<PcepValueType> listIterator = llOptionalTlv.listIterator();
+ while (listIterator.hasNext()) {
+ PcepValueType tlv = listIterator.next();
+ switch (tlv.getType()) {
+ case StatefulIPv4LspIdentidiersTlv.TYPE:
+ lspIdenTlv = (StatefulIPv4LspIdentidiersTlv) tlv;
+ break;
+ case SymbolicPathNameTlv.TYPE:
+ pathNameTlv = (SymbolicPathNameTlv) tlv;
+ break;
+ default:
+ // currently this tlv is not required
+ }
+ }
+
+ IpTunnelEndPoint tunnelEndPointSrc;
+ tunnelEndPointSrc = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(lspIdenTlv.getIpv4IngressAddress()));
+ IpTunnelEndPoint tunnelEndPointDst;
+ tunnelEndPointDst = IpTunnelEndPoint.ipTunnelPoint(IpAddress.valueOf(lspIdenTlv.getIpv4EgressAddress()));
+
+ Path path = buildPathFromRroObj(rroObj, providerId);
+
+ SparseAnnotations annotations = DefaultAnnotations.builder()
+ .set("bandwidth", (new Integer(bandwidth)).toString())
+ .build();
+
+ DefaultTunnelDescription td = new DefaultTunnelDescription(null, tunnelEndPointSrc,
+ tunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), providerId,
+ TunnelName.tunnelName(pathNameTlv.toString()),
+ path, annotations);
+ TunnelId tId = tunnelAdded(td);
+
+ Tunnel tunnel = new DefaultTunnel(providerId, tunnelEndPointSrc, tunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), tId,
+ TunnelName.tunnelName(pathNameTlv.toString()), path, annotations);
+
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, path, RequestType.LSP_STATE_RPT);
+ pcepTunnelData.setStatefulIpv4IndentifierTlv(lspIdenTlv);
+ pcepTunnelAPIMapper.addPccTunnelDB(pcepTunnelData);
+ pcepTunnelAPIMapper.addToTunnelIdMap(pcepTunnelData);
+ }
+
+ @Override
+ public void clientConnected(PccId pccId) {
+ // TODO
+ }
+
+ @Override
+ public void clientDisconnected(PccId pccId) {
+ // TODO
+ }
+
+
+
+ @Override
+ public void handlePcepTunnelStatistics(PcepTunnelStatistics pcepTunnelStatistics) {
+ TunnelId id = getTunnelId(String.valueOf(pcepTunnelStatistics.id()));
+ TunnelStatistics tunnelStatistics = buildTunnelStatistics(pcepTunnelStatistics);
+ tunnelStatisticsMap.put(id, tunnelStatistics);
+ }
+ }
+
+ @Override
+ public Tunnel tunnelQueryById(TunnelId tunnelId) {
+ return service.tunnelQueryById(tunnelId);
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/RequestType.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/RequestType.java
new file mode 100644
index 00000000..51854451
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/RequestType.java
@@ -0,0 +1,41 @@
+/*
+ * 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.pcep.tunnel.impl;
+
+/**
+ * Enum of request types between pcc and pcep.
+ */
+public enum RequestType {
+ /**
+ * Specifies the request type for PCC is to create new tunnel.
+ */
+ CREATE,
+
+ /**
+ * Specifies the request type for PCC is to update existing tunnel.
+ */
+ UPDATE,
+
+ /**
+ * Specifies the request type for PCC is to delete existing tunnel.
+ */
+ DELETE,
+
+ /**
+ * Specifies the request type for PCC to report existing tunnel.
+ */
+ LSP_STATE_RPT;
+} \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/SrpIdGenerators.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/SrpIdGenerators.java
new file mode 100644
index 00000000..5b5a5fb2
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/SrpIdGenerators.java
@@ -0,0 +1,56 @@
+/*
+ * 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.pcep.tunnel.impl;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.slf4j.Logger;
+
+/**
+ * Unique Srp Id generator for pcep messages.
+ */
+public final class SrpIdGenerators {
+
+ private static final Logger log = getLogger(SrpIdGenerators.class);
+ private static final AtomicInteger SRP_ID_GEN = new AtomicInteger();
+ private static final int MAX_SRP_ID = 0x7FFFFFFF;
+ private static int srpId;
+
+ /**
+ * Default constructor.
+ */
+ private SrpIdGenerators() {
+ }
+
+ /**
+ * Get the next srp id.
+ *
+ * @return srp id
+ */
+ public static int create() {
+ do {
+ if (srpId >= MAX_SRP_ID) {
+ if (SRP_ID_GEN.get() >= MAX_SRP_ID) {
+ SRP_ID_GEN.set(0);
+ }
+ }
+ srpId = SRP_ID_GEN.incrementAndGet();
+ } while (srpId > MAX_SRP_ID);
+ return srpId;
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/TunnelStatsCollector.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/TunnelStatsCollector.java
new file mode 100644
index 00000000..39249c5c
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/TunnelStatsCollector.java
@@ -0,0 +1,104 @@
+/*
+ *
+ * * 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.pcep.tunnel.impl;
+
+
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.jboss.netty.util.HashedWheelTimer;
+import org.jboss.netty.util.Timeout;
+import org.jboss.netty.util.TimerTask;
+import org.onlab.util.Timer;
+import org.onosproject.pcep.api.PcepController;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/*
+ * Sends Stats Request and collect the tunnel statistics with a time interval.
+ */
+public class TunnelStatsCollector implements TimerTask {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PcepController controller;
+
+ private int refreshInterval;
+ private final HashedWheelTimer timer = Timer.getTimer();
+
+ private String pcepTunnelId;
+ private Timeout timeout;
+ private volatile boolean stopped;
+
+
+ /**
+ * Greate a tunnel status collector object.
+ *
+ * @param id tunnel whose status data will be collected
+ * @param refreshInterval time interval for collecting statistic
+ */
+ public TunnelStatsCollector(String id, int refreshInterval) {
+ this.pcepTunnelId = id;
+ this.refreshInterval = refreshInterval;
+ }
+
+ @Override
+ public void run(Timeout timeout) throws Exception {
+ if (stopped || timeout.isCancelled()) {
+ return;
+ }
+ log.trace("Collecting stats for {}", pcepTunnelId);
+
+ sendTunnelStatistic();
+ if (!stopped && !timeout.isCancelled()) {
+ log.trace("Scheduling stats collection in {} seconds for {}",
+ this.refreshInterval, pcepTunnelId);
+ timeout.getTimer().newTimeout(this, refreshInterval, TimeUnit.SECONDS);
+ }
+
+ }
+
+ private void sendTunnelStatistic() {
+ controller.getTunnelStatistics(pcepTunnelId);
+
+ }
+
+ synchronized void adjustPollInterval(int pollInterval) {
+ this.refreshInterval = pollInterval;
+ }
+
+ /**
+ * Starts the collector.
+ */
+ public synchronized void start() {
+ log.info("Starting Tunnel Stats collection thread for {}", pcepTunnelId);
+ stopped = false;
+ timeout = timer.newTimeout(this, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Stops the collector.
+ */
+ public synchronized void stop() {
+ log.info("Stopping Tunnel Stats collection thread for {}", pcepTunnelId);
+ stopped = true;
+ timeout.cancel();
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/package-info.java b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/impl/package-info.java
new file mode 100644
index 00000000..5074eecb
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/main/java/org/onosproject/provider/pcep/tunnel/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 PCEP controller as a means of infrastructure tunnel discovery.
+ */
+package org.onosproject.provider.pcep.tunnel.impl; \ No newline at end of file
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientAdapter.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientAdapter.java
new file mode 100644
index 00000000..49e29514
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientAdapter.java
@@ -0,0 +1,92 @@
+package org.onosproject.provider.pcep.tunnel.impl;
+
+import static org.junit.Assert.assertNotNull;
+
+import java.util.List;
+import java.util.concurrent.RejectedExecutionException;
+
+import org.jboss.netty.channel.Channel;
+import org.onosproject.pcep.controller.PccId;
+import org.onosproject.pcep.controller.PcepClient;
+import org.onosproject.pcepio.protocol.PcepFactories;
+import org.onosproject.pcepio.protocol.PcepFactory;
+import org.onosproject.pcepio.protocol.PcepMessage;
+import org.onosproject.pcepio.protocol.PcepVersion;
+
+public class PcepClientAdapter implements PcepClient {
+
+ private Channel channel;
+ protected String channelId;
+
+ private boolean connected;
+ private PccId pccId;
+
+ private PcepVersion pcepVersion;
+
+ public void init(PccId pccId, PcepVersion pcepVersion) {
+ this.pccId = pccId;
+ this.pcepVersion = pcepVersion;
+ }
+
+ @Override
+ public final void disconnectClient() {
+ this.channel.close();
+ }
+
+ @Override
+ public final void sendMessage(PcepMessage m) {
+ }
+
+ @Override
+ public final void sendMessage(List<PcepMessage> msgs) {
+ try {
+ PcepMessage pcepMsg = msgs.get(0);
+ assertNotNull("PCEP MSG should be created.", pcepMsg);
+ } catch (RejectedExecutionException e) {
+ throw e;
+ }
+ }
+
+ @Override
+ public final boolean isConnected() {
+ return this.connected;
+ }
+
+ @Override
+ public String channelId() {
+ return channelId;
+ }
+
+ @Override
+ public final PccId getPccId() {
+ return this.pccId;
+ };
+
+ @Override
+ public final String getStringId() {
+ return this.pccId.toString();
+ }
+
+ @Override
+ public final void handleMessage(PcepMessage m) {
+ }
+
+ @Override
+ public boolean isOptical() {
+ return false;
+ }
+
+ @Override
+ public PcepFactory factory() {
+ return PcepFactories.getFactory(pcepVersion);
+ }
+
+ @Override
+ public final boolean isSyncComplete() {
+ return false;
+ }
+
+ @Override
+ public final void setIsSyncComplete(boolean value) {
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientControllerAdapter.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientControllerAdapter.java
new file mode 100644
index 00000000..74aa590c
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepClientControllerAdapter.java
@@ -0,0 +1,189 @@
+package org.onosproject.provider.pcep.tunnel.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.onlab.packet.IpAddress;
+import org.onosproject.pcep.controller.PccId;
+import org.onosproject.pcep.controller.PcepClient;
+import org.onosproject.pcep.controller.PcepClientController;
+import org.onosproject.pcep.controller.PcepClientListener;
+import org.onosproject.pcep.controller.PcepEventListener;
+import org.onosproject.pcep.controller.driver.PcepAgent;
+import org.onosproject.pcepio.protocol.PcepMessage;
+import org.onosproject.pcepio.protocol.PcepVersion;
+
+import com.google.common.collect.Sets;
+
+public class PcepClientControllerAdapter implements PcepClientController {
+
+ protected ConcurrentHashMap<PccId, PcepClient> connectedClients =
+ new ConcurrentHashMap<PccId, PcepClient>();
+
+ protected PcepClientAgent agent = new PcepClientAgent();
+ protected Set<PcepClientListener> pcepClientListener = new HashSet<>();
+
+ protected Set<PcepEventListener> pcepEventListener = Sets.newHashSet();
+
+ @Activate
+ public void activate() {
+ }
+
+ @Deactivate
+ public void deactivate() {
+ }
+
+ @Override
+ public Collection<PcepClient> getClients() {
+ return connectedClients.values();
+ }
+
+ @Override
+ public PcepClient getClient(PccId pccId) {
+ //return connectedClients.get(pccIpAddress);
+ PcepClientAdapter pc = new PcepClientAdapter();
+ pc.init(PccId.pccId(IpAddress.valueOf(0xac000001)), PcepVersion.PCEP_1);
+ return pc;
+ }
+
+ @Override
+ public void addListener(PcepClientListener listener) {
+ if (!pcepClientListener.contains(listener)) {
+ this.pcepClientListener.add(listener);
+ }
+ }
+
+ @Override
+ public void removeListener(PcepClientListener listener) {
+ this.pcepClientListener.remove(listener);
+ }
+
+ @Override
+ public void addEventListener(PcepEventListener listener) {
+ pcepEventListener.add(listener);
+ }
+
+ @Override
+ public void removeEventListener(PcepEventListener listener) {
+ pcepEventListener.remove(listener);
+ }
+
+ @Override
+ public void writeMessage(PccId pccId, PcepMessage msg) {
+ this.getClient(pccId).sendMessage(msg);
+ }
+
+ @Override
+ public void processClientMessage(PccId pccId, PcepMessage msg) {
+
+ PcepClient pc = getClient(pccId);
+
+ switch (msg.getType()) {
+ case NONE:
+ break;
+ case OPEN:
+ break;
+ case KEEP_ALIVE:
+ //log.debug("Sending Keep Alive Message to {" + pccIpAddress.toString() + "}");
+ pc.sendMessage(Collections.singletonList(pc.factory().buildKeepaliveMsg().build()));
+ break;
+ case PATH_COMPUTATION_REQUEST:
+ break;
+ case PATH_COMPUTATION_REPLY:
+ break;
+ case NOTIFICATION:
+ break;
+ case ERROR:
+ break;
+ case CLOSE:
+ //log.debug("Sending Close Message to { }", pccIpAddress.toString());
+ pc.sendMessage(Collections.singletonList(pc.factory().buildCloseMsg().build()));
+ break;
+ case REPORT:
+ for (PcepEventListener l : pcepEventListener) {
+ l.handleMessage(pccId, msg);
+ }
+ break;
+ case UPDATE:
+ for (PcepEventListener l : pcepEventListener) {
+ l.handleMessage(pccId, msg);
+ }
+ break;
+ case INITIATE:
+ for (PcepEventListener l : pcepEventListener) {
+ l.handleMessage(pccId, msg);
+ }
+ break;
+ case LABEL_UPDATE:
+ break;
+ case MAX:
+ break;
+ case END:
+ break;
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void closeConnectedClients() {
+ PcepClient pc;
+ for (PccId id : connectedClients.keySet()) {
+ pc = getClient(id);
+ pc.disconnectClient();
+ }
+ }
+
+ /**
+ * Implementation of an Pcep Agent which is responsible for
+ * keeping track of connected clients and the state in which
+ * they are.
+ */
+ public class PcepClientAgent implements PcepAgent {
+
+ @Override
+ public boolean addConnectedClient(PccId pccId, PcepClient pc) {
+
+ if (connectedClients.get(pccId) != null) {
+ return false;
+ } else {
+ connectedClients.put(pccId, pc);
+ for (PcepClientListener l : pcepClientListener) {
+ l.clientConnected(pccId);
+ }
+ return true;
+ }
+ }
+
+ @Override
+ public boolean validActivation(PccId pccId) {
+ if (connectedClients.get(pccId) == null) {
+ //log.error("Trying to activate client but is not in "
+ // + "connected switches: pccIp {}. Aborting ..", pccIpAddress.toString());
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public void removeConnectedClient(PccId pccId) {
+ connectedClients.remove(pccId);
+ for (PcepClientListener l : pcepClientListener) {
+ //log.warn("removal for {}", pccIpAddress.toString());
+ l.clientDisconnected(pccId);
+ }
+ }
+
+ @Override
+ public void processPcepMessage(PccId pccId, PcepMessage m) {
+ processClientMessage(pccId, m);
+ }
+ }
+
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepControllerAdapter.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepControllerAdapter.java
new file mode 100644
index 00000000..65266116
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepControllerAdapter.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014 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.pcep.tunnel.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.pcep.api.PcepController;
+import org.onosproject.pcep.api.PcepDpid;
+import org.onosproject.pcep.api.PcepLinkListener;
+import org.onosproject.pcep.api.PcepSwitch;
+import org.onosproject.pcep.api.PcepSwitchListener;
+import org.onosproject.pcep.api.PcepTunnel;
+import org.onosproject.pcep.api.PcepTunnelListener;
+
+public class PcepControllerAdapter implements PcepController {
+
+ @Override
+ public Iterable<PcepSwitch> getSwitches() {
+ return null;
+ }
+
+ @Override
+ public PcepSwitch getSwitch(PcepDpid did) {
+ return null;
+ }
+
+ @Override
+ public void addListener(PcepSwitchListener listener) {
+
+ }
+
+ @Override
+ public void removeListener(PcepSwitchListener listener) {
+ }
+
+ @Override
+ public void addLinkListener(PcepLinkListener listener) {
+ }
+
+ @Override
+ public void removeLinkListener(PcepLinkListener listener) {
+ }
+
+ @Override
+ public void addTunnelListener(PcepTunnelListener listener) {
+ }
+
+ @Override
+ public void removeTunnelListener(PcepTunnelListener listener) {
+ }
+
+ @Override
+ public PcepTunnel applyTunnel(DeviceId srcDid, DeviceId dstDid, long srcPort, long dstPort, long bandwidth,
+ String name) {
+ return null;
+ }
+
+ @Override
+ public Boolean deleteTunnel(String id) {
+ return null;
+ }
+
+ @Override
+ public Boolean updateTunnelBandwidth(String id, long bandwidth) {
+ return null;
+ }
+
+ @Override
+ public void getTunnelStatistics(String pcepTunnelId) {
+
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepReleaseTunnelProviderTest.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepReleaseTunnelProviderTest.java
new file mode 100644
index 00000000..e3861381
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepReleaseTunnelProviderTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014 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.pcep.tunnel.impl;
+
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultTunnel;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.IpElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.pcepio.types.StatefulIPv4LspIdentidiersTlv;
+
+
+public class PcepReleaseTunnelProviderTest {
+
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+ PcepTunnelProvider tunnelProvider = new PcepTunnelProvider();
+ private final TunnelProviderRegistryAdapter registry = new TunnelProviderRegistryAdapter();
+ private final PcepClientControllerAdapter controller = new PcepClientControllerAdapter();
+ private final PcepControllerAdapter ctl = new PcepControllerAdapter();
+ private final PcepTunnelApiMapper pcepTunnelAPIMapper = new PcepTunnelApiMapper();
+ private final TunnelServiceAdapter tunnelService = new TunnelServiceAdapter();
+
+ @Test
+ public void testCasePcepReleaseTunnel() {
+ tunnelProvider.tunnelProviderRegistry = registry;
+ tunnelProvider.pcepClientController = controller;
+ tunnelProvider.controller = ctl;
+ tunnelProvider.tunnelService = tunnelService;
+ tunnelProvider.pcepTunnelAPIMapper = pcepTunnelAPIMapper;
+ tunnelProvider.cfgService = new ComponentConfigAdapter();
+ tunnelProvider.activate();
+
+ Tunnel tunnel;
+ Path path;
+ List<Link> links = new ArrayList<Link>();
+
+ ProviderId pid = new ProviderId("pcep", PROVIDER_ID);
+
+ IpAddress srcIp = IpAddress.valueOf(0xB6024E20);
+ IpElementId srcElementId = IpElementId.ipElement(srcIp);
+
+ IpAddress dstIp = IpAddress.valueOf(0xB6024E21);
+ IpElementId dstElementId = IpElementId.ipElement(dstIp);
+
+ IpTunnelEndPoint ipTunnelEndPointSrc;
+ ipTunnelEndPointSrc = IpTunnelEndPoint.ipTunnelPoint(srcIp);
+
+ IpTunnelEndPoint ipTunnelEndPointDst;
+ ipTunnelEndPointDst = IpTunnelEndPoint.ipTunnelPoint(dstIp);
+
+ ConnectPoint src = new ConnectPoint(srcElementId, PortNumber.portNumber(10023));
+
+ ConnectPoint dst = new ConnectPoint(dstElementId, PortNumber.portNumber(10023));
+
+ Link link = new DefaultLink(pid, src, dst, Link.Type.DIRECT, EMPTY);
+ links.add(link);
+
+ path = new DefaultPath(pid, links, 20, EMPTY);
+
+ tunnel = new DefaultTunnel(pid, ipTunnelEndPointSrc, ipTunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), TunnelId.valueOf(1), TunnelName.tunnelName("T123"),
+ path, EMPTY);
+
+ // for releasing tunnel tunnel should exist in db
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, path, RequestType.DELETE);
+ pcepTunnelData.setPlspId(1);
+ StatefulIPv4LspIdentidiersTlv tlv = new StatefulIPv4LspIdentidiersTlv(0, (short) 1, (short) 2, 3, 4);
+ pcepTunnelData.setStatefulIpv4IndentifierTlv(tlv);
+ tunnelProvider.pcepTunnelAPIMapper.addToTunnelIdMap(pcepTunnelData);
+
+ tunnelProvider.pcepTunnelAPIMapper.handleCreateTunnelRequestQueue(1, pcepTunnelData);
+
+ tunnelProvider.releaseTunnel(tunnel);
+ }
+
+
+ @After
+ public void tearDown() throws IOException {
+ tunnelProvider.deactivate();
+ tunnelProvider.controller = null;
+ tunnelProvider.pcepClientController = null;
+ tunnelProvider.tunnelProviderRegistry = null;
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepSetupTunnelProviderTest.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepSetupTunnelProviderTest.java
new file mode 100644
index 00000000..ef4816ec
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepSetupTunnelProviderTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014 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.pcep.tunnel.impl;
+
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultTunnel;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.IpElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+
+public class PcepSetupTunnelProviderTest {
+
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+ PcepTunnelProvider tunnelProvider = new PcepTunnelProvider();
+ private final TunnelProviderRegistryAdapter registry = new TunnelProviderRegistryAdapter();
+ private final PcepClientControllerAdapter controller = new PcepClientControllerAdapter();
+ private final PcepControllerAdapter ctl = new PcepControllerAdapter();
+ private final TunnelServiceAdapter tunnelService = new TunnelServiceAdapter();
+
+ @Test
+ public void testCasePcepSetupTunnel() {
+
+ tunnelProvider.tunnelProviderRegistry = registry;
+ tunnelProvider.pcepClientController = controller;
+ tunnelProvider.controller = ctl;
+ tunnelProvider.cfgService = new ComponentConfigAdapter();
+ tunnelProvider.tunnelService = tunnelService;
+ tunnelProvider.activate();
+
+
+ Tunnel tunnel;
+ Path path;
+ ProviderId pid = new ProviderId("pcep", PROVIDER_ID);
+ List<Link> links = new ArrayList<Link>();
+ IpAddress srcIp = IpAddress.valueOf(0xC010101);
+ IpElementId srcElementId = IpElementId.ipElement(srcIp);
+
+ IpAddress dstIp = IpAddress.valueOf(0xC010102);
+ IpElementId dstElementId = IpElementId.ipElement(dstIp);
+
+ IpTunnelEndPoint ipTunnelEndPointSrc;
+ ipTunnelEndPointSrc = IpTunnelEndPoint.ipTunnelPoint(srcIp);
+
+ IpTunnelEndPoint ipTunnelEndPointDst;
+ ipTunnelEndPointDst = IpTunnelEndPoint.ipTunnelPoint(dstIp);
+
+ ConnectPoint src = new ConnectPoint(srcElementId, PortNumber.portNumber(10023));
+
+ ConnectPoint dst = new ConnectPoint(dstElementId, PortNumber.portNumber(10023));
+
+ Link link = new DefaultLink(pid, src, dst, Link.Type.DIRECT, EMPTY);
+ links.add(link);
+
+ path = new DefaultPath(pid, links, 10, EMPTY);
+
+ tunnel = new DefaultTunnel(pid, ipTunnelEndPointSrc, ipTunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), TunnelId.valueOf(1), TunnelName.tunnelName("T123"),
+ path, EMPTY);
+
+ tunnelProvider.setupTunnel(tunnel, path);
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ tunnelProvider.deactivate();
+ tunnelProvider.controller = null;
+ tunnelProvider.pcepClientController = null;
+ tunnelProvider.tunnelProviderRegistry = null;
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProviderTest.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProviderTest.java
new file mode 100644
index 00000000..0fcd1447
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepTunnelProviderTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2014 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.pcep.tunnel.impl;
+
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultTunnel;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.IpElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+
+public class PcepTunnelProviderTest {
+
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+ PcepTunnelProvider tunnelProvider = new PcepTunnelProvider();
+ private final TunnelProviderRegistryAdapter registry = new TunnelProviderRegistryAdapter();
+ private final PcepClientControllerAdapter controller = new PcepClientControllerAdapter();
+ private final PcepControllerAdapter ctl = new PcepControllerAdapter();
+ private final TunnelServiceAdapter tunnelService = new TunnelServiceAdapter();
+
+ @Test
+ public void testCasePcepSetupTunnel() {
+
+ tunnelProvider.tunnelProviderRegistry = registry;
+ tunnelProvider.pcepClientController = controller;
+ tunnelProvider.controller = ctl;
+ tunnelProvider.cfgService = new ComponentConfigAdapter();
+ tunnelProvider.tunnelService = tunnelService;
+ tunnelProvider.activate();
+
+ Tunnel tunnel;
+ Path path;
+ ProviderId pid = new ProviderId("pcep", PROVIDER_ID);
+ List<Link> links = new ArrayList<Link>();
+ IpAddress srcIp = IpAddress.valueOf(0xC010101);
+ IpElementId srcElementId = IpElementId.ipElement(srcIp);
+
+ IpAddress dstIp = IpAddress.valueOf(0xC010102);
+ IpElementId dstElementId = IpElementId.ipElement(dstIp);
+
+ IpTunnelEndPoint ipTunnelEndPointSrc;
+ ipTunnelEndPointSrc = IpTunnelEndPoint.ipTunnelPoint(srcIp);
+
+ IpTunnelEndPoint ipTunnelEndPointDst;
+ ipTunnelEndPointDst = IpTunnelEndPoint.ipTunnelPoint(dstIp);
+
+ ConnectPoint src = new ConnectPoint(srcElementId, PortNumber.portNumber(10023));
+
+ ConnectPoint dst = new ConnectPoint(dstElementId, PortNumber.portNumber(10023));
+
+ Link link = new DefaultLink(pid, src, dst, Link.Type.DIRECT, EMPTY);
+ links.add(link);
+
+ path = new DefaultPath(pid, links, 10, EMPTY);
+
+ tunnel = new DefaultTunnel(pid, ipTunnelEndPointSrc, ipTunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), TunnelId.valueOf(1), TunnelName.tunnelName("T123"),
+ path, EMPTY);
+
+ tunnelProvider.setupTunnel(tunnel, path);
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ tunnelProvider.deactivate();
+ tunnelProvider.controller = null;
+ tunnelProvider.pcepClientController = null;
+ tunnelProvider.tunnelProviderRegistry = null;
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepUpdateTunnelProviderTest.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepUpdateTunnelProviderTest.java
new file mode 100644
index 00000000..1bcf99dd
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/PcepUpdateTunnelProviderTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014 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.pcep.tunnel.impl;
+
+import static org.onosproject.net.DefaultAnnotations.EMPTY;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.After;
+import org.junit.Test;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.DefaultGroupId;
+import org.onosproject.incubator.net.tunnel.DefaultTunnel;
+import org.onosproject.incubator.net.tunnel.IpTunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultLink;
+import org.onosproject.net.DefaultPath;
+import org.onosproject.net.IpElementId;
+import org.onosproject.net.Link;
+import org.onosproject.net.Path;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.pcepio.types.StatefulIPv4LspIdentidiersTlv;
+
+
+public class PcepUpdateTunnelProviderTest {
+
+ static final String PROVIDER_ID = "org.onosproject.provider.tunnel.pcep";
+ PcepTunnelProvider tunnelProvider = new PcepTunnelProvider();
+ private final TunnelProviderRegistryAdapter registry = new TunnelProviderRegistryAdapter();
+ private final PcepClientControllerAdapter controller = new PcepClientControllerAdapter();
+ private final PcepControllerAdapter ctl = new PcepControllerAdapter();
+ private final PcepTunnelApiMapper pcepTunnelAPIMapper = new PcepTunnelApiMapper();
+ private final TunnelServiceAdapter tunnelService = new TunnelServiceAdapter();
+
+
+ @Test
+ public void testCasePcepUpdateTunnel() {
+ tunnelProvider.tunnelProviderRegistry = registry;
+ tunnelProvider.pcepClientController = controller;
+ tunnelProvider.controller = ctl;
+ tunnelProvider.pcepTunnelAPIMapper = pcepTunnelAPIMapper;
+ tunnelProvider.cfgService = new ComponentConfigAdapter();
+ tunnelProvider.tunnelService = tunnelService;
+ tunnelProvider.activate();
+
+ Tunnel tunnel;
+ Path path;
+ ProviderId pid = new ProviderId("pcep", PROVIDER_ID);
+ List<Link> links = new ArrayList<Link>();
+ IpAddress srcIp = IpAddress.valueOf(0xD010101);
+ IpElementId srcElementId = IpElementId.ipElement(srcIp);
+
+ IpAddress dstIp = IpAddress.valueOf(0xD010102);
+ IpElementId dstElementId = IpElementId.ipElement(dstIp);
+
+ IpTunnelEndPoint ipTunnelEndPointSrc;
+ ipTunnelEndPointSrc = IpTunnelEndPoint.ipTunnelPoint(srcIp);
+
+ IpTunnelEndPoint ipTunnelEndPointDst;
+ ipTunnelEndPointDst = IpTunnelEndPoint.ipTunnelPoint(dstIp);
+
+ ConnectPoint src = new ConnectPoint(srcElementId, PortNumber.portNumber(10023));
+
+ ConnectPoint dst = new ConnectPoint(dstElementId, PortNumber.portNumber(10023));
+
+ Link link = new DefaultLink(pid, src, dst, Link.Type.DIRECT, EMPTY);
+ links.add(link);
+
+ path = new DefaultPath(pid, links, 20, EMPTY);
+
+ tunnel = new DefaultTunnel(pid, ipTunnelEndPointSrc, ipTunnelEndPointDst, Tunnel.Type.MPLS,
+ new DefaultGroupId(0), TunnelId.valueOf(1), TunnelName.tunnelName("T123"),
+ path, EMPTY);
+
+ // for updating tunnel tunnel should exist in db
+ PcepTunnelData pcepTunnelData = new PcepTunnelData(tunnel, path, RequestType.UPDATE);
+ pcepTunnelData.setPlspId(1);
+ StatefulIPv4LspIdentidiersTlv tlv = new StatefulIPv4LspIdentidiersTlv(0, (short) 1, (short) 2, 3, 4);
+ pcepTunnelData.setStatefulIpv4IndentifierTlv(tlv);
+ tunnelProvider.pcepTunnelAPIMapper.addToTunnelIdMap(pcepTunnelData);
+
+ tunnelProvider.pcepTunnelAPIMapper.handleCreateTunnelRequestQueue(1, pcepTunnelData);
+
+ tunnelProvider.updateTunnel(tunnel, path);
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ tunnelProvider.deactivate();
+ tunnelProvider.controller = null;
+ tunnelProvider.pcepClientController = null;
+ tunnelProvider.tunnelProviderRegistry = null;
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelProviderRegistryAdapter.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelProviderRegistryAdapter.java
new file mode 100644
index 00000000..7292d0b1
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelProviderRegistryAdapter.java
@@ -0,0 +1,56 @@
+package org.onosproject.provider.pcep.tunnel.impl;
+
+import java.util.Set;
+
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelDescription;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelProvider;
+import org.onosproject.incubator.net.tunnel.TunnelProviderRegistry;
+import org.onosproject.incubator.net.tunnel.TunnelProviderService;
+import org.onosproject.net.provider.ProviderId;
+
+public class TunnelProviderRegistryAdapter implements TunnelProviderRegistry {
+ TunnelProvider provider;
+
+ @Override
+ public TunnelProviderService register(TunnelProvider provider) {
+ this.provider = provider;
+ return new TestProviderService();
+ }
+
+ @Override
+ public void unregister(TunnelProvider provider) {
+ }
+
+ @Override
+ public Set<ProviderId> getProviders() {
+ return null;
+ }
+
+ private class TestProviderService implements TunnelProviderService {
+
+ @Override
+ public TunnelProvider provider() {
+ return null;
+ }
+
+ @Override
+ public TunnelId tunnelAdded(TunnelDescription tunnel) {
+ return null;
+ }
+
+ @Override
+ public void tunnelRemoved(TunnelDescription tunnel) {
+ }
+
+ @Override
+ public void tunnelUpdated(TunnelDescription tunnel) {
+ }
+
+ @Override
+ public Tunnel tunnelQueryById(TunnelId tunnelId) {
+ return null;
+ }
+ }
+}
diff --git a/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelServiceAdapter.java b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelServiceAdapter.java
new file mode 100644
index 00000000..1dd77332
--- /dev/null
+++ b/framework/src/onos/providers/pcep/tunnel/src/test/java/org/onosproject/provider/pcep/tunnel/impl/TunnelServiceAdapter.java
@@ -0,0 +1,107 @@
+package org.onosproject.provider.pcep.tunnel.impl;
+
+import org.onosproject.core.ApplicationId;
+import org.onosproject.incubator.net.tunnel.Tunnel;
+import org.onosproject.incubator.net.tunnel.TunnelEndPoint;
+import org.onosproject.incubator.net.tunnel.TunnelId;
+import org.onosproject.incubator.net.tunnel.TunnelListener;
+import org.onosproject.incubator.net.tunnel.TunnelName;
+import org.onosproject.incubator.net.tunnel.TunnelService;
+import org.onosproject.incubator.net.tunnel.TunnelSubscription;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.DeviceId;
+
+import java.util.Collection;
+import java.util.Collections;
+
+public class TunnelServiceAdapter implements TunnelService {
+ @Override
+ public Tunnel borrowTunnel(ApplicationId consumerId, TunnelId tunnelId, Annotations... annotations) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> borrowTunnel(ApplicationId consumerId, TunnelName tunnelName,
+ Annotations... annotations) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> borrowTunnel(ApplicationId consumerId, TunnelEndPoint src, TunnelEndPoint dst,
+ Annotations... annotations) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> borrowTunnel(ApplicationId consumerId, TunnelEndPoint src, TunnelEndPoint dst,
+ Tunnel.Type type, Annotations... annotations) {
+ return null;
+ }
+
+ @Override
+ public boolean returnTunnel(ApplicationId consumerId, TunnelId tunnelId, Annotations... annotations) {
+ return false;
+ }
+
+ @Override
+ public boolean returnTunnel(ApplicationId consumerId, TunnelName tunnelName, Annotations... annotations) {
+ return false;
+ }
+
+ @Override
+ public boolean returnTunnel(ApplicationId consumerId, TunnelEndPoint src, TunnelEndPoint dst,
+ Tunnel.Type type, Annotations... annotations) {
+ return false;
+ }
+
+ @Override
+ public boolean returnTunnel(ApplicationId consumerId, TunnelEndPoint src, TunnelEndPoint dst,
+ Annotations... annotations) {
+ return false;
+ }
+
+ @Override
+ public Tunnel queryTunnel(TunnelId tunnelId) {
+ return null;
+ }
+
+ @Override
+ public Collection<TunnelSubscription> queryTunnelSubscription(ApplicationId consumerId) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> queryTunnel(Tunnel.Type type) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> queryTunnel(TunnelEndPoint src, TunnelEndPoint dst) {
+ return null;
+ }
+
+ @Override
+ public Collection<Tunnel> queryAllTunnels() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public int tunnelCount() {
+ return 0;
+ }
+
+ @Override
+ public Iterable<Tunnel> getTunnels(DeviceId deviceId) {
+ return null;
+ }
+
+ @Override
+ public void addListener(TunnelListener listener) {
+
+ }
+
+ @Override
+ public void removeListener(TunnelListener listener) {
+
+ }
+}
diff --git a/framework/src/onos/providers/pom.xml b/framework/src/onos/providers/pom.xml
new file mode 100644
index 00000000..87dd5656
--- /dev/null
+++ b/framework/src/onos/providers/pom.xml
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2014 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</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>onos-providers</artifactId>
+ <packaging>pom</packaging>
+
+ <description>ONOS information providers and control/management protocol adapter</description>
+
+ <modules>
+ <module>openflow</module>
+ <module>lldp</module>
+ <module>host</module>
+ <module>netconf</module>
+ <module>null</module>
+ <module>pcep</module>
+ <module>ovsdb</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-incubator-api</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-misc</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-junit</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>