[VOL-4246] Feature parity with the previous implementation
Change-Id: I3741edb3c1b88b1cf8b5e6d4ff0900132e2e5e6a
diff --git a/README.md b/README.md
index 75e5c4e..f1ac476 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,87 @@
-ONOS OLT OpenFlow Control Application
-=====================================
+# org.opencord.org
-Build
------
+This ONOS applications is responsible to configure the flows
+required to manage an OLT Device as reported by VOLTHA.
-Let's build all the code
+## Configuration
+The `org.opencord.olt` application depends on `org.opencord.sadis` and exposes
+the following configuration:
+
+```shell
+karaf@root > cfg get org.opencord.olt.impl.Olt
+org.opencord.olt.impl.Olt
+ name=defaultBpId, type=string, value=Default, defaultValue=Default, description=Default bandwidth profile id that is used for authentication trap flows.
+ name=flowProcessingThreads, type=integer, value=8, defaultValue=8, description=Number of threads used to process flows.
+ name=multicastServiceName, type=string, value=MC, defaultValue=MC, description=Default multicast service name.
+ name=requeueDelay, type=integer, value=500, defaultValue=500, description=Delay in ms to put an event back in the queue, used to avoid retrying things to often if conditions are not met.
+ name=subscriberProcessingThreads, type=integer, value=8, defaultValue=8, description=Number of threads used to process flows.
+
+karaf@root > cfg get org.opencord.olt.impl.OltFlowService
+org.opencord.olt.impl.OltFlowService
+ name=enableDhcpOnNni, type=boolean, value=true, defaultValue=false, description=Create DHCP trap flow on NNI port(s).
+ name=enablePppoe, type=boolean, value=false, defaultValue=false, description=Send PPPoED authentication trap flows before subscriber provisioning.
+ name=defaultTechProfileId, type=integer, value=64, defaultValue=64, description=Default technology profile id that is used for authentication trap flows.
+ name=enableIgmpOnNni, type=boolean, value=false, defaultValue=false, description=Create IGMP trap flow on NNI port(s).
+ name=enableEapol, type=boolean, value=true, defaultValue=true, description=Send EAPOL authentication trap flows before subscriber provisioning.
+ name=enableDhcpV6, type=boolean, value=false, defaultValue=false, description=Enable flows for DHCP v6 if dhcp is required in sadis config.
+ name=enableDhcpV4, type=boolean, value=true, defaultValue=true, description=Enable flows for DHCP v4 if dhcp is required in sadis config.
+
+karaf@root > cfg get org.opencord.olt.impl.OltMeterService
+org.opencord.olt.impl.OltMeterService
+ name=deleteMeters, type=boolean, value=true, defaultValue=true, description=Delete meters when reference count drops to zero.
+
+
+```
+
+## CLI commands
+
+```text
+volt-add-subscriber-access (Adds a subscriber to an access device)
+volt-bpmeter-mappings (Shows information about programmed meters, including the relation with the Bandwidth Profile)
+volt-failed-subscribers (Shows subscribers that failed provisioning)
+volt-olts (Shows vOLTs connected to ONOS)
+volt-port-status (Shows information about the OLT ports (default EAPOL, subscriber flows)
+volt-programmed-meters (Shows information about programmed meters, including the relation with the Bandwidth Profile)
+volt-programmed-subscribers (Shows subscribers programmed in the dataplane)
+volt-remove-subscriber-access (Adds a subscriber to an access device)
+volt-requested-subscribers (Shows subscribers programmed by the operator. Their data-plane status depends on the ONU status)
+
+```
+
+## App Design
+
+The `org.opencord.olt` application internal structure reflects the following diagram:
+
+![OLT App Diagram](./assets/diagram.png)
+
+## Install the app on a running ONOS
+
+> _**Prerequisites**_
+>
+> - A running ONOS cluster (can be a single node cluster)
+> - ONOS REST APIs exposed out of the kubernetes cluster and reachable from your machine (we used port 30120 in the following example)
+
+
+If you want to try out the app on a running ONOS cluster you can follow this procedure:
+
+1) Build the app (requires Java 11 to be installed)
+ ```shell
mvn clean install
+ ```
+2) Uninstall the current version of the app:
+ ```shell
+ curl --fail -sSL --user karaf:karaf --noproxy 127.0.0.1 -X DELETE http://127.0.0.1:30120/onos/v1/applications/org.opencord.olt
+ ```
+ You should see the following confirmation message in the logs:
+ ```shell
+ 17:15:39.279 INFO [ApplicationManager] Application org.opencord.olt has been uninstalled
+ ```
+3) Install the new version of the app
+ ```shell
+ curl --fail -sSL --user karaf:karaf --noproxy 127.0.0.1 -X POST -HContent-Type:application/octet-stream http://127.0.0.1:30120/onos/v1/applications?activate=true --data-binary @app/target/olt-app-5.0.0-SNAPSHOT.oar
+ ```
+ you should see:
+ ```shell
+ 17:55:37.197 INFO [ApplicationManager] Application org.opencord.olt has been activated
+ ```
\ No newline at end of file
diff --git a/TEST-README.md b/TEST-README.md
deleted file mode 100644
index 63f8686..0000000
--- a/TEST-README.md
+++ /dev/null
@@ -1,160 +0,0 @@
-How to test technology profile
-------------------------------
-
-- Export ONOS directory:
-
-```
-export ONOS_ROOT=~/voltha-projects/onos
-source $ONOS_ROOT/tools/dev/bash_profile
-```
-
-- Build and run ONOS:
-
-```
-mvn clean install -DskipTests -Dcheckstyle.skip=true OR onos-buck build onos
-ok clean
-```
-
-- Build SADIS and OLT apps:
-
-```
-mvn clean install
-```
-
-- Install and Activate SADIS and OLT apps:
-
-```
-onos-app localhost install ~/voltha-projects/sadis/app/target/sadis-app-3.0.0.oar
-onos-app localhost activate org.opencord.sadis
-
-onos-app localhost install ~/voltha-projects/olt/app/target/olt-app-3.0.1.oar
-onos-app localhost activate org.opencord.olt
-```
-
-- To test with the oldest configuration (as oldest SADIS) - it includes only S and C tags:
-
-```
-onos-netcfg localhost ~/voltha-projects/olt/app/src/main/resources/cfg.json
-```
-
-- To test AT&T use case:
- - Note:the tech profile table id is 10 to work with Mininet - if Voltha is used, then it will be 64 or greater value
-
-```
-onos-netcfg localhost ~/voltha-projects/olt/app/src/main/resources/vlan_cfg.json
-```
-
-- To test DT use case (any vlan):
- - Note:the tech profile table id is 10 to work with Mininet - if Voltha is used, then it will be 64 or greater value
-
-```
-onos-netcfg localhost ~/voltha-projects/olt/app/src/main/resources/any_vlan_cfg.json
-```
-
-- To test without VOLTHA, you can use Mininet with UserSwitch and emulate the topology:
-
-```
-sudo mn --custom ~/voltha-projects/olt/app/src/main/resources/custom-topo.py --switch user --controller=remote,ip=127.0.0.1,port=6633
-```
-
-- Run ONOS client:
-
-```
- cd /tmp/onos-1.13.6/apache-karaf-3.0.8/bin
- ./client
-```
-
-- Check devices, flows and ports:
-
-```
-devices
-
-the output (note that: the driver is pmc-olt):
-
-id=of:0000000000000001, available=true, local-status=connected 2s ago, role=MASTER, type=SWITCH, mfr=Stanford University, Ericsson Research and CPqD Research, hw=OpenFlow 1.3 Reference Userspace Switch, sw=Sep 10 2018 16:56:31, serial=1, chassis=1, driver=pmc-olt, channelId=127.0.0.1:36022, locType=none, managementAddress=127.0.0.1, name=of:0000000000000001, protocol=OF_13
-
-flows
-
-the output:
-
-deviceId=of:0000000000000001, flowRuleCount=4
- id=ac00002f18dba3, state=ADDED, bytes=0, packets=0, duration=0, liveType=UNKNOWN, priority=10000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, ETH_TYPE:eapol], treatment=DefaultTrafficTreatment{immediate=[OUTPUT:CONTROLLER], deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac00005512664b, state=ADDED, bytes=0, packets=0, duration=0, liveType=UNKNOWN, priority=10000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:LOCAL, ETH_TYPE:eapol], treatment=DefaultTrafficTreatment{immediate=[OUTPUT:CONTROLLER], deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac00005d0fea43, state=ADDED, bytes=0, packets=0, duration=0, liveType=UNKNOWN, priority=10000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, ETH_TYPE:lldp], treatment=DefaultTrafficTreatment{immediate=[OUTPUT:CONTROLLER], deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000617983d6, state=ADDED, bytes=0, packets=0, duration=0, liveType=UNKNOWN, priority=10000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, ETH_TYPE:ipv4, IP_PROTO:17, UDP_SRC:68, UDP_DST:67], treatment=DefaultTrafficTreatment{immediate=[OUTPUT:CONTROLLER], deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
-
-ports
-
-the output (assume that port 1 is UNI and 2 is NNI):
-
-id=of:0000000000000001, available=true, local-status=connected 7s ago, role=MASTER, type=SWITCH, mfr=Stanford University, Ericsson Research and CPqD Research, hw=OpenFlow 1.3 Reference Userspace Switch, sw=Sep 10 2018 16:56:31, serial=1, chassis=1, driver=pmc-olt, channelId=127.0.0.1:36022, locType=none, managementAddress=127.0.0.1, name=of:0000000000000001, protocol=OF_13
- port=LOCAL, state=enabled, type=copper, speed=10 , adminState=enabled, portMac=00:00:00:00:00:01, portName=tap:
- port=1, state=enabled, type=copper, speed=10485 , adminState=enabled, portMac=ba:5d:49:53:9f:d2, portName=s1-eth1
- port=2, state=enabled, type=copper, speed=10485 , adminState=enabled, portMac=9e:11:ee:ba:4f:f4, portName=s1-eth
-
-```
-
-- Add subscriber to UNI port:
-
-```
-volt-add-subscriber-access of:0000000000000001 1
-```
-
-- The flows when you use cfg.json:
-
-```
-Upstream Flows:
-
- id=ac000000f780f6, state=ADDED, bytes=0, packets=0, duration=5, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:0], treatment=DefaultTrafficTreatment{immediate=[VLAN_ID:2], deferred=[], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000c9fd17bf, state=ADDED, bytes=0, packets=0, duration=5, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:2], treatment=DefaultTrafficTreatment{immediate=[VLAN_PUSH:vlan, VLAN_ID:4], deferred=[OUTPUT:2], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
-
-Downstream Flows:
-
- id=ac000022a77664, state=ADDED, bytes=0, packets=0, duration=5, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, METADATA:200000001, VLAN_VID:4], treatment=DefaultTrafficTreatment{immediate=[VLAN_POP], deferred=[], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac00001801d6d1, state=ADDED, bytes=0, packets=0, duration=5, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, VLAN_VID:2], treatment=DefaultTrafficTreatment{immediate=[VLAN_POP, VLAN_ID:0], deferred=[OUTPUT:1], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
-```
-
-- The flows when you use vlan_cfg.json:
-
-```
-Upstream Flows:
-
- id=ac000000f780f6, state=ADDED, bytes=0, packets=0, duration=6, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:0], treatment=DefaultTrafficTreatment{immediate=[VLAN_ID:2], deferred=[], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000c9fd17bf, state=ADDED, bytes=0, packets=0, duration=6, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:2], treatment=DefaultTrafficTreatment{immediate=[VLAN_PUSH:vlan, VLAN_ID:4], deferred=[OUTPUT:2], transition=TABLE:10, meter=[METER:1], cleared=false, StatTrigger=null, metadata=null}
-
-Downstream Flows:
-
- id=ac000022a77664, state=ADDED, bytes=0, packets=0, duration=6, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, METADATA:200000001, VLAN_VID:4], treatment=DefaultTrafficTreatment{immediate=[VLAN_POP], deferred=[], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac00001801d6d1, state=ADDED, bytes=0, packets=0, duration=6, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, VLAN_VID:2], treatment=DefaultTrafficTreatment{immediate=[VLAN_POP, VLAN_ID:0], deferred=[OUTPUT:1], transition=TABLE:10, meter=[METER:2], cleared=false, StatTrigger=null, metadata=null}
-
-Upstream Meter:
-
- DefaultMeter{device=of:0000000000000001, cellId=1, appId=org.opencord.olt, unit=KB_PER_SEC, isBurst=true, state=ADDED, bands=[DefaultBand{rate=200000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=10000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=10000000, burst-size=0, type=DROP, drop-precedence=null}]}
-
-Downstream Meter:
-
- DefaultMeter{device=of:0000000000000001, cellId=2, appId=org.opencord.olt, unit=KB_PER_SEC, isBurst=true, state=ADDED, bands=[DefaultBand{rate=300000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=20000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=30000000, burst-size=0, type=DROP, drop-precedence=null}]}
-```
-
-- The flows when you use any_vlan_cfg.json:
-
-```
-Upstream Flows:
-
- id=ac000009d37362, state=ADDED, bytes=0, packets=0, duration=3, liveType=UNKNOWN, priority=500, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1], treatment=DefaultTrafficTreatment{immediate=[NOACTION], deferred=[], transition=None, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000fb6f690a, state=ADDED, bytes=0, packets=0, duration=3, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:Any], treatment=DefaultTrafficTreatment{immediate=[], deferred=[], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000076e2984, state=ADDED, bytes=0, packets=0, duration=3, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:1, VLAN_VID:Any], treatment=DefaultTrafficTreatment{immediate=[VLAN_PUSH:qinq, VLAN_ID:4], deferred=[OUTPUT:2], transition=TABLE:10, meter=[METER:1], cleared=false, StatTrigger=null, metadata=null}
-
-Downstream Flows:
-
- id=ac000022a77664, state=ADDED, bytes=0, packets=0, duration=3, liveType=UNKNOWN, priority=1000, tableId=0, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, METADATA:200000001, VLAN_VID:4], treatment=DefaultTrafficTreatment{immediate=[], deferred=[VLAN_POP], transition=TABLE:1, meter=[], cleared=false, StatTrigger=null, metadata=null}
- id=ac0000911ae9cb, state=ADDED, bytes=0, packets=0, duration=3, liveType=UNKNOWN, priority=1000, tableId=1, appId=org.opencord.olt, payLoad=null, selector=[IN_PORT:2, VLAN_VID:Any], treatment=DefaultTrafficTreatment{immediate=[], deferred=[OUTPUT:1], transition=TABLE:10, meter=[METER:2], cleared=false, StatTrigger=null, metadata=null}
-
-Upstream Meter:
-
- DefaultMeter{device=of:0000000000000001, cellId=1, appId=org.opencord.olt, unit=KB_PER_SEC, isBurst=true, state=ADDED, bands=[DefaultBand{rate=200000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=10000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=10000000, burst-size=0, type=DROP, drop-precedence=null}]}
-
-Downstream Meter:
-
- DefaultMeter{device=of:0000000000000001, cellId=2, appId=org.opencord.olt, unit=KB_PER_SEC, isBurst=true, state=ADDED, bands=[DefaultBand{rate=300000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=20000000, burst-size=348000, type=DROP, drop-precedence=null}, DefaultBand{rate=30000000, burst-size=0, type=DROP, drop-precedence=null}]}
-```
diff --git a/api/pom.xml b/api/pom.xml
index 522aed0..34d69e0 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2016-present Open Networking Foundation
+ ~ Copyright 2021 Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -17,32 +17,21 @@
<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>
<artifactId>olt</artifactId>
<groupId>org.opencord</groupId>
- <version>4.6.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
+ <version>5.0.0-SNAPSHOT</version>
</parent>
+ <modelVersion>4.0.0</modelVersion>
<version>${olt.api.version}</version>
<artifactId>olt-api</artifactId>
<packaging>bundle</packaging>
- <url>http://opencord.org</url>
-
- <description>OLT application API</description>
-
- <dependencies>
- <dependency>
- <groupId>org.opencord</groupId>
- <artifactId>sadis-api</artifactId>
- <version>${sadis.api.version}</version>
- <scope>provided</scope>
- </dependency>
- </dependencies>
-
+ <properties>
+ <maven.compiler.source>11</maven.compiler.source>
+ <maven.compiler.target>11</maven.compiler.target>
+ </properties>
<build>
<plugins>
<plugin>
@@ -51,4 +40,4 @@
</plugin>
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/api/src/main/java/org/opencord/olt/AccessDevicePort.java b/api/src/main/java/org/opencord/olt/AccessDevicePort.java
deleted file mode 100644
index 8a1665d..0000000
--- a/api/src/main/java/org/opencord/olt/AccessDevicePort.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2021-present Open Networking Foundation
- *
- * 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.opencord.olt;
-
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-
-/**
- * OLT device port.
- */
-public class AccessDevicePort {
-
- Port port;
- Type type;
-
- /**
- * OLT Port Type.
- */
- public enum Type {
- NNI,
- UNI,
- }
-
- /**
- * Creates an AccessDevicePort with given ONOS port object and OLT port type.
- *
- * @param port ONOS port
- * @param type OLT port type
- */
- public AccessDevicePort(Port port, Type type) {
- this.port = port;
- this.type = type;
- }
-
- /**
- * Get ONOS Port object.
- *
- * @return ONOS port
- */
- public Port port() {
- return this.port;
- }
-
- /**
- * Get OLT port Type.
- *
- * @return OLT port type
- */
- public Type type() {
- return this.type;
- }
-
- /**
- * Check port is enabled state on ONOS.
- *
- * @return is Access Device Port enabled
- */
- public boolean isEnabled() {
- return this.port.isEnabled();
- }
-
- /**
- * Get Device ID of OLT which the port is connected.
- *
- * @return OLT Device ID
- */
- public DeviceId deviceId() {
- return (DeviceId) this.port.element().id();
- }
-
- /**
- * Get port number.
- *
- * @return port number
- */
- public PortNumber number() {
- return this.port.number();
- }
-
- /**
- * Get port name which is combination of serial number and uni index.
- *
- * @return port name (ex: BBSM00010001-1)
- */
- public String name() {
- return this.port.annotations().value(AnnotationKeys.PORT_NAME);
- }
-
- @Override
- public String toString() {
- return deviceId().toString() + '/' + number() + '[' + name() + ']';
- }
-
-}
diff --git a/api/src/main/java/org/opencord/olt/AccessDeviceService.java b/api/src/main/java/org/opencord/olt/AccessDeviceService.java
index 4878d97..9f39715 100644
--- a/api/src/main/java/org/opencord/olt/AccessDeviceService.java
+++ b/api/src/main/java/org/opencord/olt/AccessDeviceService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,93 +16,62 @@
package org.opencord.olt;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
import org.onlab.packet.VlanId;
import org.onosproject.event.ListenerService;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
-import com.google.common.collect.ImmutableMap;
-import org.opencord.sadis.UniTagInformation;
+import java.util.List;
-/**
- * Service for interacting with an access device (OLT).
- */
-public interface AccessDeviceService
- extends ListenerService<AccessDeviceEvent, AccessDeviceListener> {
-
+public interface AccessDeviceService extends ListenerService<AccessDeviceEvent, AccessDeviceListener> {
/**
* Provisions connectivity for a subscriber on an access device.
- * Installs flows for all uni tag information
+ * It installs flows for all uni tag information
*
- * @param port subscriber's connection point
- * @return true if successful false otherwise
+ * @param cp subscriber's connection point
+ * @return true if the request is accepted (does not guarantee the subscriber is provisioned)
*/
- boolean provisionSubscriber(ConnectPoint port);
+ boolean provisionSubscriber(ConnectPoint cp);
/**
- * Removes provisioned connectivity for a subscriber from an access device.
- * Removes flows for all uni tag information
- *
- * @param port subscriber's connection point
- * @return true if successful false otherwise
+ * Removes subscriber flows from a given ConnectPoint.
+ * @param cp subscriber's connect point
+ * @return true if the request is accepted (does not guarantee the subscriber is removed)
*/
- boolean removeSubscriber(ConnectPoint port);
+ boolean removeSubscriber(ConnectPoint cp);
/**
- * Provisions a uni tag information for the specific subscriber.
- * It finds the related uni tag information from the subscriber uni tag list
- * and installs it
- *
- * @param subscriberId Identification of the subscriber
- * @param sTag additional outer tag on this port
- * @param cTag additional inner tag on this port
- * @param tpId additional technology profile id
- * @return true if successful false otherwise
+ * Provisions connectivity for a particular service for a subscriber.
+ * @param cp subscriber's connect point
+ * @param cTag service's cTag
+ * @param sTag service's sTag
+ * @param tpId service's Technology Profile ID
+ * @return true if the request is accepted (does not guarantee the subscriber is provisioned)
*/
- boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
- Optional<VlanId> cTag, Optional<Integer> tpId);
+ boolean provisionSubscriber(ConnectPoint cp, VlanId cTag, VlanId sTag, Integer tpId);
/**
- * Removes a uni tag information for the specific subscriber.
- * It finds the related uni tag information from the subscriber uni tag list
- * and remove it
- *
- * @param subscriberId Identification of the subscriber
- * @param sTag additional outer tag on this port
- * @param cTag additional inner tag on this port
- * @param tpId additional technology profile id
- * @return true if successful false otherwise
+ * Removes connectivity for a particular service for a subscriber.
+ * @param cp subscriber's connect point
+ * @param cTag service's cTag
+ * @param sTag service's sTag
+ * @param tpId service's Technology Profile ID
+ * @return true if the request is accepted (does not guarantee the subscriber is removed)
*/
- boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
- Optional<VlanId> cTag, Optional<Integer> tpId);
+ boolean removeSubscriber(ConnectPoint cp, VlanId cTag, VlanId sTag, Integer tpId);
/**
- * Returns the list of active OLTs.
- *
- * @return a List
+ * Returns a list of connected OLT devices ID.
+ * @return a list of devices
*/
- List<DeviceId> fetchOlts();
+ List<DeviceId> getConnectedOlts();
/**
- * Returns information about subscribers that have been programmed in the
- * data-plane. It shows all uni tag information list of the subscribers even if
- * these have not been programmed.
+ * Finds the ConnectPoint to which a subscriber is connected.
*
- * @return an immutable map of locations and subscriber information
+ * @param id The id of the subscriber, this is the same ID as in Sadis
+ * @return Subscribers ConnectPoint if found else null
*/
- ImmutableMap<ConnectPoint, Set<UniTagInformation>> getProgSubs();
-
- /**
- * Returns information about subscribers that have NOT been programmed in the
- * data-plane. It shows all uni tag information list of the subscribers even if
- * these have not been programmed, meaning no flows have been sent to the device.
- *
- * @return an immutable map of locations and subscriber information
- */
- ImmutableMap<ConnectPoint, Set<UniTagInformation>> getFailedSubs();
+ ConnectPoint findSubscriberConnectPoint(String id);
}
diff --git a/api/src/main/java/org/opencord/olt/AccessSubscriberId.java b/api/src/main/java/org/opencord/olt/AccessSubscriberId.java
deleted file mode 100644
index f85ea0f..0000000
--- a/api/src/main/java/org/opencord/olt/AccessSubscriberId.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.opencord.olt;
-
-/**
- * Class representing the identifier for a subscriber.
- * It encapsulates the name of the logical port for the subscriber in
- * ONOS.
- * For e.g. if in ONOS you see
- * <pre>
- * onos> ports
- * id=of:00000000c0a83264, available=true, local-status=connected 3m48s ago, role=MASTER, mfr=VOLTHA Projectd,...
- * port=32, state=enabled, type=fiber, speed=0 , adminState=enabled, portMac=08:00:00:00:00:20, portName=TWSH80808082
- * </pre>
- * the subscriber id would encapsulate the string "TWSH80808082"
- */
-public class AccessSubscriberId {
- // string representing the identifier
- String id;
-
- /**
- * Creates an AccessSubscriberId with the passed id.
- *
- * @param id identifier of the subscriber
- */
- public AccessSubscriberId(String id) {
- this.id = id;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#hashCode()
- */
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + (this.id == null ? 0 : this.id.hashCode());
-
- return result;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#equals(java.lang.Object)
- */
- @Override
- public boolean equals(final Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null) {
- return false;
- }
- if (this.getClass() != obj.getClass()) {
- return false;
- }
-
- final AccessSubscriberId other = (AccessSubscriberId) obj;
- if (this.id == null) {
- if (other.id != null) {
- return false;
- }
- } else if (!this.id.equals(other.id)) {
- return false;
- }
-
- return true;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see java.lang.Object#toString()
- */
- @Override
- public String toString() {
- return id;
- }
-}
diff --git a/api/src/main/java/org/opencord/olt/package-info.java b/api/src/main/java/org/opencord/olt/package-info.java
index f48f829..d6cff7d 100644
--- a/api/src/main/java/org/opencord/olt/package-info.java
+++ b/api/src/main/java/org/opencord/olt/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
*/
/**
- * OLT application api.
+ * OLT App APIs.
*/
-package org.opencord.olt;
+package org.opencord.olt;
\ No newline at end of file
diff --git a/app/app.xml b/app/app.xml
index da3f106..914f727 100644
--- a/app/app.xml
+++ b/app/app.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2018-present Open Networking Foundation
+ ~ Copyright 2021-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -21,8 +21,8 @@
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}">
<description>${project.description}</description>
- <artifact>mvn:${project.groupId}/olt-api/${olt.api.version}</artifact>
<artifact>mvn:${project.groupId}/olt-impl/${project.version}</artifact>
<artifact>mvn:${project.groupId}/olt-web/${project.version}</artifact>
+ <artifact>mvn:${project.groupId}/olt-api/${project.version}</artifact>
<artifact>mvn:${project.groupId}/sadis-api/${sadis.api.version}</artifact>
</app>
diff --git a/app/features.xml b/app/features.xml
index d63d0d7..fffac01 100644
--- a/app/features.xml
+++ b/app/features.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!--
- ~ Copyright 2018-present Open Networking Foundation
+ ~ Copyright 2021-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -18,9 +18,9 @@
<feature name="${project.artifactId}" version="${project.version}"
description="${project.description}">
<feature>onos-api</feature>
- <bundle>mvn:${project.groupId}/olt-api/${olt.api.version}</bundle>
<bundle>mvn:${project.groupId}/olt-impl/${project.version}</bundle>
<bundle>mvn:${project.groupId}/olt-web/${project.version}</bundle>
+ <bundle>mvn:${project.groupId}/olt-api/${project.version}</bundle>
<bundle>mvn:${project.groupId}/sadis-api/${sadis.api.version}</bundle>
</feature>
</features>
diff --git a/app/pom.xml b/app/pom.xml
index 4185d06..4a537c7 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2016-present Open Networking Foundation
+ ~ Copyright 2021 Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -18,17 +18,17 @@
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">
<parent>
- <groupId>org.opencord</groupId>
<artifactId>olt</artifactId>
- <version>4.6.0-SNAPSHOT</version>
+ <groupId>org.opencord</groupId>
+ <version>5.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
-
<modelVersion>4.0.0</modelVersion>
+ <groupId>org.opencord</groupId>
<artifactId>olt-app</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<description>OLT application for CORD</description>
-
<properties>
<onos.app.name>org.opencord.olt</onos.app.name>
<onos.app.title>OLT Access Management</onos.app.title>
@@ -39,7 +39,6 @@
CORD OLT Access management application
</onos.app.readme>
</properties>
-
<build>
<plugins>
<plugin>
@@ -48,4 +47,4 @@
</plugin>
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/assets/diagram.png b/assets/diagram.png
new file mode 100644
index 0000000..c46c2b9
--- /dev/null
+++ b/assets/diagram.png
Binary files differ
diff --git a/impl/pom.xml b/impl/pom.xml
index 2c7282b..c3609db 100644
--- a/impl/pom.xml
+++ b/impl/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2016-present Open Networking Foundation
+ ~ Copyright 2021 Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -18,75 +18,79 @@
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">
<parent>
- <groupId>org.opencord</groupId>
<artifactId>olt</artifactId>
- <version>4.6.0-SNAPSHOT</version>
+ <groupId>org.opencord</groupId>
+ <version>5.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
-
<modelVersion>4.0.0</modelVersion>
<artifactId>olt-impl</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
- <description>OLT application for CORD</description>
-
<dependencies>
<dependency>
- <groupId>org.opencord</groupId>
- <artifactId>olt-api</artifactId>
- <version>${olt.api.version}</version>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
- <groupId>org.opencord</groupId>
- <artifactId>sadis-api</artifactId>
- <version>${sadis.api.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-cli</artifactId>
<version>${onos.version}</version>
<scope>provided</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onlab-osgi</artifactId>
+ <version>${onos.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-api</artifactId>
<version>${onos.version}</version>
- <classifier>tests</classifier>
<scope>test</scope>
+ <classifier>tests</classifier>
</dependency>
-
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-core-serializers</artifactId>
<version>${onos.version}</version>
<scope>provided</scope>
</dependency>
-
+ <dependency>
+ <groupId>org.opencord</groupId>
+ <artifactId>sadis-api</artifactId>
+ <version>${sadis.api.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.25</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ <version>1.7.25</version>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-all</artifactId>
+ <version>1.9.5</version>
+ </dependency>
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.console</artifactId>
<scope>provided</scope>
</dependency>
-
<dependency>
<groupId>org.apache.karaf.shell</groupId>
<artifactId>org.apache.karaf.shell.core</artifactId>
<scope>provided</scope>
</dependency>
-
<dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.10.2</version>
- <scope>provided</scope>
+ <groupId>org.opencord</groupId>
+ <artifactId>olt-api</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ <scope>compile</scope>
</dependency>
-
</dependencies>
-
<build>
<plugins>
<plugin>
@@ -104,4 +108,4 @@
</plugin>
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/OltUniPortCompleter.java b/impl/src/main/java/org/opencord/olt/cli/OltUniPortCompleter.java
index 68ceca6..98301db 100644
--- a/impl/src/main/java/org/opencord/olt/cli/OltUniPortCompleter.java
+++ b/impl/src/main/java/org/opencord/olt/cli/OltUniPortCompleter.java
@@ -19,34 +19,34 @@
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onosproject.cli.net.PortNumberCompleter;
-import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
+import org.opencord.olt.impl.OltDeviceServiceInterface;
+
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
-import java.util.stream.StreamSupport;
@Service
public class OltUniPortCompleter extends PortNumberCompleter {
- private static final String NNI = "nni-";
public OltUniPortCompleter() {
}
protected List<String> choices() {
+ DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
DeviceId deviceId = this.lookForDeviceId();
if (deviceId == null) {
return Collections.emptyList();
} else {
- DeviceService deviceService = (DeviceService) DefaultServiceDirectory.getService(DeviceService.class);
- return (List) StreamSupport.stream(deviceService.getPorts(deviceId).spliterator(), false)
- .filter((port) -> {
- return port.isEnabled() && !port.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI);
- })
- .map((port) -> {
- return port.number().toString();
- }).collect(Collectors.toList());
+ Device device = deviceService.getDevice(DeviceId.deviceId(deviceId.toString()));
+ OltDeviceServiceInterface oltDeviceService =
+ DefaultServiceDirectory.getService(OltDeviceServiceInterface.class);
+ return deviceService.getPorts(deviceId).stream()
+ .filter((port) -> port.isEnabled() && !oltDeviceService.isNniPort(device, port.number()))
+ .map((port) -> port.number().toString())
+ .collect(Collectors.toList());
}
}
}
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowBpMeterMappingsCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowBpMeterMappingsCommand.java
deleted file mode 100644
index eb1b47d..0000000
--- a/impl/src/main/java/org/opencord/olt/cli/ShowBpMeterMappingsCommand.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.meter.MeterKey;
-import org.opencord.olt.internalapi.AccessDeviceMeterService;
-
-import java.util.Collection;
-import java.util.Map;
-
-@Service
-@Command(scope = "onos", name = "volt-bpmeter-mappings",
- description = "Shows information about bandwidthProfile-meterKey (device / meter) mappings")
-public class ShowBpMeterMappingsCommand extends AbstractShellCommand {
-
- @Override
- protected void doExecute() {
- AccessDeviceMeterService service = AbstractShellCommand.get(AccessDeviceMeterService.class);
- Map<String, Collection<MeterKey>> bpMeterMappings = service.getBpMeterMappings();
- bpMeterMappings.forEach(this::display);
- }
-
- private void display(String bpInfo, Collection<MeterKey> meterKeyList) {
- meterKeyList.forEach(meterKey ->
- print("bpInfo=%s deviceId=%s meterId=%s",
- bpInfo, meterKey.deviceId(), meterKey.meterId()));
-
- }
-}
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowFailedSubscribersCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowFailedSubscribersCommand.java
index 5d6bf3d..cb40faa 100644
--- a/impl/src/main/java/org/opencord/olt/cli/ShowFailedSubscribersCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowFailedSubscribersCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,31 +19,20 @@
import org.apache.karaf.shell.api.action.Command;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-import org.opencord.olt.AccessDeviceService;
-import org.opencord.sadis.UniTagInformation;
-
-import java.util.Map;
-import java.util.Set;
-
/**
- * Shows subscriber information for those subscriber which have been programmed
- * in the data-plane.
+ * Shows failed subscribers on an OLTs.
*/
@Service
@Command(scope = "onos", name = "volt-failed-subscribers",
- description = "Shows subscribers awaiting for programming in the dataplane")
+ description = "Shows subscribers that failed provisioning")
public class ShowFailedSubscribersCommand extends AbstractShellCommand {
@Override
protected void doExecute() {
- AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
- Map<ConnectPoint, Set<UniTagInformation>> info = service.getFailedSubs();
- info.forEach(this::display);
+ // NOTE
+ // this command is still available purely for backward compatibility but whilst the old implementation
+ // had a limited number of retries available the new implementation will keep retrying.
+ print("Unimplemented");
}
- private void display(ConnectPoint cp, Set<UniTagInformation> uniTagInformation) {
- uniTagInformation.forEach(uniTag ->
- print("location=%s tagInformation=%s", cp, uniTag));
- }
-}
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowMeterMappings.java b/impl/src/main/java/org/opencord/olt/cli/ShowMeterMappings.java
new file mode 100644
index 0000000..7e2175d
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowMeterMappings.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.opencord.olt.impl.MeterData;
+import org.opencord.olt.impl.OltMeterServiceInterface;
+
+import java.util.Map;
+/**
+ * Shows meters to bandwidth profile mappings.
+ */
+@Service
+@Command(scope = "onos", name = "volt-bpmeter-mappings",
+ description = "Shows information about programmed meters, including the relation with the Bandwidth Profile")
+public class ShowMeterMappings extends AbstractShellCommand {
+
+ @Override
+ protected void doExecute() {
+ OltMeterServiceInterface service = AbstractShellCommand.get(OltMeterServiceInterface.class);
+ Map<DeviceId, Map<String, MeterData>> meters = service.getProgrammedMeters();
+ if (meters.isEmpty()) {
+ print("No meters programmed by the olt app");
+ }
+ meters.forEach(this::display);
+ }
+
+ private void display(DeviceId deviceId, Map<String, MeterData> data) {
+ data.forEach((bp, md) ->
+ print("\tbpInfo=%s deviceId=%s meterId=%s",
+ deviceId, bp, md.getMeterId()));
+
+ }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowOltCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowOltCommand.java
index 08aaa48..c379116 100644
--- a/impl/src/main/java/org/opencord/olt/cli/ShowOltCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowOltCommand.java
@@ -40,9 +40,9 @@
protected void doExecute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
if (outputJson()) {
- print("%s", json(service.fetchOlts()));
+ print("%s", json(service.getConnectedOlts()));
} else {
- service.fetchOlts().forEach(did -> print("OLT %s", did));
+ service.getConnectedOlts().forEach(did -> print("OLT %s", did));
}
}
@@ -62,4 +62,4 @@
}
return node;
}
-}
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowPortStatus.java b/impl/src/main/java/org/opencord/olt/cli/ShowPortStatus.java
new file mode 100644
index 0000000..f3b35a0
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowPortStatus.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.cli.net.PortNumberCompleter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.opencord.olt.impl.OltFlowServiceInterface;
+import org.opencord.olt.impl.OltPortStatus;
+import org.opencord.olt.impl.ServiceKey;
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Shows ports' status of an OLT.
+ */
+@Service
+@Command(scope = "onos", name = "volt-port-status",
+ description = "Shows information about the OLT ports (default EAPOL, subscriber flows)")
+public class ShowPortStatus extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "deviceId", description = "Access device ID",
+ required = false, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ private String strDeviceId = null;
+
+ @Argument(index = 1, name = "port", description = "Subscriber port number",
+ required = false, multiValued = false)
+ @Completion(PortNumberCompleter.class)
+ private String strPort = null;
+
+ @Override
+ protected void doExecute() {
+
+ OltFlowServiceInterface service = AbstractShellCommand.get(OltFlowServiceInterface.class);
+ Map<ServiceKey, OltPortStatus> connectPointStatus = service.getConnectPointStatus();
+ if (connectPointStatus.isEmpty()) {
+ print("No ports handled by the olt app");
+ return;
+ }
+
+ Map<DeviceId, Map<PortNumber, Map<UniTagInformation, OltPortStatus>>> sortedStatus = new HashMap<>();
+
+ DeviceId deviceId = strDeviceId != null ? DeviceId.deviceId(strDeviceId) : null;
+ PortNumber portNumber = strPort != null ? PortNumber.portNumber(strPort) : null;
+
+ connectPointStatus.forEach((sk, fs) -> {
+ if (deviceId != null && !deviceId.equals(sk.getPort().connectPoint()
+ .deviceId())) {
+ return;
+ }
+ if (portNumber != null && !portNumber.equals(sk.getPort().connectPoint().port())) {
+ return;
+ }
+ sortedStatus.compute(sk.getPort().connectPoint().deviceId(), (id, portMap) -> {
+ if (portMap == null) {
+ portMap = new HashMap<>();
+ }
+ portMap.compute(sk.getPort().connectPoint().port(), (id2, ps) -> {
+ if (ps == null) {
+ ps = new HashMap<>();
+ }
+ ps.put(sk.getService(), fs);
+ return ps;
+ });
+
+ return portMap;
+ });
+ });
+
+ sortedStatus.forEach(this::display);
+ }
+
+ private void display(DeviceId deviceId, Map<PortNumber, Map<UniTagInformation, OltPortStatus>> ports) {
+ print("deviceId=%s, managedPorts=%d", deviceId, ports.size());
+
+ ports.forEach((port, subscribers) -> {
+ print("\tport=%s", port);
+ subscribers.forEach((uti, status) -> {
+ print("\t\tservice=%s defaultEapolStatus=%s subscriberFlowsStatus=%s dhcpStatus=%s",
+ uti.getServiceName(), status.defaultEapolStatus,
+ status.subscriberFlowsStatus, status.dhcpStatus);
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMeters.java b/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMeters.java
new file mode 100644
index 0000000..da2307e
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMeters.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.cli;
+
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.DeviceId;
+import org.opencord.olt.impl.MeterData;
+import org.opencord.olt.impl.OltMeterServiceInterface;
+
+import java.util.Map;
+/**
+ * Displays the programmed meters through the OLT app.
+ */
+@Service
+@Command(scope = "onos", name = "volt-programmed-meters",
+ description = "Shows information about programmed meters, including the relation with the Bandwidth Profile")
+public class ShowProgrammedMeters extends AbstractShellCommand {
+
+ @Override
+ protected void doExecute() {
+ OltMeterServiceInterface service = AbstractShellCommand.get(OltMeterServiceInterface.class);
+ Map<DeviceId, Map<String, MeterData>> meters = service.getProgrammedMeters();
+ if (meters.isEmpty()) {
+ print("No meters programmed by the olt app");
+ return;
+ }
+ meters.forEach(this::display);
+ }
+
+ private void display(DeviceId deviceId, Map<String, MeterData> data) {
+ print("deviceId=%s, meterCount=%d", deviceId, data.size());
+ data.forEach((bp, md) ->
+ print("\tmeterId=%s bandwidthProfile=%s status=%s",
+ md.getMeterId(), bp, md.getMeterStatus()));
+
+ }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMetersCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMetersCommand.java
deleted file mode 100644
index a7dbd39..0000000
--- a/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedMetersCommand.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.meter.MeterKey;
-import org.opencord.olt.internalapi.AccessDeviceMeterService;
-
-import java.util.Set;
-
-/**
- * Shows information about device-meter mappings that have been programmed in the
- * data-plane.
- */
-@Service
-@Command(scope = "onos", name = "volt-programmed-meters",
- description = "Shows device-meter mappings programmed in the data-plane")
-public class ShowProgrammedMetersCommand extends AbstractShellCommand {
-
- @Override
- protected void doExecute() {
- AccessDeviceMeterService service = AbstractShellCommand.get(AccessDeviceMeterService.class);
- Set<MeterKey> programmedMeters = service.getProgMeters();
- programmedMeters.forEach(this::display);
- }
-
- private void display(MeterKey meterKey) {
- print("device=%s meter=%s", meterKey.deviceId(), meterKey.meterId());
- }
-}
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedSubscribersCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedSubscribersCommand.java
index 413272b..ac8d00d 100644
--- a/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedSubscribersCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowProgrammedSubscribersCommand.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,34 +16,60 @@
package org.opencord.olt.cli;
+import org.apache.karaf.shell.api.action.Argument;
import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-import org.opencord.olt.AccessDeviceService;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.opencord.olt.impl.OltFlowServiceInterface;
+import org.opencord.olt.impl.ServiceKey;
import org.opencord.sadis.UniTagInformation;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
/**
- * Shows subscriber information for those subscriber which have been programmed
- * in the data-plane.
+ * Shows programmed subscribers.
*/
@Service
@Command(scope = "onos", name = "volt-programmed-subscribers",
description = "Shows subscribers programmed in the dataplane")
public class ShowProgrammedSubscribersCommand extends AbstractShellCommand {
+ @Argument(index = 0, name = "deviceId", description = "Access device ID",
+ required = false, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ private String strDeviceId = null;
+
+ @Argument(index = 1, name = "port", description = "Subscriber port number",
+ required = false, multiValued = false)
+ @Completion(OltUniPortCompleter.class)
+ private String strPort = null;
+
@Override
protected void doExecute() {
- AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
- Map<ConnectPoint, Set<UniTagInformation>> info = service.getProgSubs();
- info.forEach(this::display);
+ OltFlowServiceInterface service = AbstractShellCommand.get(OltFlowServiceInterface.class);
+ Map<ServiceKey, UniTagInformation> info = service.getProgrammedSubscribers();
+ Set<Map.Entry<ServiceKey, UniTagInformation>> entries = info.entrySet();
+ if (strDeviceId != null && !strDeviceId.isEmpty()) {
+ entries = entries.stream().filter(entry -> entry.getKey().getPort().connectPoint().deviceId()
+ .equals(DeviceId.deviceId(strDeviceId))).collect(Collectors.toSet());
+ }
+
+ if (strPort != null && !strPort.isEmpty()) {
+ PortNumber portNumber = PortNumber.portNumber(strPort);
+ entries = entries.stream().filter(entry -> entry.getKey().getPort().connectPoint().port()
+ .equals(portNumber)).collect(Collectors.toSet());
+ }
+
+ entries.forEach(entry -> display(entry.getKey(), entry.getValue()));
}
- private void display(ConnectPoint cp, Set<UniTagInformation> uniTagInformation) {
- uniTagInformation.forEach(uniTag ->
- print("location=%s tagInformation=%s", cp, uniTag));
+ private void display(ServiceKey sk, UniTagInformation uniTag) {
+ print("location=%s tagInformation=%s", sk.getPort().connectPoint(), uniTag);
}
-}
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/ShowRequestedSubscribersCommand.java b/impl/src/main/java/org/opencord/olt/cli/ShowRequestedSubscribersCommand.java
new file mode 100644
index 0000000..37ef532
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/cli/ShowRequestedSubscribersCommand.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.opencord.olt.impl.OltFlowServiceInterface;
+import org.opencord.olt.impl.ServiceKey;
+
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Shows requested subscribers.
+ */
+@Service
+@Command(scope = "onos", name = "volt-requested-subscribers",
+ description = "Shows subscribers programmed by the operator. " +
+ "Their data-plane status depends on the ONU status.")
+public class ShowRequestedSubscribersCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "deviceId", description = "Access device ID",
+ required = false, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ private String strDeviceId = null;
+
+ @Argument(index = 1, name = "port", description = "Subscriber port number",
+ required = false, multiValued = false)
+ @Completion(OltUniPortCompleter.class)
+ private String strPort = null;
+
+ @Override
+ protected void doExecute() {
+ OltFlowServiceInterface service = AbstractShellCommand.get(OltFlowServiceInterface.class);
+ Map<ServiceKey, Boolean> info = service.getRequestedSubscribers();
+ Set<Map.Entry<ServiceKey, Boolean>> entries = info.entrySet();
+ if (strDeviceId != null && !strDeviceId.isEmpty()) {
+ entries = entries.stream().filter(entry -> entry.getKey().getPort().connectPoint().deviceId()
+ .equals(DeviceId.deviceId(strDeviceId))).collect(Collectors.toSet());
+ }
+
+ if (strPort != null && !strPort.isEmpty()) {
+ PortNumber portNumber = PortNumber.portNumber(strPort);
+ entries = entries.stream().filter(entry -> entry.getKey().getPort().connectPoint().port()
+ .equals(portNumber)).collect(Collectors.toSet());
+ }
+
+ entries.forEach(entry -> display(entry.getKey(), entry.getValue()));
+ }
+
+ private void display(ServiceKey sk, Boolean status) {
+ print("location=%s service=%s provisioned=%s", sk.getPort(), sk.getService().getServiceName(), status);
+ }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/SubscriberAddAllCommand.java b/impl/src/main/java/org/opencord/olt/cli/SubscriberAddAllCommand.java
new file mode 100644
index 0000000..2a1a3c6
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/cli/SubscriberAddAllCommand.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.cli;
+
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cli.net.DeviceIdCompleter;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.opencord.olt.AccessDeviceService;
+
+/**
+ * Adds a subscriber to an access device.
+ */
+@Service
+@Command(scope = "onos", name = "volt-add-all-subscriber-access",
+ description = "Adds a subscriber to an access device")
+public class SubscriberAddAllCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "deviceId", description = "Access device ID",
+ required = true, multiValued = false)
+ @Completion(DeviceIdCompleter.class)
+ private String strDeviceId = null;
+
+ @Override
+ protected void doExecute() {
+ AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
+ DeviceService deviceService = AbstractShellCommand.get(DeviceService.class);
+ DeviceId deviceId = DeviceId.deviceId(strDeviceId);
+
+ deviceService.getPorts(deviceId).forEach(p -> {
+ if (p.isEnabled()) {
+ PortNumber port = p.number();
+ ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
+ service.provisionSubscriber(connectPoint);
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/SubscriberAddCommand.java b/impl/src/main/java/org/opencord/olt/cli/SubscriberAddCommand.java
index de76903..5eeee4e 100644
--- a/impl/src/main/java/org/opencord/olt/cli/SubscriberAddCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/SubscriberAddCommand.java
@@ -28,7 +28,7 @@
import org.opencord.olt.AccessDeviceService;
/**
- * Adds a subscriber to an access device.
+ * Adds all possible subscribers to an access device.
*/
@Service
@Command(scope = "onos", name = "volt-add-subscriber-access",
@@ -48,11 +48,10 @@
@Override
protected void doExecute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
-
DeviceId deviceId = DeviceId.deviceId(strDeviceId);
PortNumber port = PortNumber.portNumber(strPort);
ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
service.provisionSubscriber(connectPoint);
}
-}
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/SubscriberRemoveCommand.java b/impl/src/main/java/org/opencord/olt/cli/SubscriberRemoveCommand.java
index 79a7369..cf3da93 100644
--- a/impl/src/main/java/org/opencord/olt/cli/SubscriberRemoveCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/SubscriberRemoveCommand.java
@@ -22,18 +22,17 @@
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.cli.net.PortNumberCompleter;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.opencord.olt.AccessDeviceService;
/**
- * Adds a subscriber to an access device.
+ * Removes a subscriber to an access device.
*/
@Service
@Command(scope = "onos", name = "volt-remove-subscriber-access",
- description = "Removes a subscriber to an access device")
+ description = "Remove a subscriber from an access device")
public class SubscriberRemoveCommand extends AbstractShellCommand {
@Argument(index = 0, name = "deviceId", description = "Access device ID",
@@ -43,18 +42,16 @@
@Argument(index = 1, name = "port", description = "Subscriber port number",
required = true, multiValued = false)
- @Completion(PortNumberCompleter.class)
+ @Completion(OltUniPortCompleter.class)
private String strPort = null;
@Override
protected void doExecute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
-
DeviceId deviceId = DeviceId.deviceId(strDeviceId);
PortNumber port = PortNumber.portNumber(strPort);
ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
service.removeSubscriber(connectPoint);
-
}
-}
+}
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/cli/UniTagAddCommand.java b/impl/src/main/java/org/opencord/olt/cli/UniTagAddCommand.java
index 4eb6495..bd42704 100644
--- a/impl/src/main/java/org/opencord/olt/cli/UniTagAddCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/UniTagAddCommand.java
@@ -22,10 +22,10 @@
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onlab.packet.VlanId;
import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
import org.opencord.olt.AccessDeviceService;
-import org.opencord.olt.AccessSubscriberId;
-import java.util.Optional;
+import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Adds a subscriber uni tag.
@@ -37,29 +37,39 @@
@Argument(index = 0, name = "portName", description = "Port name",
required = true, multiValued = false)
- private String strPortName = null;
+ private String portName = null;
@Option(name = "--cTag", description = "Inner vlan id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strCtag = null;
@Option(name = "--sTag", description = "Outer vlan id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strStag = null;
@Option(name = "--tpId", description = "Technology profile id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strTpId = null;
@Override
protected void doExecute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
- AccessSubscriberId portName = new AccessSubscriberId(strPortName);
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ print("ConnectPoint not found for %s", portName);
+ return;
+ }
+ if (isNullOrEmpty(strCtag) || isNullOrEmpty(strStag) || isNullOrEmpty(strTpId)) {
+ print("Values for c-tag (%s), s-tag (%s) and technology profile Id (%s) " +
+ "are required", strCtag, strStag, strTpId);
+ return;
+ }
- Optional<VlanId> cTag = strCtag == null ? Optional.empty() : Optional.of(VlanId.vlanId(strCtag));
- Optional<VlanId> sTag = strStag == null ? Optional.empty() : Optional.of(VlanId.vlanId(strStag));
- Optional<Integer> tpId = strTpId == null ? Optional.empty() : Optional.of(Integer.parseInt(strTpId));
- service.provisionSubscriber(portName, sTag, cTag, tpId);
+ VlanId cTag = VlanId.vlanId(strCtag);
+ VlanId sTag = VlanId.vlanId(strStag);
+ Integer tpId = Integer.valueOf(strTpId);
+ service.provisionSubscriber(cp, cTag, sTag, tpId);
}
}
diff --git a/impl/src/main/java/org/opencord/olt/cli/UniTagRemoveCommand.java b/impl/src/main/java/org/opencord/olt/cli/UniTagRemoveCommand.java
index e256914..d05418a 100644
--- a/impl/src/main/java/org/opencord/olt/cli/UniTagRemoveCommand.java
+++ b/impl/src/main/java/org/opencord/olt/cli/UniTagRemoveCommand.java
@@ -22,10 +22,11 @@
import org.apache.karaf.shell.api.action.lifecycle.Service;
import org.onlab.packet.VlanId;
import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
import org.opencord.olt.AccessDeviceService;
-import org.opencord.olt.AccessSubscriberId;
-import java.util.Optional;
+
+import static com.google.common.base.Strings.isNullOrEmpty;
/**
* Removes a uni tag from a subscriber (portname).
@@ -37,29 +38,39 @@
@Argument(index = 0, name = "portName", description = "Port name",
required = true, multiValued = false)
- private String strPortName = null;
+ private String portName = null;
@Option(name = "--cTag", description = "Inner vlan id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strCtag = null;
@Option(name = "--sTag", description = "Outer vlan id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strStag = null;
@Option(name = "--tpId", description = "Technology profile id",
- required = false, multiValued = false)
+ required = true, multiValued = false)
private String strTpId = null;
@Override
protected void doExecute() {
AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
- AccessSubscriberId portName = new AccessSubscriberId(strPortName);
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ print("ConnectPoint not found for %s", portName);
+ return;
+ }
+ if (isNullOrEmpty(strCtag) || isNullOrEmpty(strStag) || isNullOrEmpty(strTpId)) {
+ print("Values for c-tag (%s), s-tag (%s) and technology profile Id (%s) " +
+ "are required", strCtag, strStag, strTpId);
+ return;
+ }
- Optional<VlanId> cTag = strCtag == null ? Optional.empty() : Optional.of(VlanId.vlanId(strCtag));
- Optional<VlanId> sTag = strStag == null ? Optional.empty() : Optional.of(VlanId.vlanId(strStag));
- Optional<Integer> tpId = strTpId == null ? Optional.empty() : Optional.of(Integer.parseInt(strTpId));
- service.removeSubscriber(portName, sTag, cTag, tpId);
+ VlanId cTag = VlanId.vlanId(strCtag);
+ VlanId sTag = VlanId.vlanId(strStag);
+ Integer tpId = Integer.valueOf(strTpId);
+ service.removeSubscriber(cp, cTag, sTag, tpId);
}
}
diff --git a/impl/src/main/java/org/opencord/olt/cli/package-info.java b/impl/src/main/java/org/opencord/olt/cli/package-info.java
index b100077..c657d73 100644
--- a/impl/src/main/java/org/opencord/olt/cli/package-info.java
+++ b/impl/src/main/java/org/opencord/olt/cli/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
*/
/**
- * OLT application handling PMC OLT hardware.
+ * CLI package for OLT application.
*/
-package org.opencord.olt.cli;
+package org.opencord.olt.cli;
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/driver/NokiaOltPipeline.java b/impl/src/main/java/org/opencord/olt/driver/NokiaOltPipeline.java
deleted file mode 100644
index f8b9009..0000000
--- a/impl/src/main/java/org/opencord/olt/driver/NokiaOltPipeline.java
+++ /dev/null
@@ -1,783 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.driver;
-
-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.Lists;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.EthType;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.NextGroup;
-import org.onosproject.net.behaviour.Pipeliner;
-import org.onosproject.net.behaviour.PipelinerContext;
-import org.onosproject.net.driver.AbstractHandlerBehaviour;
-import org.onosproject.net.flow.DefaultFlowRule;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.FlowRule;
-import org.onosproject.net.flow.FlowRuleOperations;
-import org.onosproject.net.flow.FlowRuleOperationsContext;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criteria;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.EthTypeCriterion;
-import org.onosproject.net.flow.criteria.IPCriterion;
-import org.onosproject.net.flow.criteria.IPProtocolCriterion;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveStore;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.group.DefaultGroupBucket;
-import org.onosproject.net.group.DefaultGroupDescription;
-import org.onosproject.net.group.DefaultGroupKey;
-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.GroupEvent;
-import org.onosproject.net.group.GroupKey;
-import org.onosproject.net.group.GroupListener;
-import org.onosproject.net.group.GroupService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.StorageService;
-import org.slf4j.Logger;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Pipeliner for OLT device.
- */
-
-public class NokiaOltPipeline extends AbstractHandlerBehaviour implements Pipeliner {
-
- private static final Integer QQ_TABLE = 1;
- private static final short MCAST_VLAN = 4000;
- private static final String OLTCOOKIES = "olt-cookies-must-be-unique";
- private static final int EAPOL_FLOW_PRIORITY = 1200;
- private final Logger log = getLogger(getClass());
-
- private ServiceDirectory serviceDirectory;
- private FlowRuleService flowRuleService;
- private GroupService groupService;
- private CoreService coreService;
- private StorageService storageService;
-
- private DeviceId deviceId;
- private ApplicationId appId;
-
-
- protected FlowObjectiveStore flowObjectiveStore;
-
- private Cache<GroupKey, NextObjective> pendingGroups;
-
- protected static KryoNamespace appKryo = new KryoNamespace.Builder()
- .register(KryoNamespaces.API)
- .register(GroupKey.class)
- .register(DefaultGroupKey.class)
- .register(OltPipelineGroup.class)
- .build("OltPipeline");
- @Override
- public void init(DeviceId deviceId, PipelinerContext context) {
- log.debug("Initiate OLT pipeline");
- this.serviceDirectory = context.directory();
- this.deviceId = deviceId;
-
- flowRuleService = serviceDirectory.get(FlowRuleService.class);
- coreService = serviceDirectory.get(CoreService.class);
- groupService = serviceDirectory.get(GroupService.class);
- flowObjectiveStore = context.store();
- storageService = serviceDirectory.get(StorageService.class);
-
- appId = coreService.registerApplication(
- "org.onosproject.driver.OLTPipeline");
-
-
- pendingGroups = CacheBuilder.newBuilder()
- .expireAfterWrite(20, TimeUnit.SECONDS)
- .removalListener((RemovalNotification<GroupKey, NextObjective> notification) -> {
- if (notification.getCause() == RemovalCause.EXPIRED) {
- fail(notification.getValue(), ObjectiveError.GROUPINSTALLATIONFAILED);
- }
- }).build();
-
- groupService.addListener(new InnerGroupListener());
-
- }
-
- @Override
- public void filter(FilteringObjective filter) {
- Instructions.OutputInstruction output;
-
- if (filter.meta() != null && !filter.meta().immediate().isEmpty()) {
- output = (Instructions.OutputInstruction) filter.meta().immediate().stream()
- .filter(t -> t.type().equals(Instruction.Type.OUTPUT))
- .limit(1)
- .findFirst().get();
-
- if (output == null || !output.port().equals(PortNumber.CONTROLLER)) {
- log.error("OLT can only filter packet to controller");
- fail(filter, ObjectiveError.UNSUPPORTED);
- return;
- }
- } else {
- fail(filter, ObjectiveError.BADPARAMS);
- return;
- }
-
- if (filter.key().type() != Criterion.Type.IN_PORT) {
- fail(filter, ObjectiveError.BADPARAMS);
- return;
- }
-
- EthTypeCriterion ethType = (EthTypeCriterion)
- filterForCriterion(filter.conditions(), Criterion.Type.ETH_TYPE);
-
- if (ethType == null) {
- fail(filter, ObjectiveError.BADPARAMS);
- return;
- }
-
- if (ethType.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
- provisionEapol(filter, ethType, output);
- } else if (ethType.ethType().equals(EthType.EtherType.IPV4.ethType())) {
- IPProtocolCriterion ipProto = (IPProtocolCriterion)
- filterForCriterion(filter.conditions(), Criterion.Type.IP_PROTO);
- if (ipProto.protocol() == IPv4.PROTOCOL_IGMP) {
- provisionIgmp(filter, ethType, ipProto, output);
- } else {
- log.error("OLT can only filter igmp");
- fail(filter, ObjectiveError.UNSUPPORTED);
- }
- } else {
- log.error("OLT can only filter eapol and igmp");
- fail(filter, ObjectiveError.UNSUPPORTED);
- }
-
- }
-
- private void installObjective(FlowRule.Builder ruleBuilder, Objective objective) {
- FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder();
- switch (objective.op()) {
-
- case ADD:
- flowBuilder.add(ruleBuilder.build());
- break;
- case REMOVE:
- flowBuilder.remove(ruleBuilder.build());
- break;
- default:
- log.warn("Unknown operation {}", objective.op());
- }
-
- flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() {
- @Override
- public void onSuccess(FlowRuleOperations ops) {
- objective.context().ifPresent(context -> context.onSuccess(objective));
- }
-
- @Override
- public void onError(FlowRuleOperations ops) {
- objective.context()
- .ifPresent(context -> context.onError(objective, ObjectiveError.FLOWINSTALLATIONFAILED));
- }
- }));
- }
-
- @Override
- public void forward(ForwardingObjective fwd) {
-
- if (checkForMulticast(fwd)) {
- processMulticastRule(fwd);
- return;
- }
-
- if (checkForEapol(fwd)) {
- log.warn("Discarding EAPOL flow which is not supported on this pipeline");
- return;
- }
-
- TrafficTreatment treatment = fwd.treatment();
-
- List<Instruction> instructions = treatment.allInstructions();
-
- Optional<Instruction> vlanIntruction = instructions.stream()
- .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
- .filter(i -> ((L2ModificationInstruction) i).subtype() ==
- L2ModificationInstruction.L2SubType.VLAN_PUSH ||
- ((L2ModificationInstruction) i).subtype() ==
- L2ModificationInstruction.L2SubType.VLAN_POP)
- .findAny();
-
- if (vlanIntruction.isPresent()) {
- L2ModificationInstruction vlanIns =
- (L2ModificationInstruction) vlanIntruction.get();
-
- if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_PUSH) {
- installUpstreamRules(fwd);
- } else if (vlanIns.subtype() == L2ModificationInstruction.L2SubType.VLAN_POP) {
- installDownstreamRules(fwd);
- } else {
- log.error("Unknown OLT operation: {}", fwd);
- fail(fwd, ObjectiveError.UNSUPPORTED);
- return;
- }
-
- pass(fwd);
- } else {
- TrafficSelector selector = fwd.selector();
-
- if (fwd.treatment() != null) {
- // Deal with SPECIFIC and VERSATILE in the same manner.
- FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
- .forDevice(deviceId)
- .withSelector(selector)
- .fromApp(fwd.appId())
- .withPriority(fwd.priority())
- .withTreatment(fwd.treatment());
-
- if (fwd.permanent()) {
- ruleBuilder.makePermanent();
- } else {
- ruleBuilder.makeTemporary(fwd.timeout());
- }
- installObjective(ruleBuilder, fwd);
-
- } else {
- log.error("No treatment error: {}", fwd);
- fail(fwd, ObjectiveError.UNSUPPORTED);
- }
- }
-
- }
-
-
- @Override
- public void next(NextObjective nextObjective) {
- if (nextObjective.type() != NextObjective.Type.BROADCAST) {
- log.error("OLT only supports broadcast groups.");
- fail(nextObjective, ObjectiveError.BADPARAMS);
- }
-
- if (nextObjective.next().size() != 1) {
- log.error("OLT only supports singleton broadcast groups.");
- fail(nextObjective, ObjectiveError.BADPARAMS);
- }
-
- TrafficTreatment treatment = nextObjective.next().stream().findFirst().get();
-
-
- GroupBucket bucket = DefaultGroupBucket.createAllGroupBucket(treatment);
- GroupKey key = new DefaultGroupKey(appKryo.serialize(nextObjective.id()));
-
-
- pendingGroups.put(key, nextObjective);
-
- switch (nextObjective.op()) {
- case ADD:
- GroupDescription groupDesc =
- new DefaultGroupDescription(deviceId,
- GroupDescription.Type.ALL,
- new GroupBuckets(Collections.singletonList(bucket)),
- key,
- null,
- nextObjective.appId());
- groupService.addGroup(groupDesc);
- break;
- case REMOVE:
- groupService.removeGroup(deviceId, key, nextObjective.appId());
- break;
- case ADD_TO_EXISTING:
- groupService.addBucketsToGroup(deviceId, key,
- new GroupBuckets(Collections.singletonList(bucket)),
- key, nextObjective.appId());
- break;
- case REMOVE_FROM_EXISTING:
- groupService.removeBucketsFromGroup(deviceId, key,
- new GroupBuckets(Collections.singletonList(bucket)),
- key, nextObjective.appId());
- break;
- default:
- log.warn("Unknown next objective operation: {}", nextObjective.op());
- }
-
-
- }
-
- private void processMulticastRule(ForwardingObjective fwd) {
- if (fwd.nextId() == null) {
- log.error("Multicast objective does not have a next id");
- fail(fwd, ObjectiveError.BADPARAMS);
- }
-
- GroupKey key = getGroupForNextObjective(fwd.nextId());
-
- if (key == null) {
- log.error("Group for forwarding objective missing: {}", fwd);
- fail(fwd, ObjectiveError.GROUPMISSING);
- }
-
- Group group = groupService.getGroup(deviceId, key);
- TrafficTreatment treatment =
- buildTreatment(Instructions.createGroup(group.id()));
-
- FlowRule rule = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .forTable(0)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(fwd.selector())
- .withTreatment(treatment)
- .build();
-
- FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
- switch (fwd.op()) {
-
- case ADD:
- builder.add(rule);
- break;
- case REMOVE:
- builder.remove(rule);
- break;
- case ADD_TO_EXISTING:
- case REMOVE_FROM_EXISTING:
- break;
- default:
- log.warn("Unknown forwarding operation: {}", fwd.op());
- }
-
- applyFlowRules(builder, fwd);
-
- }
-
- private boolean checkForMulticast(ForwardingObjective fwd) {
-
- IPCriterion ip = (IPCriterion) filterForCriterion(fwd.selector().criteria(),
- Criterion.Type.IPV4_DST);
-
- if (ip == null) {
- return false;
- }
-
- return ip.ip().isMulticast();
-
- }
-
- private boolean checkForEapol(ForwardingObjective fwd) {
- EthTypeCriterion ethType = (EthTypeCriterion)
- filterForCriterion(fwd.selector().criteria(), Criterion.Type.ETH_TYPE);
-
- return ethType != null && ethType.ethType().equals(EthType.EtherType.EAPOL.ethType());
- }
- private GroupKey getGroupForNextObjective(Integer nextId) {
- NextGroup next = flowObjectiveStore.getNextGroup(nextId);
- return appKryo.deserialize(next.data());
-
- }
-
- private void installDownstreamRules(ForwardingObjective fwd) {
- List<Pair<Instruction, Instruction>> vlanOps =
- vlanOps(fwd,
- L2ModificationInstruction.L2SubType.VLAN_POP);
-
- if (vlanOps == null) {
- return;
- }
-
- Instructions.OutputInstruction output = (Instructions.OutputInstruction) fetchOutput(fwd, "downstream");
-
- if (output == null) {
- return;
- }
-
- Pair<Instruction, Instruction> popAndRewrite = vlanOps.remove(0);
-
- TrafficSelector selector = fwd.selector();
-
- Criterion outerVlan = selector.getCriterion(Criterion.Type.VLAN_VID);
- Criterion innerVlan = selector.getCriterion(Criterion.Type.INNER_VLAN_VID);
- Criterion inport = selector.getCriterion(Criterion.Type.IN_PORT);
- Criterion bullshit = Criteria.matchMetadata(output.port().toLong());
-
- if (outerVlan == null || innerVlan == null || inport == null) {
- log.error("Forwarding objective is underspecified: {}", fwd);
- fail(fwd, ObjectiveError.BADPARAMS);
- return;
- }
-
- Criterion innerVid = Criteria.matchVlanId(((VlanIdCriterion) innerVlan).vlanId());
-
- FlowRule.Builder outer = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(buildSelector(inport, outerVlan, bullshit))
- .withTreatment(buildTreatment(popAndRewrite.getLeft(),
- Instructions.transition(QQ_TABLE)));
-
- FlowRule.Builder inner = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .forTable(QQ_TABLE)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(buildSelector(inport, innerVid))
- .withTreatment(buildTreatment(popAndRewrite.getLeft(),
- output));
-
- applyRules(fwd, inner, outer);
-
- }
-
- private boolean hasUntaggedVlanTag(TrafficSelector selector) {
- Iterator<Criterion> iter = selector.criteria().iterator();
-
- while (iter.hasNext()) {
- Criterion criterion = iter.next();
- if (criterion.type() == Criterion.Type.VLAN_VID &&
- ((VlanIdCriterion) criterion).vlanId().toShort() == VlanId.UNTAGGED) {
- return true;
- }
- }
-
- return false;
- }
-
- private void installUpstreamRules(ForwardingObjective fwd) {
- List<Pair<Instruction, Instruction>> vlanOps =
- vlanOps(fwd,
- L2ModificationInstruction.L2SubType.VLAN_PUSH);
- FlowRule.Builder inner;
-
- if (vlanOps == null) {
- return;
- }
-
- Instruction output = fetchOutput(fwd, "upstream");
-
- if (output == null) {
- return;
- }
-
- Pair<Instruction, Instruction> innerPair = vlanOps.remove(0);
-
- Pair<Instruction, Instruction> outerPair = vlanOps.remove(0);
-
-
- if (hasUntaggedVlanTag(fwd.selector())) {
- inner = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(fwd.selector())
- .withTreatment(buildTreatment(innerPair.getLeft(),
- innerPair.getRight(),
- Instructions.transition(QQ_TABLE)));
- } else {
- inner = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(fwd.selector())
- .withTreatment(buildTreatment(
- innerPair.getRight(),
- Instructions.transition(QQ_TABLE)));
- }
-
-
- PortCriterion inPort = (PortCriterion)
- fwd.selector().getCriterion(Criterion.Type.IN_PORT);
-
- VlanId cVlanId = ((L2ModificationInstruction.ModVlanIdInstruction)
- innerPair.getRight()).vlanId();
-
- FlowRule.Builder outer = DefaultFlowRule.builder()
- .fromApp(fwd.appId())
- .forDevice(deviceId)
- .forTable(QQ_TABLE)
- .makePermanent()
- .withPriority(fwd.priority())
- .withSelector(buildSelector(inPort,
- Criteria.matchVlanId(cVlanId)))
- .withTreatment(buildTreatment(outerPair.getLeft(),
- outerPair.getRight(),
- output));
-
- applyRules(fwd, inner, outer);
-
- }
-
- private Instruction fetchOutput(ForwardingObjective fwd, String direction) {
- Instruction output = fwd.treatment().allInstructions().stream()
- .filter(i -> i.type() == Instruction.Type.OUTPUT)
- .findFirst().orElse(null);
-
- if (output == null) {
- log.error("OLT {} rule has no output", direction);
- fail(fwd, ObjectiveError.BADPARAMS);
- return null;
- }
- return output;
- }
-
- private List<Pair<Instruction, Instruction>> vlanOps(ForwardingObjective fwd,
- L2ModificationInstruction.L2SubType type) {
-
- List<Pair<Instruction, Instruction>> vlanOps = findVlanOps(
- fwd.treatment().allInstructions(), type);
-
- if (vlanOps == null) {
- String direction = type == L2ModificationInstruction.L2SubType.VLAN_POP
- ? "downstream" : "upstream";
- log.error("Missing vlan operations in {} forwarding: {}", direction, fwd);
- fail(fwd, ObjectiveError.BADPARAMS);
- return null;
- }
- return vlanOps;
- }
-
-
- private List<Pair<Instruction, Instruction>> findVlanOps(List<Instruction> instructions,
- L2ModificationInstruction.L2SubType type) {
-
- List<Instruction> vlanPushs = findL2Instructions(
- type,
- instructions);
- List<Instruction> vlanSets = findL2Instructions(
- L2ModificationInstruction.L2SubType.VLAN_ID,
- instructions);
-
- if (vlanPushs.size() != vlanSets.size()) {
- return null;
- }
-
- List<Pair<Instruction, Instruction>> pairs = Lists.newArrayList();
-
- for (int i = 0; i < vlanPushs.size(); i++) {
- pairs.add(new ImmutablePair<>(vlanPushs.get(i), vlanSets.get(i)));
- }
- return pairs;
- }
-
- private List<Instruction> findL2Instructions(L2ModificationInstruction.L2SubType subType,
- List<Instruction> actions) {
- return actions.stream()
- .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
- .filter(i -> ((L2ModificationInstruction) i).subtype() == subType)
- .collect(Collectors.toList());
- }
-
- private void provisionEapol(FilteringObjective filter,
- EthTypeCriterion ethType,
- Instructions.OutputInstruction output) {
-
- TrafficSelector selector = buildSelector(filter.key(), ethType);
- TrafficTreatment treatment = buildTreatment(output);
- buildAndApplyRule(filter, selector, treatment, EAPOL_FLOW_PRIORITY);
-
- }
-
- private void provisionIgmp(FilteringObjective filter, EthTypeCriterion ethType,
- IPProtocolCriterion ipProto,
- Instructions.OutputInstruction output) {
- TrafficSelector selector = buildSelector(filter.key(), ethType, ipProto);
- TrafficTreatment treatment = buildTreatment(output);
- buildAndApplyRule(filter, selector, treatment);
- }
-
- private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
- TrafficTreatment treatment) {
- buildAndApplyRule(filter, selector, treatment, filter.priority());
- }
-
- private void buildAndApplyRule(FilteringObjective filter, TrafficSelector selector,
- TrafficTreatment treatment, int priority) {
- FlowRule rule = DefaultFlowRule.builder()
- .fromApp(filter.appId())
- .forDevice(deviceId)
- .forTable(0)
- .makePermanent()
- .withSelector(selector)
- .withTreatment(treatment)
- .withPriority(priority)
- .build();
-
- FlowRuleOperations.Builder opsBuilder = FlowRuleOperations.builder();
-
- switch (filter.type()) {
- case PERMIT:
- opsBuilder.add(rule);
- break;
- case DENY:
- opsBuilder.remove(rule);
- break;
- default:
- log.warn("Unknown filter type : {}", filter.type());
- fail(filter, ObjectiveError.UNSUPPORTED);
- }
-
- applyFlowRules(opsBuilder, filter);
- }
-
- private void applyRules(ForwardingObjective fwd,
- FlowRule.Builder inner, FlowRule.Builder outer) {
- FlowRuleOperations.Builder builder = FlowRuleOperations.builder();
- switch (fwd.op()) {
- case ADD:
- builder.add(inner.build()).add(outer.build());
- break;
- case REMOVE:
- builder.remove(inner.build()).remove(outer.build());
- break;
- case ADD_TO_EXISTING:
- break;
- case REMOVE_FROM_EXISTING:
- break;
- default:
- log.warn("Unknown forwarding operation: {}", fwd.op());
- }
-
- applyFlowRules(builder, fwd);
- }
-
- private void applyFlowRules(FlowRuleOperations.Builder builder,
- Objective objective) {
- flowRuleService.apply(builder.build(new FlowRuleOperationsContext() {
- @Override
- public void onSuccess(FlowRuleOperations ops) {
- pass(objective);
- }
-
- @Override
- public void onError(FlowRuleOperations ops) {
- fail(objective, ObjectiveError.FLOWINSTALLATIONFAILED);
- }
- }));
- }
-
- private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
- return criteria.stream()
- .filter(c -> c.type().equals(type))
- .limit(1)
- .findFirst().orElse(null);
- }
-
- private TrafficSelector buildSelector(Criterion... criteria) {
-
-
- TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-
- for (Criterion c : criteria) {
- sBuilder.add(c);
- }
-
- return sBuilder.build();
- }
-
- private TrafficTreatment buildTreatment(Instruction... instructions) {
-
-
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
- for (Instruction i : instructions) {
- tBuilder.add(i);
- }
-
- return tBuilder.build();
- }
-
-
- private void fail(Objective obj, ObjectiveError error) {
- obj.context().ifPresent(context -> context.onError(obj, error));
- }
-
- private void pass(Objective obj) {
- obj.context().ifPresent(context -> context.onSuccess(obj));
- }
-
-
- private class InnerGroupListener implements GroupListener {
- @Override
- public void event(GroupEvent event) {
- if (event.type() == GroupEvent.Type.GROUP_ADDED || event.type() == GroupEvent.Type.GROUP_UPDATED) {
- GroupKey key = event.subject().appCookie();
-
- NextObjective obj = pendingGroups.getIfPresent(key);
- if (obj != null) {
- flowObjectiveStore.putNextGroup(obj.id(), new OltPipelineGroup(key));
- pass(obj);
- pendingGroups.invalidate(key);
- }
- }
- }
- }
-
- private static class OltPipelineGroup implements NextGroup {
-
- private final GroupKey key;
-
- public OltPipelineGroup(GroupKey key) {
- this.key = key;
- }
-
- public GroupKey key() {
- return key;
- }
-
- @Override
- public byte[] data() {
- return appKryo.serialize(key);
- }
-
- }
-
- @Override
- public List<String> getNextMappings(NextGroup nextGroup) {
- // TODO Implementation deferred to vendor
- return null;
- }
-}
diff --git a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
index 459526c..29d234b 100644
--- a/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
+++ b/impl/src/main/java/org/opencord/olt/driver/OltPipeline.java
@@ -1152,7 +1152,9 @@
// Builds the batch using the accumulated flow rules
private void sendFilters(List<Pair<FilteringObjective, FlowRule>> pairs) {
FlowRuleOperations.Builder flowOpsBuilder = FlowRuleOperations.builder();
- log.debug("Sending batch of {} filter-objs", pairs.size());
+ if (log.isDebugEnabled()) {
+ log.debug("Sending batch of {} filter-objs", pairs.size());
+ }
List<Objective> filterObjs = Lists.newArrayList();
// Iterates over all accumulated flow rules and then build an unique batch
pairs.forEach(pair -> {
@@ -1161,12 +1163,16 @@
switch (filter.type()) {
case PERMIT:
flowOpsBuilder.add(rule);
- log.debug("Applying add filter-obj {} to device: {}", filter.id(), deviceId);
+ if (log.isTraceEnabled()) {
+ log.trace("Applying add filter-obj {} to device: {}", filter.id(), deviceId);
+ }
filterObjs.add(filter);
break;
case DENY:
flowOpsBuilder.remove(rule);
- log.debug("Deleting flow rule {} to device: {}", rule, deviceId);
+ if (log.isTraceEnabled()) {
+ log.trace("Deleting flow rule {} from device: {}", rule, deviceId);
+ }
filterObjs.add(filter);
break;
default:
diff --git a/impl/src/main/java/org/opencord/olt/impl/AccessDevicePort.java b/impl/src/main/java/org/opencord/olt/impl/AccessDevicePort.java
new file mode 100644
index 0000000..b6d2213
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/AccessDevicePort.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Port;
+
+import java.util.Objects;
+
+/**
+ * OLT device port.
+ */
+public class AccessDevicePort {
+
+ private ConnectPoint cp;
+ private String name;
+
+ /**
+ * Creates an AccessDevicePort with given ONOS port.
+ *
+ * @param port ONOS port
+ */
+ public AccessDevicePort(Port port) {
+ this.cp = ConnectPoint.deviceConnectPoint(port.element().id() + "/" + port.number().toLong());
+ this.name = OltUtils.getPortName(port);
+ }
+
+ /**
+ * Creates an AccessDevicePort with given ONOS connectPoint and name.
+ *
+ * @param cp ONOS connect point
+ * @param name OLT port name
+ */
+ public AccessDevicePort(ConnectPoint cp, String name) {
+ this.cp = cp;
+ this.name = name;
+ }
+
+ /**
+ * Get ONOS ConnectPoint object.
+ *
+ * @return ONOS connect point
+ */
+ public ConnectPoint connectPoint() {
+ return this.cp;
+ }
+
+ /**
+ * Get OLT port name which is combination of serial number and uni index.
+ *
+ * @return OLT port name (ex: BBSM00010001-1)
+ */
+ public String name() {
+ return this.name;
+ }
+
+ @Override
+ public String toString() {
+ return cp.toString() + '[' + name + ']';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ AccessDevicePort that = (AccessDevicePort) o;
+ return Objects.equals(cp, that.cp) &&
+ Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(cp, name);
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/ConsistentHasher.java b/impl/src/main/java/org/opencord/olt/impl/ConsistentHasher.java
deleted file mode 100644
index 52e9b96..0000000
--- a/impl/src/main/java/org/opencord/olt/impl/ConsistentHasher.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import com.google.common.hash.Hashing;
-import org.onosproject.cluster.NodeId;
-
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Returns a server hosting a given key based on consistent hashing.
- */
-public class ConsistentHasher {
-
- private static class Entry implements Comparable<Entry> {
- private final NodeId server;
- private final int hash;
-
- public Entry(NodeId server, int hash) {
- this.server = server;
- this.hash = hash;
- }
-
- public Entry(int hash) {
- server = null;
- this.hash = hash;
- }
-
- @Override
- public int compareTo(Entry o) {
- if (this.hash > o.hash) {
- return 1;
- } else if (this.hash < o.hash) {
- return -1;
- } // else
- return 0;
- }
- }
-
- private final int weight;
-
- private List<Entry> table;
-
- /**
- * Creates a new hasher with the given list of servers.
- *
- * @param servers list of servers
- * @param weight weight
- */
- public ConsistentHasher(List<NodeId> servers, int weight) {
- this.weight = weight;
-
- this.table = new ArrayList<>();
-
- servers.forEach(this::addServer);
- }
-
- /**
- * Adds a new server to the list of servers.
- *
- * @param server server ID
- */
- public synchronized void addServer(NodeId server) {
- // Add weight number of buckets for each server
- for (int i = 0; i < weight; i++) {
- String label = server.toString() + i;
- int hash = getHash(label);
- Entry e = new Entry(server, hash);
- table.add(e);
- }
-
- Collections.sort(table);
- }
-
- /**
- * Removes a server from the list of servers.
- *
- * @param server server ID
- */
- public synchronized void removeServer(NodeId server) {
- table.removeIf(e -> e.server.equals(server));
- }
-
- /**
- * Hashes a given input string and finds that server that should
- * handle the given ID.
- *
- * @param s input
- * @return server ID
- */
- public synchronized NodeId hash(String s) {
- Entry temp = new Entry(getHash(s));
-
- int pos = Collections.binarySearch(this.table, temp);
-
- // translate a negative not-found result into the closest following match
- if (pos < 0) {
- pos = Math.abs(pos + 1);
- }
-
- // wraparound if the hash was after the last entry in the table
- if (pos == this.table.size()) {
- pos = 0;
- }
-
- return table.get(pos).server;
- }
-
- private int getHash(String s) {
- // stable, uniformly-distributed hash
- return Hashing.murmur3_128().hashString(s, Charset.defaultCharset()).asInt();
- }
-}
diff --git a/impl/src/main/java/org/opencord/olt/impl/DiscoveredSubscriber.java b/impl/src/main/java/org/opencord/olt/impl/DiscoveredSubscriber.java
new file mode 100644
index 0000000..4280eaf
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/DiscoveredSubscriber.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+
+import java.util.Objects;
+
+import static org.opencord.olt.impl.OltUtils.portWithName;
+
+/**
+ * Contains a subscriber's information and status for a specific device and port.
+ */
+public class DiscoveredSubscriber {
+
+ /**
+ * Describe whether the subscriber needs to be added or removed.
+ */
+ public enum Status {
+ ADDED,
+ REMOVED,
+ }
+
+ public Port port;
+ public Device device;
+ public Enum<Status> status;
+ public boolean hasSubscriber;
+ public SubscriberAndDeviceInformation subscriberAndDeviceInformation;
+
+ /**
+ * Creates the class with the proper information.
+ *
+ * @param device the device of the subscriber
+ * @param port the port
+ * @param status the status for this specific subscriber
+ * @param hasSubscriber is the subscriber present
+ * @param si the information about the tags/dhcp and other info.
+ */
+ public DiscoveredSubscriber(Device device, Port port, Status status, boolean hasSubscriber,
+ SubscriberAndDeviceInformation si) {
+ this.device = device;
+ this.port = port;
+ this.status = status;
+ this.hasSubscriber = hasSubscriber;
+ subscriberAndDeviceInformation = si;
+ }
+
+ /**
+ * Returns the port name for the subscriber.
+ *
+ * @return the port name.
+ */
+ public String portName() {
+ return OltUtils.getPortName(port);
+ }
+
+ @Override
+ public String toString() {
+
+ return String.format("%s (status: %s, provisionSubscriber: %s)",
+ portWithName(this.port),
+ this.status, this.hasSubscriber
+ );
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ DiscoveredSubscriber that = (DiscoveredSubscriber) o;
+ return hasSubscriber == that.hasSubscriber &&
+ port.equals(that.port) &&
+ device.equals(that.device) &&
+ status.equals(that.status);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(port, device, status, hasSubscriber, subscriberAndDeviceInformation);
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/MeterData.java b/impl/src/main/java/org/opencord/olt/impl/MeterData.java
new file mode 100644
index 0000000..208383d
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/MeterData.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.meter.MeterCellId;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterState;
+
+import java.util.Objects;
+
+/**
+ * Class containing Meter Data.
+ */
+public class MeterData {
+ private MeterCellId meterCellId;
+ private MeterState meterStatus;
+ private String bandwidthProfileName;
+
+ public MeterData(MeterCellId meterCellId, MeterState meterStatus, String bandwidthProfile) {
+ this.meterCellId = meterCellId;
+ this.meterStatus = meterStatus;
+ this.bandwidthProfileName = bandwidthProfile;
+ }
+
+ public void setMeterCellId(MeterCellId meterCellId) {
+ this.meterCellId = meterCellId;
+ }
+
+ public void setMeterStatus(MeterState meterStatus) {
+ this.meterStatus = meterStatus;
+ }
+
+ public MeterId getMeterId() {
+ return (MeterId) meterCellId;
+ }
+
+ public MeterCellId getMeterCellId() {
+ return meterCellId;
+ }
+
+ public MeterState getMeterStatus() {
+ return meterStatus;
+ }
+
+ public String getBandwidthProfileName() {
+ return bandwidthProfileName;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ MeterData meterData = (MeterData) o;
+ return Objects.equals(meterCellId, meterData.meterCellId) &&
+ meterStatus == meterData.meterStatus &&
+ Objects.equals(bandwidthProfileName, meterData.bandwidthProfileName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(meterCellId, meterStatus, bandwidthProfileName);
+ }
+
+ @Override
+ public String toString() {
+ return "MeterData{" +
+ "meterCellId=" + meterCellId +
+ ", meterStatus=" + meterStatus +
+ ", bandwidthProfile='" + bandwidthProfileName + '\'' +
+ '}';
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/Olt.java b/impl/src/main/java/org/opencord/olt/impl/Olt.java
index 74c5811..747a0b4 100644
--- a/impl/src/main/java/org/opencord/olt/impl/Olt.java
+++ b/impl/src/main/java/org/opencord/olt/impl/Olt.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,18 +15,11 @@
*/
package org.opencord.olt.impl;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onlab.util.KryoNamespace;
import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterEvent;
-import org.onosproject.cluster.ClusterEventListener;
import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.ControllerNode;
import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.AbstractListenerManager;
@@ -35,36 +28,17 @@
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
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.DeviceService;
-import org.onosproject.net.driver.DriverService;
-import org.onosproject.net.flow.FlowRuleService;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.meter.MeterId;
-import org.onosproject.store.primitives.DefaultConsistentMap;
import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMultimap;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
import org.opencord.olt.AccessDeviceEvent;
import org.opencord.olt.AccessDeviceListener;
-import org.opencord.olt.AccessDevicePort;
import org.opencord.olt.AccessDeviceService;
-import org.opencord.olt.AccessSubscriberId;
-import org.opencord.olt.internalapi.AccessDeviceFlowService;
-import org.opencord.olt.internalapi.AccessDeviceMeterService;
-import org.opencord.sadis.BandwidthProfileInformation;
import org.opencord.sadis.BaseInformationService;
import org.opencord.sadis.SadisService;
import org.opencord.sadis.SubscriberAndDeviceInformation;
@@ -78,73 +52,60 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Dictionary;
+import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Optional;
import java.util.Properties;
import java.util.Set;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
-import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
-import static java.util.stream.Collectors.*;
import static org.onlab.util.Tools.get;
import static org.onlab.util.Tools.groupedThreads;
+import static org.opencord.olt.impl.OltUtils.getPortName;
+import static org.opencord.olt.impl.OltUtils.portWithName;
import static org.opencord.olt.impl.OsgiPropertyConstants.*;
-import static org.slf4j.LoggerFactory.getLogger;
/**
- * Provisions rules on access devices.
+ * OLT Application.
*/
@Component(immediate = true,
property = {
DEFAULT_BP_ID + ":String=" + DEFAULT_BP_ID_DEFAULT,
DEFAULT_MCAST_SERVICE_NAME + ":String=" + DEFAULT_MCAST_SERVICE_NAME_DEFAULT,
- EAPOL_DELETE_RETRY_MAX_ATTEMPS + ":Integer=" +
- EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT,
- PROVISION_DELAY + ":Integer=" + PROVISION_DELAY_DEFAULT,
+ FLOW_PROCESSING_THREADS + ":Integer=" + FLOW_PROCESSING_THREADS_DEFAULT,
+ SUBSCRIBER_PROCESSING_THREADS + ":Integer=" + SUBSCRIBER_PROCESSING_THREADS_DEFAULT,
+ REQUEUE_DELAY + ":Integer=" + REQUEUE_DELAY_DEFAULT
})
public class Olt
extends AbstractListenerManager<AccessDeviceEvent, AccessDeviceListener>
implements AccessDeviceService {
- private static final String SADIS_NOT_RUNNING = "Sadis is not running.";
- private static final String APP_NAME = "org.opencord.olt";
-
- private static final short EAPOL_DEFAULT_VLAN = 4091;
- private static final String NO_UPLINK_PORT = "No uplink port found for OLT device {}";
-
- public static final int HASH_WEIGHT = 10;
- public static final long PENDING_SUBS_MAP_TIMEOUT_MILLIS = 30000L;
-
- private final Logger log = getLogger(getClass());
-
- private static final String NNI = "nni-";
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ComponentConfigService cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected CoreService coreService;
+ protected MastershipService mastershipService;
- //Dependency on driver service is to ensure correct startup order
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected DriverService driverService;
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
@Reference(cardinality = ReferenceCardinality.OPTIONAL,
bind = "bindSadisService",
@@ -153,31 +114,23 @@
protected volatile SadisService sadisService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected AccessDeviceFlowService oltFlowService;
+ protected OltDeviceServiceInterface oltDeviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected AccessDeviceMeterService oltMeterService;
+ protected OltFlowServiceInterface oltFlowService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected OltMeterServiceInterface oltMeterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected StorageService storageService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected ClusterService clusterService;
+ protected CoreService coreService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected MastershipService mastershipService;
+ protected ApplicationId appId;
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected LeadershipService leadershipService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected FlowRuleService flowRuleService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected ComponentConfigService componentConfigService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected HostService hostService;
+ private static final String ONOS_OLT_SERVICE = "onos/olt-service";
/**
* Default bandwidth profile id that is used for authentication trap flows.
@@ -190,456 +143,322 @@
protected String multicastServiceName = DEFAULT_MCAST_SERVICE_NAME_DEFAULT;
/**
- * Default amounts of eapol retry.
+ * Number of threads used to process flows.
**/
- protected int eapolDeleteRetryMaxAttempts = EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT;
+ protected int flowProcessingThreads = FLOW_PROCESSING_THREADS_DEFAULT;
/**
- * Delay between EAPOL removal and data plane flows provisioning.
+ * Number of threads used to process flows.
+ **/
+ protected int subscriberProcessingThreads = SUBSCRIBER_PROCESSING_THREADS_DEFAULT;
+
+ /**
+ * Delay in ms to put an event back in the queue, used to avoid retrying things to often if conditions are not met.
+ **/
+ protected int requeueDelay = REQUEUE_DELAY_DEFAULT;
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ /**
+ * A queue to asynchronously process events.
*/
- protected int provisionDelay = PROVISION_DELAY_DEFAULT;
-
- private final DeviceListener deviceListener = new InternalDeviceListener();
- private final ClusterEventListener clusterListener = new InternalClusterListener();
- private final HostListener hostListener = new InternalHostListener();
-
- private ConsistentHasher hasher;
+ protected Map<ConnectPoint, LinkedBlockingQueue<DiscoveredSubscriber>> eventsQueues;
protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
- private BaseInformationService<BandwidthProfileInformation> bpService;
- private ExecutorService oltInstallers = Executors.newFixedThreadPool(4,
- groupedThreads("onos/olt-service",
- "olt-installer-%d"));
+ /**
+ * Listener for OLT devices events.
+ */
+ protected OltDeviceListener deviceListener = new OltDeviceListener();
+ protected ScheduledExecutorService discoveredSubscriberExecutor =
+ Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
+ "discovered-cp-%d", log));
- protected ExecutorService eventExecutor;
- protected ExecutorService hostEventExecutor;
- protected ExecutorService retryExecutor;
- protected ScheduledExecutorService provisionExecutor;
+ protected ScheduledExecutorService queueExecutor =
+ Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
+ "discovered-cp-restore-%d", log));
- private ConsistentMultimap<ConnectPoint, UniTagInformation> programmedSubs;
- private ConsistentMultimap<ConnectPoint, UniTagInformation> failedSubs;
+ /**
+ * Executor used to defer flow provisioning to a different thread pool.
+ */
+ protected ExecutorService flowsExecutor;
- protected Map<DeviceId, BlockingQueue<SubscriberFlowInfo>> pendingSubscribersForDevice;
- private ConsistentMultimap<ConnectPoint, SubscriberFlowInfo> waitingMacSubscribers;
+ /**
+ * Executor used to defer subscriber handling from API call to a different thread pool.
+ */
+ protected ExecutorService subscriberExecutor;
+
+ private static final String APP_NAME = "org.opencord.olt";
+
+ private final ReentrantReadWriteLock queueLock = new ReentrantReadWriteLock();
+ private final Lock queueWriteLock = queueLock.writeLock();
+ private final Lock queueReadLock = queueLock.readLock();
@Activate
- public void activate(ComponentContext context) {
- eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
- "events-%d", log));
- hostEventExecutor = Executors.newFixedThreadPool(8, groupedThreads("onos/olt", "mac-events-%d", log));
- retryExecutor = Executors.newCachedThreadPool();
- provisionExecutor = Executors.newSingleThreadScheduledExecutor(groupedThreads("onos/olt",
- "provision-%d", log));
+ protected void activate(ComponentContext context) {
+ cfgService.registerProperties(getClass());
modified(context);
- ApplicationId appId = coreService.registerApplication(APP_NAME);
- componentConfigService.registerProperties(getClass());
+ appId = coreService.registerApplication(APP_NAME);
KryoNamespace serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
+ .register(ConnectPoint.class)
+ .register(DiscoveredSubscriber.class)
+ .register(DiscoveredSubscriber.Status.class)
+ .register(SubscriberAndDeviceInformation.class)
.register(UniTagInformation.class)
- .register(SubscriberFlowInfo.class)
- .register(AccessDevicePort.class)
- .register(AccessDevicePort.Type.class)
.register(LinkedBlockingQueue.class)
.build();
- programmedSubs = storageService.<ConnectPoint, UniTagInformation>consistentMultimapBuilder()
- .withName("volt-programmed-subs")
+ eventsQueues = storageService.<ConnectPoint, LinkedBlockingQueue<DiscoveredSubscriber>>consistentMapBuilder()
+ .withName("volt-subscriber-queues")
+ .withApplicationId(appId)
.withSerializer(Serializer.using(serializer))
- .withApplicationId(appId)
- .build();
-
- failedSubs = storageService.<ConnectPoint, UniTagInformation>consistentMultimapBuilder()
- .withName("volt-failed-subs")
- .withSerializer(Serializer.using(serializer))
- .withApplicationId(appId)
- .build();
-
- KryoNamespace macSerializer = KryoNamespace.newBuilder()
- .register(KryoNamespaces.API)
- .register(SubscriberFlowInfo.class)
- .register(AccessDevicePort.class)
- .register(AccessDevicePort.Type.class)
- .register(UniTagInformation.class)
- .build();
-
- waitingMacSubscribers = storageService.<ConnectPoint, SubscriberFlowInfo>consistentMultimapBuilder()
- .withName("volt-waiting-mac-subs")
- .withSerializer(Serializer.using(macSerializer))
- .withApplicationId(appId)
- .build();
- //TODO possibly use consistent multimap with compute on key and element to avoid
- // lock on all objects of the map, while instead obtaining and releasing the lock
- // on a per subscriber basis.
- pendingSubscribersForDevice = new DefaultConsistentMap<>(
- storageService.<DeviceId, BlockingQueue<SubscriberFlowInfo>>consistentMapBuilder()
- .withName("volt-pending-subs")
- .withSerializer(Serializer.using(serializer))
- .withApplicationId(appId)
- .buildAsyncMap(), PENDING_SUBS_MAP_TIMEOUT_MILLIS).asJavaMap();
- eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
-
- if (sadisService != null) {
- subsService = sadisService.getSubscriberInfoService();
- bpService = sadisService.getBandwidthProfileService();
- } else {
- log.warn(SADIS_NOT_RUNNING);
- }
-
- List<NodeId> readyNodes = clusterService.getNodes().stream()
- .filter(c -> clusterService.getState(c.id()) == ControllerNode.State.READY)
- .map(ControllerNode::id)
- .collect(toList());
- hasher = new ConsistentHasher(readyNodes, HASH_WEIGHT);
- clusterService.addListener(clusterListener);
-
- // look for all provisioned devices in Sadis and create EAPOL flows for the
- // UNI ports
- Iterable<Device> devices = deviceService.getDevices();
- for (Device d : devices) {
- if (isLocalLeader(d.id())) {
- checkAndCreateDeviceFlows(d);
- }
- }
+ .build().asJavaMap();
deviceService.addListener(deviceListener);
- hostService.addListener(hostListener);
- log.info("Started with Application ID {}", appId.id());
+
+ discoveredSubscriberExecutor.execute(this::processDiscoveredSubscribers);
+ eventDispatcher.addSink(AccessDeviceEvent.class, listenerRegistry);
+ log.info("Started");
+
+ deviceListener.handleExistingPorts();
}
@Deactivate
- public void deactivate() {
- componentConfigService.unregisterProperties(getClass(), false);
- clusterService.removeListener(clusterListener);
- deviceService.removeListener(deviceListener);
- hostService.removeListener(hostListener);
- eventDispatcher.removeSink(AccessDeviceEvent.class);
- eventExecutor.shutdown();
- hostEventExecutor.shutdown();
- retryExecutor.shutdown();
- provisionExecutor.shutdown();
+ protected void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ discoveredSubscriberExecutor.shutdown();
+ flowsExecutor.shutdown();
+ subscriberExecutor.shutdown();
+ deviceListener.deactivate();
log.info("Stopped");
}
@Modified
public void modified(ComponentContext context) {
Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
-
- try {
+ if (context != null) {
String bpId = get(properties, DEFAULT_BP_ID);
defaultBpId = isNullOrEmpty(bpId) ? defaultBpId : bpId;
String mcastSN = get(properties, DEFAULT_MCAST_SERVICE_NAME);
multicastServiceName = isNullOrEmpty(mcastSN) ? multicastServiceName : mcastSN;
- String eapolDeleteRetryNew = get(properties, EAPOL_DELETE_RETRY_MAX_ATTEMPS);
- eapolDeleteRetryMaxAttempts = isNullOrEmpty(eapolDeleteRetryNew) ? EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT :
- Integer.parseInt(eapolDeleteRetryNew.trim());
+ String flowThreads = get(properties, FLOW_PROCESSING_THREADS);
+ int oldFlowProcessingThreads = flowProcessingThreads;
+ flowProcessingThreads = isNullOrEmpty(flowThreads) ?
+ oldFlowProcessingThreads : Integer.parseInt(flowThreads.trim());
- log.debug("OLT properties: DefaultBpId: {}, MulticastServiceName: {}, EapolDeleteRetryMaxAttempts: {}",
- defaultBpId, multicastServiceName, eapolDeleteRetryMaxAttempts);
+ if (flowsExecutor == null || oldFlowProcessingThreads != flowProcessingThreads) {
+ if (flowsExecutor != null) {
+ flowsExecutor.shutdown();
+ }
+ flowsExecutor = Executors.newFixedThreadPool(flowProcessingThreads,
+ groupedThreads(ONOS_OLT_SERVICE,
+ "flows-installer-%d"));
+ }
- } catch (Exception e) {
- log.error("Error while modifying the properties", e);
- defaultBpId = DEFAULT_BP_ID_DEFAULT;
- multicastServiceName = DEFAULT_MCAST_SERVICE_NAME_DEFAULT;
+ String subscriberThreads = get(properties, SUBSCRIBER_PROCESSING_THREADS);
+ int oldSubscriberProcessingThreads = subscriberProcessingThreads;
+ subscriberProcessingThreads = isNullOrEmpty(subscriberThreads) ?
+ oldSubscriberProcessingThreads : Integer.parseInt(subscriberThreads.trim());
+
+ if (subscriberExecutor == null || oldSubscriberProcessingThreads != subscriberProcessingThreads) {
+ if (subscriberExecutor != null) {
+ subscriberExecutor.shutdown();
+ }
+ subscriberExecutor = Executors.newFixedThreadPool(subscriberProcessingThreads,
+ groupedThreads(ONOS_OLT_SERVICE,
+ "subscriber-installer-%d"));
+ }
+
+ String queueDelay = get(properties, REQUEUE_DELAY);
+ requeueDelay = isNullOrEmpty(queueDelay) ?
+ REQUEUE_DELAY_DEFAULT : Integer.parseInt(queueDelay.trim());
}
+ log.info("Modified. Values = {}: {}, {}: {}, " +
+ "{}:{}, {}:{}, {}:{}",
+ DEFAULT_BP_ID, defaultBpId,
+ DEFAULT_MCAST_SERVICE_NAME, multicastServiceName,
+ FLOW_PROCESSING_THREADS, flowProcessingThreads,
+ SUBSCRIBER_PROCESSING_THREADS, subscriberProcessingThreads,
+ REQUEUE_DELAY, requeueDelay);
}
- protected void bindSadisService(SadisService service) {
- sadisService = service;
- bpService = sadisService.getBandwidthProfileService();
- subsService = sadisService.getSubscriberInfoService();
- log.info("Sadis-service binds to onos.");
- }
-
- protected void unbindSadisService(SadisService service) {
- sadisService = null;
- bpService = null;
- subsService = null;
- log.info("Sadis-service unbinds from onos.");
- }
@Override
- public boolean provisionSubscriber(ConnectPoint connectPoint) {
- log.info("Call to provision subscriber at {}", connectPoint);
- DeviceId deviceId = connectPoint.deviceId();
- Port subscriberPortOnos = deviceService.getPort(deviceId, connectPoint.port());
- checkNotNull(subscriberPortOnos, "Invalid connect point:" + connectPoint);
- AccessDevicePort subscriberPort = new AccessDevicePort(subscriberPortOnos, AccessDevicePort.Type.UNI);
+ public boolean provisionSubscriber(ConnectPoint cp) {
+ subscriberExecutor.submit(() -> {
+ Device device = deviceService.getDevice(cp.deviceId());
+ Port port = deviceService.getPort(device.id(), cp.port());
+ AccessDevicePort accessDevicePort = new AccessDevicePort(port);
- if (isSubscriberInstalled(connectPoint)) {
- log.warn("Subscriber at {} already provisioned or in the process .."
- + " not taking any more action", connectPoint);
- return true;
- }
-
- // Find the subscriber config at this connect point
- SubscriberAndDeviceInformation sub = getSubscriber(connectPoint);
- if (sub == null) {
- log.warn("No subscriber found for {}", connectPoint);
- return false;
- }
-
- // Get the uplink port
- AccessDevicePort uplinkPort = getUplinkPort(deviceService.getDevice(deviceId));
- if (uplinkPort == null) {
- log.warn(NO_UPLINK_PORT, deviceId);
- return false;
- }
-
- // delete Eapol authentication flow with default bandwidth
- // wait until Eapol rule with defaultBpId is removed to install subscriber-based rules
- // retry deletion if it fails/times-out
- retryExecutor.execute(new DeleteEapolInstallSub(subscriberPort, uplinkPort, sub, 1));
- return true;
- }
-
- // returns true if subscriber is programmed or in the process of being programmed
- private boolean isSubscriberInstalled(ConnectPoint connectPoint) {
- Collection<? extends UniTagInformation> uniTagInformationSet =
- programmedSubs.get(connectPoint).value();
- if (!uniTagInformationSet.isEmpty()) {
- return true;
- }
- //Check if the subscriber is already getting provisioned
- // so we do not provision twice
- AtomicBoolean isPending = new AtomicBoolean(false);
- pendingSubscribersForDevice.computeIfPresent(connectPoint.deviceId(), (id, infos) -> {
- for (SubscriberFlowInfo fi : infos) {
- if (fi.getUniPort().equals(connectPoint.port())) {
- log.debug("Subscriber is already pending, {}", fi);
- isPending.set(true);
- break;
- }
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ log.warn("will not provision a subscriber on the NNI {}", accessDevicePort);
+ return false;
}
- return infos;
+
+ log.info("Provisioning subscriber on {}", accessDevicePort);
+
+ if (oltFlowService.isSubscriberServiceProvisioned(accessDevicePort)) {
+ log.error("Subscriber on {} is already provisioned", accessDevicePort);
+ return false;
+ }
+
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(port));
+ if (si == null) {
+ log.error("Subscriber information not found in sadis for port {}", accessDevicePort);
+ return false;
+ }
+ DiscoveredSubscriber sub = new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.ADDED, true, si);
+
+ // NOTE we need to keep a list of the subscribers that are provisioned on a port,
+ // regardless of the flow status
+ si.uniTagList().forEach(uti -> {
+ ServiceKey sk = new ServiceKey(accessDevicePort, uti);
+ oltFlowService.updateProvisionedSubscriberStatus(sk, true);
+ });
+
+ addSubscriberToQueue(sub);
+ return true;
});
-
- return isPending.get();
- }
-
- private class DeleteEapolInstallSub implements Runnable {
- AccessDevicePort subscriberPort;
- AccessDevicePort uplinkPort;
- SubscriberAndDeviceInformation sub;
- private int attemptNumber;
-
- DeleteEapolInstallSub(AccessDevicePort subscriberPort, AccessDevicePort uplinkPort,
- SubscriberAndDeviceInformation sub,
- int attemptNumber) {
- this.subscriberPort = subscriberPort;
- this.uplinkPort = uplinkPort;
- this.sub = sub;
- this.attemptNumber = attemptNumber;
- }
-
- @Override
- public void run() {
- CompletableFuture<ObjectiveError> filterFuture = new CompletableFuture<>();
- oltFlowService.processEapolFilteringObjectives(subscriberPort,
- defaultBpId, Optional.empty(), filterFuture,
- VlanId.vlanId(EAPOL_DEFAULT_VLAN),
- false);
- filterFuture.thenAcceptAsync(filterStatus -> {
- if (filterStatus == null) {
- log.info("Default eapol flow deleted in attempt {} of {}"
- + "... provisioning subscriber flows on {}",
- attemptNumber, eapolDeleteRetryMaxAttempts, subscriberPort);
-
- // FIXME this is needed to prevent that default EAPOL flow removal and
- // data plane flows install are received by the device at the same time
- provisionExecutor.schedule(
- () -> provisionUniTagList(subscriberPort, uplinkPort, sub),
- provisionDelay, TimeUnit.MILLISECONDS);
- } else {
- if (attemptNumber <= eapolDeleteRetryMaxAttempts) {
- log.warn("The filtering future failed {} for subscriber on {}"
- + "... retrying {} of {} attempts",
- filterStatus, subscriberPort, attemptNumber, eapolDeleteRetryMaxAttempts);
- retryExecutor.execute(
- new DeleteEapolInstallSub(subscriberPort, uplinkPort, sub,
- attemptNumber + 1));
- } else {
- log.error("The filtering future failed {} for subscriber on {}"
- + "after {} attempts. Subscriber provisioning failed",
- filterStatus, subscriberPort, eapolDeleteRetryMaxAttempts);
- sub.uniTagList().forEach(ut ->
- failedSubs.put(
- new ConnectPoint(subscriberPort.deviceId(), subscriberPort.number()), ut));
- }
- }
- });
- }
-
- }
-
- @Override
- public boolean removeSubscriber(ConnectPoint connectPoint) {
- Port subscriberPort = deviceService.getPort(connectPoint);
- if (subscriberPort == null) {
- log.error("Subscriber port not found at: {}", connectPoint);
- return false;
- }
- return removeSubscriber(new AccessDevicePort(subscriberPort, AccessDevicePort.Type.UNI));
- }
-
- private boolean removeSubscriber(AccessDevicePort subscriberPort) {
- log.info("Call to un-provision subscriber at {}", subscriberPort);
- //TODO we need to check if the subscriber is pending
- // Get the subscriber connected to this port from the local cache
- // If we don't know about the subscriber there's no need to remove it
- DeviceId deviceId = subscriberPort.deviceId();
-
- ConnectPoint connectPoint = new ConnectPoint(deviceId, subscriberPort.number());
- Collection<? extends UniTagInformation> uniTagInformationSet = programmedSubs.get(connectPoint).value();
- if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
- log.warn("Subscriber on connectionPoint {} was not previously programmed, " +
- "no need to remove it", connectPoint);
- return true;
- }
-
- // Get the uplink port
- AccessDevicePort uplinkPort = getUplinkPort(deviceService.getDevice(deviceId));
- if (uplinkPort == null) {
- log.warn(NO_UPLINK_PORT, deviceId);
- return false;
- }
-
- for (UniTagInformation uniTag : uniTagInformationSet) {
-
- if (multicastServiceName.equals(uniTag.getServiceName())) {
- continue;
- }
-
- unprovisionVlans(uplinkPort, subscriberPort, uniTag);
-
- // remove eapol with subscriber bandwidth profile
- Optional<String> upstreamOltBw = uniTag.getUpstreamOltBandwidthProfile() == null ?
- Optional.empty() : Optional.of(uniTag.getUpstreamOltBandwidthProfile());
- oltFlowService.processEapolFilteringObjectives(subscriberPort,
- uniTag.getUpstreamBandwidthProfile(),
- upstreamOltBw,
- null, uniTag.getPonCTag(), false);
-
- if (subscriberPort.port() != null && subscriberPort.isEnabled()) {
- // reinstall eapol with default bandwidth profile
- oltFlowService.processEapolFilteringObjectives(subscriberPort, defaultBpId, Optional.empty(),
- null, VlanId.vlanId(EAPOL_DEFAULT_VLAN), true);
- } else {
- log.debug("Port {} is no longer enabled or it's unavailable. Not "
- + "reprogramming default eapol flow", connectPoint);
- }
- }
+ //NOTE this only means we have taken the request in, nothing more.
return true;
}
-
@Override
- public boolean provisionSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
- Optional<VlanId> cTag, Optional<Integer> tpId) {
+ public boolean removeSubscriber(ConnectPoint cp) {
+ subscriberExecutor.submit(() -> {
+ Device device = deviceService.getDevice(DeviceId.deviceId(cp.deviceId().toString()));
+ Port port = deviceService.getPort(device.id(), cp.port());
+ AccessDevicePort accessDevicePort = new AccessDevicePort(port);
- log.info("Provisioning subscriber using subscriberId {}, sTag {}, cTag {}, tpId {}",
- subscriberId, sTag, cTag, tpId);
-
- // Check if we can find the connect point to which this subscriber is connected
- ConnectPoint cp = findSubscriberConnectPoint(subscriberId.toString());
- if (cp == null) {
- log.warn("ConnectPoint for {} not found", subscriberId);
- return false;
- }
- AccessDevicePort subscriberPort = new AccessDevicePort(deviceService.getPort(cp), AccessDevicePort.Type.UNI);
-
- if (!sTag.isPresent() && !cTag.isPresent()) {
- return provisionSubscriber(cp);
- } else if (sTag.isPresent() && cTag.isPresent() && tpId.isPresent()) {
- AccessDevicePort uplinkPort = getUplinkPort(deviceService.getDevice(cp.deviceId()));
- if (uplinkPort == null) {
- log.warn(NO_UPLINK_PORT, cp.deviceId());
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ log.warn("will not un-provision a subscriber on the NNI {}",
+ accessDevicePort);
return false;
}
- //delete Eapol authentication flow with default bandwidth
- //wait until Eapol rule with defaultBpId is removed to install subscriber-based rules
- //install subscriber flows
- CompletableFuture<ObjectiveError> filterFuture = new CompletableFuture<>();
- oltFlowService.processEapolFilteringObjectives(subscriberPort, defaultBpId, Optional.empty(),
- filterFuture, VlanId.vlanId(EAPOL_DEFAULT_VLAN), false);
- filterFuture.thenAcceptAsync(filterStatus -> {
- if (filterStatus == null) {
- provisionUniTagInformation(uplinkPort, subscriberPort, cTag.get(), sTag.get(), tpId.get());
- }
+ log.info("Un-provisioning subscriber on {}", accessDevicePort);
+
+ if (!oltFlowService.isSubscriberServiceProvisioned(accessDevicePort)) {
+ log.error("Subscriber on {} is not provisioned", accessDevicePort);
+ return false;
+ }
+
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(port));
+ if (si == null) {
+ log.error("Subscriber information not found in sadis for port {}",
+ accessDevicePort);
+ // NOTE that we are returning true so that the subscriber is removed from the queue
+ // and we can move on provisioning others
+ return false;
+ }
+ DiscoveredSubscriber sub = new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.REMOVED, true, si);
+
+ // NOTE we need to keep a list of the subscribers that are provisioned on a port,
+ // regardless of the flow status
+ si.uniTagList().forEach(uti -> {
+ ServiceKey sk = new ServiceKey(accessDevicePort, uti);
+ oltFlowService.updateProvisionedSubscriberStatus(sk, false);
});
+
+ addSubscriberToQueue(sub);
return true;
- } else {
- log.warn("Provisioning failed for subscriber: {}", subscriberId);
+ });
+ //NOTE this only means we have taken the request in, nothing more.
+ return true;
+ }
+
+ @Override
+ public boolean provisionSubscriber(ConnectPoint cp, VlanId cTag, VlanId sTag, Integer tpId) {
+ log.debug("Provisioning subscriber on {}, with cTag {}, stag {}, tpId {}",
+ cp, cTag, sTag, tpId);
+ Device device = deviceService.getDevice(cp.deviceId());
+ Port port = deviceService.getPort(device.id(), cp.port());
+ AccessDevicePort accessDevicePort = new AccessDevicePort(port);
+
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ log.warn("will not provision a subscriber on the NNI {}", accessDevicePort);
return false;
}
- }
- @Override
- public boolean removeSubscriber(AccessSubscriberId subscriberId, Optional<VlanId> sTag,
- Optional<VlanId> cTag, Optional<Integer> tpId) {
- // Check if we can find the connect point to which this subscriber is connected
- ConnectPoint cp = findSubscriberConnectPoint(subscriberId.toString());
- if (cp == null) {
- log.warn("ConnectPoint for {} not found", subscriberId);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ UniTagInformation specificService = getUniTagInformation(getPortName(port), cTag, sTag, tpId);
+ if (specificService == null) {
+ log.error("Can't find Information for subscriber on {}, with cTag {}, " +
+ "stag {}, tpId {}", cp, cTag, sTag, tpId);
return false;
}
- AccessDevicePort subscriberPort = new AccessDevicePort(deviceService.getPort(cp), AccessDevicePort.Type.UNI);
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ uniTagInformationList.add(specificService);
+ si.setUniTagList(uniTagInformationList);
+ DiscoveredSubscriber sub = new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.ADDED, true, si);
- if (!sTag.isPresent() && !cTag.isPresent()) {
- return removeSubscriber(cp);
- } else if (sTag.isPresent() && cTag.isPresent() && tpId.isPresent()) {
- // Get the uplink port
- AccessDevicePort uplinkPort = getUplinkPort(deviceService.getDevice(cp.deviceId()));
- if (uplinkPort == null) {
- log.warn(NO_UPLINK_PORT, cp.deviceId());
- return false;
- }
-
- Optional<UniTagInformation> tagInfo = getUniTagInformation(subscriberPort, cTag.get(),
- sTag.get(), tpId.get());
- if (!tagInfo.isPresent()) {
- log.warn("UniTagInformation does not exist for {}, cTag {}, sTag {}, tpId {}",
- subscriberPort, cTag, sTag, tpId);
- return false;
- }
-
- unprovisionVlans(uplinkPort, subscriberPort, tagInfo.get());
- return true;
- } else {
- log.warn("Removing subscriber is not possible - please check the provided information" +
- "for the subscriber: {}", subscriberId);
+ // NOTE we need to keep a list of the subscribers that are provisioned on a port,
+ // regardless of the flow status
+ ServiceKey sk = new ServiceKey(accessDevicePort, specificService);
+ if (oltFlowService.isSubscriberServiceProvisioned(sk)) {
+ log.error("Subscriber on {} is already provisioned", sk);
return false;
}
+ oltFlowService.updateProvisionedSubscriberStatus(sk, true);
+
+ addSubscriberToQueue(sub);
+ return true;
}
@Override
- public ImmutableMap<ConnectPoint, Set<UniTagInformation>> getProgSubs() {
- return programmedSubs.stream()
- .collect(collectingAndThen(
- groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
- ImmutableMap::copyOf));
+ public boolean removeSubscriber(ConnectPoint cp, VlanId cTag, VlanId sTag, Integer tpId) {
+ log.debug("Un-provisioning subscriber on {} with cTag {}, stag {}, tpId {}",
+ cp, cTag, sTag, tpId);
+ Device device = deviceService.getDevice(cp.deviceId());
+ Port port = deviceService.getPort(device.id(), cp.port());
+ AccessDevicePort accessDevicePort = new AccessDevicePort(port);
+
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ log.warn("will not un-provision a subscriber on the NNI {}",
+ accessDevicePort);
+ return false;
+ }
+
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ UniTagInformation specificService = getUniTagInformation(getPortName(port), cTag, sTag, tpId);
+ if (specificService == null) {
+ log.error("Can't find Information for subscriber on {}, with cTag {}, " +
+ "stag {}, tpId {}", cp, cTag, sTag, tpId);
+ return false;
+ }
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ uniTagInformationList.add(specificService);
+ si.setUniTagList(uniTagInformationList);
+ DiscoveredSubscriber sub = new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.ADDED, true, si);
+
+ // NOTE we need to keep a list of the subscribers that are provisioned on a port,
+ // regardless of the flow status
+ ServiceKey sk = new ServiceKey(accessDevicePort, specificService);
+ if (!oltFlowService.isSubscriberServiceProvisioned(sk)) {
+ log.error("Subscriber on {} is not provisioned", sk);
+ return false;
+ }
+ oltFlowService.updateProvisionedSubscriberStatus(sk, false);
+
+ addSubscriberToQueue(sub);
+ return true;
}
@Override
- public ImmutableMap<ConnectPoint, Set<UniTagInformation>> getFailedSubs() {
- return failedSubs.stream()
- .collect(collectingAndThen(
- groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
- ImmutableMap::copyOf));
- }
-
- @Override
- public List<DeviceId> fetchOlts() {
- // look through all the devices and find the ones that are OLTs as per Sadis
+ public List<DeviceId> getConnectedOlts() {
List<DeviceId> olts = new ArrayList<>();
Iterable<Device> devices = deviceService.getDevices();
for (Device d : devices) {
- if (getOltInfo(d) != null) {
+ if (oltDeviceService.isOlt(d)) {
// So this is indeed an OLT device
olts.add(d.id());
}
@@ -648,19 +467,20 @@
}
/**
- * Finds the connect point to which a subscriber is connected.
+ * Finds the connect-point to which a subscriber is connected.
*
* @param id The id of the subscriber, this is the same ID as in Sadis
* @return Subscribers ConnectPoint if found else null
*/
- private ConnectPoint findSubscriberConnectPoint(String id) {
+ @Override
+ public ConnectPoint findSubscriberConnectPoint(String id) {
Iterable<Device> devices = deviceService.getDevices();
for (Device d : devices) {
for (Port p : deviceService.getPorts(d.id())) {
log.trace("Comparing {} with {}", p.annotations().value(AnnotationKeys.PORT_NAME), id);
if (p.annotations().value(AnnotationKeys.PORT_NAME).equals(id)) {
- log.debug("Found on device {} port {}", d.id(), p.number());
+ log.debug("Found on {}", portWithName(p));
return new ConnectPoint(d.id(), p.number());
}
}
@@ -668,584 +488,96 @@
return null;
}
- /**
- * Gets the context of the bandwidth profile information for the given parameter.
- *
- * @param bandwidthProfile the bandwidth profile id
- * @return the context of the bandwidth profile information
- */
- private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
- if (bpService == null) {
- log.warn(SADIS_NOT_RUNNING);
- return null;
- }
- if (bandwidthProfile == null) {
- return null;
- }
- return bpService.get(bandwidthProfile);
- }
+ protected void processDiscoveredSubscribers() {
+ log.info("Started processDiscoveredSubscribers loop");
+ while (true) {
+ Set<ConnectPoint> discoveredCps;
+ try {
+ queueReadLock.lock();
+ discoveredCps = eventsQueues.keySet();
+ } catch (Exception e) {
+ log.error("Cannot read keys from queue map", e);
+ return;
+ } finally {
+ queueReadLock.unlock();
+ }
- /**
- * Removes subscriber vlan flows.
- *
- * @param uplink uplink port of the OLT
- * @param subscriberPort uni port
- * @param uniTag uni tag information
- */
- private void unprovisionVlans(AccessDevicePort uplink, AccessDevicePort subscriberPort, UniTagInformation uniTag) {
- log.info("Unprovisioning vlans for {} at {}", uniTag, subscriberPort);
- DeviceId deviceId = subscriberPort.deviceId();
+ discoveredCps.forEach(cp -> {
+ LinkedBlockingQueue<DiscoveredSubscriber> eventsQueue;
- CompletableFuture<ObjectiveError> downFuture = new CompletableFuture<>();
- CompletableFuture<ObjectiveError> upFuture = new CompletableFuture<>();
-
- VlanId deviceVlan = uniTag.getPonSTag();
- VlanId subscriberVlan = uniTag.getPonCTag();
-
- MeterId upstreamMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, uniTag.getUpstreamBandwidthProfile());
- MeterId downstreamMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, uniTag.getDownstreamBandwidthProfile());
- MeterId upstreamOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, uniTag.getUpstreamOltBandwidthProfile());
- MeterId downstreamOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, uniTag.getDownstreamOltBandwidthProfile());
-
- Optional<SubscriberFlowInfo> waitingMacSubFlowInfo =
- getAndRemoveWaitingMacSubFlowInfoForCTag(new ConnectPoint(deviceId, subscriberPort.number()),
- subscriberVlan);
- if (waitingMacSubFlowInfo.isPresent()) {
- // only dhcp objectives applied previously, so only dhcp uninstallation objective will be processed
- log.debug("Waiting MAC service removed and dhcp uninstallation objective will be processed. " +
- "waitingMacSubFlowInfo:{}", waitingMacSubFlowInfo.get());
- CompletableFuture<ObjectiveError> dhcpFuture = new CompletableFuture<>();
- oltFlowService.processDhcpFilteringObjectives(subscriberPort,
- upstreamMeterId, upstreamOltMeterId, uniTag, false, true, Optional.of(dhcpFuture));
- dhcpFuture.thenAcceptAsync(dhcpStatus -> {
- AccessDeviceEvent.Type type;
- if (dhcpStatus == null) {
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTERED;
- log.debug("Dhcp uninstallation objective was processed successfully for cTag {}, sTag {}, " +
- "tpId {} and Device/Port:{}", uniTag.getPonCTag(), uniTag.getPonSTag(),
- uniTag.getTechnologyProfileId(), subscriberPort);
- updateProgrammedSubscriber(subscriberPort, uniTag, false);
- } else {
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
- log.error("Dhcp uninstallation objective was failed for cTag {}, sTag {}, " +
- "tpId {} and Device/Port:{} :{}", uniTag.getPonCTag(), uniTag.getPonSTag(),
- uniTag.getTechnologyProfileId(), subscriberPort, dhcpStatus);
+ try {
+ queueReadLock.lock();
+ eventsQueue = eventsQueues.get(cp);
+ } catch (Exception e) {
+ log.error("Cannot get key from queue map", e);
+ return;
+ } finally {
+ queueReadLock.unlock();
}
- post(new AccessDeviceEvent(type, deviceId, subscriberPort.port(),
- deviceVlan, subscriberVlan, uniTag.getTechnologyProfileId()));
- });
- return;
- } else {
- log.debug("There is no waiting MAC service for {} and subscriberVlan: {}", subscriberPort, subscriberVlan);
- }
- ForwardingObjective.Builder upFwd =
- oltFlowService.createUpBuilder(uplink, subscriberPort, upstreamMeterId, upstreamOltMeterId, uniTag);
-
- Optional<MacAddress> macAddress = getMacAddress(deviceId, subscriberPort, uniTag);
- ForwardingObjective.Builder downFwd =
- oltFlowService.createDownBuilder(uplink, subscriberPort, downstreamMeterId, downstreamOltMeterId,
- uniTag, macAddress);
-
- oltFlowService.processIgmpFilteringObjectives(subscriberPort, upstreamMeterId, upstreamOltMeterId, uniTag,
- false, true);
- oltFlowService.processDhcpFilteringObjectives(subscriberPort,
- upstreamMeterId, upstreamOltMeterId, uniTag, false, true, Optional.empty());
- oltFlowService.processPPPoEDFilteringObjectives(subscriberPort, upstreamMeterId, upstreamOltMeterId, uniTag,
- false, true);
-
- flowObjectiveService.forward(deviceId, upFwd.remove(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- upFuture.complete(null);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- upFuture.complete(error);
- }
- }));
-
- flowObjectiveService.forward(deviceId, downFwd.remove(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- downFuture.complete(null);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- downFuture.complete(error);
- }
- }));
-
- upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
- AccessDeviceEvent.Type type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTERED;
- if (upStatus == null && downStatus == null) {
- log.debug("Uni tag information is unregistered successfully for cTag {}, sTag {}, tpId {} on {}",
- uniTag.getPonCTag(), uniTag.getPonSTag(), uniTag.getTechnologyProfileId(), subscriberPort);
- updateProgrammedSubscriber(subscriberPort, uniTag, false);
- } else if (downStatus != null) {
- log.error("Subscriber with vlan {} on {} failed downstream uninstallation: {}",
- subscriberVlan, subscriberPort, downStatus);
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
- } else if (upStatus != null) {
- log.error("Subscriber with vlan {} on {} failed upstream uninstallation: {}",
- subscriberVlan, subscriberPort, upStatus);
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_UNREGISTRATION_FAILED;
- }
- post(new AccessDeviceEvent(type, deviceId, subscriberPort.port(), deviceVlan, subscriberVlan,
- uniTag.getTechnologyProfileId()));
- }, oltInstallers);
- }
-
- private Optional<SubscriberFlowInfo> getAndRemoveWaitingMacSubFlowInfoForCTag(ConnectPoint cp, VlanId cTag) {
- SubscriberFlowInfo returnSubFlowInfo = null;
- Collection<? extends SubscriberFlowInfo> subFlowInfoSet = waitingMacSubscribers.get(cp).value();
- for (SubscriberFlowInfo subFlowInfo : subFlowInfoSet) {
- if (subFlowInfo.getTagInfo().getPonCTag().equals(cTag)) {
- returnSubFlowInfo = subFlowInfo;
- break;
- }
- }
- if (returnSubFlowInfo != null) {
- waitingMacSubscribers.remove(cp, returnSubFlowInfo);
- return Optional.of(returnSubFlowInfo);
- }
- return Optional.empty();
- }
-
- /**
- * Adds subscriber vlan flows, dhcp, eapol and igmp trap flows for the related uni port.
- *
- * @param subPort the connection point of the subscriber
- * @param uplinkPort uplink port of the OLT (the nni port)
- * @param sub subscriber information that includes s, c tags, tech profile and bandwidth profile references
- */
- private void provisionUniTagList(AccessDevicePort subPort, AccessDevicePort uplinkPort,
- SubscriberAndDeviceInformation sub) {
-
- log.debug("Provisioning vlans for subscriber on {}", subPort);
- if (log.isTraceEnabled()) {
- log.trace("Subscriber informations {}", sub);
- }
-
- if (sub.uniTagList() == null || sub.uniTagList().isEmpty()) {
- log.warn("Unitaglist doesn't exist for the subscriber {} on {}", sub.id(), subPort);
- return;
- }
-
- for (UniTagInformation uniTag : sub.uniTagList()) {
- handleSubscriberFlows(uplinkPort, subPort, uniTag);
- }
- }
-
- /**
- * Finds the uni tag information and provisions the found information.
- * If the uni tag information is not found, returns
- *
- * @param uplinkPort the nni port
- * @param subscriberPort the uni port
- * @param innerVlan the pon c tag
- * @param outerVlan the pon s tag
- * @param tpId the technology profile id
- */
- private void provisionUniTagInformation(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- VlanId innerVlan,
- VlanId outerVlan,
- Integer tpId) {
-
- Optional<UniTagInformation> gotTagInformation = getUniTagInformation(subscriberPort, innerVlan,
- outerVlan, tpId);
- if (!gotTagInformation.isPresent()) {
- return;
- }
- UniTagInformation tagInformation = gotTagInformation.get();
- handleSubscriberFlows(uplinkPort, subscriberPort, tagInformation);
- }
-
- private void updateProgrammedSubscriber(AccessDevicePort port, UniTagInformation tagInformation, boolean add) {
- if (add) {
- programmedSubs.put(new ConnectPoint(port.deviceId(), port.number()), tagInformation);
- } else {
- programmedSubs.remove(new ConnectPoint(port.deviceId(), port.number()), tagInformation);
- }
- }
-
- /**
- * Installs a uni tag information flow.
- *
- * @param uplinkPort the nni port
- * @param subscriberPort the uni port
- * @param tagInfo the uni tag information
- */
- private void handleSubscriberFlows(AccessDevicePort uplinkPort, AccessDevicePort subscriberPort,
- UniTagInformation tagInfo) {
- log.debug("Provisioning vlan-based flows for the uniTagInformation {} on {}", tagInfo, subscriberPort);
- DeviceId deviceId = subscriberPort.deviceId();
-
- if (multicastServiceName.equals(tagInfo.getServiceName())) {
- // IGMP flows are taken care of along with VOD service
- // Please note that for each service, Subscriber Registered event will be sent
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTERED, deviceId,
- subscriberPort.port(), tagInfo.getPonSTag(), tagInfo.getPonCTag(),
- tagInfo.getTechnologyProfileId()));
- return;
- }
-
- BandwidthProfileInformation upstreamBpInfo =
- getBandwidthProfileInformation(tagInfo.getUpstreamBandwidthProfile());
- BandwidthProfileInformation downstreamBpInfo =
- getBandwidthProfileInformation(tagInfo.getDownstreamBandwidthProfile());
- BandwidthProfileInformation upstreamOltBpInfo =
- getBandwidthProfileInformation(tagInfo.getUpstreamOltBandwidthProfile());
- BandwidthProfileInformation downstreamOltBpInfo =
- getBandwidthProfileInformation(tagInfo.getDownstreamOltBandwidthProfile());
- if (upstreamBpInfo == null) {
- log.warn("No meter installed since no Upstream BW Profile definition found for "
- + "ctag {} stag {} tpId {} on {}",
- tagInfo.getPonCTag(), tagInfo.getPonSTag(), tagInfo.getTechnologyProfileId(), subscriberPort);
- return;
- }
- if (downstreamBpInfo == null) {
- log.warn("No meter installed since no Downstream BW Profile definition found for "
- + "ctag {} stag {} tpId {} on {}",
- tagInfo.getPonCTag(), tagInfo.getPonSTag(),
- tagInfo.getTechnologyProfileId(), subscriberPort);
- return;
- }
- if ((upstreamOltBpInfo != null && downstreamOltBpInfo == null) ||
- (upstreamOltBpInfo == null && downstreamOltBpInfo != null)) {
- log.warn("No meter installed since only one olt BW Profile definition found for "
- + "ctag {} stag {} tpId {} and Device/port: {}:{}",
- tagInfo.getPonCTag(), tagInfo.getPonSTag(),
- tagInfo.getTechnologyProfileId(), deviceId,
- subscriberPort);
- return;
- }
-
- MeterId upOltMeterId = null;
- MeterId downOltMeterId = null;
-
- // check for meterIds for the upstream and downstream bandwidth profiles
- MeterId upMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, upstreamBpInfo.id());
- MeterId downMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, downstreamBpInfo.id());
-
- if (upstreamOltBpInfo != null) {
- // Multi UNI service
- upOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, upstreamOltBpInfo.id());
- downOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, downstreamOltBpInfo.id());
- } else {
- // NOT Multi UNI service
- log.debug("OLT bandwidth profiles fields are set to ONU bandwidth profiles");
- upstreamOltBpInfo = upstreamBpInfo;
- downstreamOltBpInfo = downstreamBpInfo;
- upOltMeterId = upMeterId;
- downOltMeterId = downMeterId;
- }
- SubscriberFlowInfo fi = new SubscriberFlowInfo(uplinkPort, subscriberPort,
- tagInfo, downMeterId, upMeterId, downOltMeterId, upOltMeterId,
- downstreamBpInfo.id(), upstreamBpInfo.id(),
- downstreamOltBpInfo.id(), upstreamOltBpInfo.id());
-
- if (upMeterId != null && downMeterId != null && upOltMeterId != null && downOltMeterId != null) {
- log.debug("Meters are existing for upstream {} and downstream {} on {}",
- upstreamBpInfo.id(), downstreamBpInfo.id(), subscriberPort);
- handleSubFlowsWithMeters(fi);
- } else {
- log.debug("Adding {} on {} to pending subs", fi, subscriberPort);
- // one or both meters are not ready. It's possible they are in the process of being
- // created for other subscribers that share the same bandwidth profile.
- pendingSubscribersForDevice.compute(deviceId, (id, queue) -> {
- if (queue == null) {
- queue = new LinkedBlockingQueue<>();
- }
- queue.add(fi);
- log.info("Added {} to pending subscribers on {}", fi, subscriberPort);
- return queue;
- });
-
- List<BandwidthProfileInformation> bws = new ArrayList<>();
- // queue up the meters to be created
- if (upMeterId == null) {
- log.debug("Missing meter for upstream {} on {}", upstreamBpInfo.id(), subscriberPort);
- bws.add(upstreamBpInfo);
- }
- if (downMeterId == null) {
- log.debug("Missing meter for downstream {} on {}", downstreamBpInfo.id(), subscriberPort);
- bws.add(downstreamBpInfo);
- }
- if (upOltMeterId == null) {
- log.debug("Missing meter for upstreamOlt {} on {}", upstreamOltBpInfo.id(), subscriberPort);
- bws.add(upstreamOltBpInfo);
- }
- if (downOltMeterId == null) {
- log.debug("Missing meter for downstreamOlt {} on {}", downstreamOltBpInfo.id(), subscriberPort);
- bws.add(downstreamOltBpInfo);
- }
- bws.stream().distinct().forEach(bw -> checkAndCreateDevMeter(deviceId, bw));
- }
- }
-
- private void checkAndCreateDevMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
- log.debug("Checking and Creating Meter with {} on {}", bwpInfo, deviceId);
- if (bwpInfo == null) {
- log.error("Can't create meter. Bandwidth profile is null for device : {}", deviceId);
- return;
- }
- //If false the meter is already being installed, skipping installation
- if (!oltMeterService.checkAndAddPendingMeter(deviceId, bwpInfo)) {
- log.debug("Meter is already being installed on {} for {}", deviceId, bwpInfo);
- return;
- }
- createMeter(deviceId, bwpInfo);
- }
-
- private void createMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
- log.info("Creating Meter with {} on {}", bwpInfo, deviceId);
- CompletableFuture<Object> meterFuture = new CompletableFuture<>();
-
- MeterId meterId = oltMeterService.createMeter(deviceId, bwpInfo,
- meterFuture);
-
- meterFuture.thenAcceptAsync(result -> {
- log.info("Meter Future for {} has completed", meterId);
- pendingSubscribersForDevice.compute(deviceId, (id, queue) -> {
- // iterate through the subscribers on hold
- if (queue != null && !queue.isEmpty()) {
- while (true) {
- //TODO this might return the reference and not the actual object so
- // it can be actually swapped underneath us.
- SubscriberFlowInfo fi = queue.peek();
- if (fi == null) {
- log.info("No more subscribers pending on {}", deviceId);
- queue = new LinkedBlockingQueue<>();
- break;
- }
- if (result == null) {
- // meter install sent to device
- log.debug("Meter {} installed for bw {} on {}", meterId, bwpInfo, deviceId);
-
- MeterId upMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, fi.getUpBpInfo());
- MeterId downMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, fi.getDownBpInfo());
- MeterId upOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, fi.getUpOltBpInfo());
- MeterId downOltMeterId = oltMeterService
- .getMeterIdFromBpMapping(deviceId, fi.getDownOltBpInfo());
- if (upMeterId != null && downMeterId != null &&
- upOltMeterId != null && downOltMeterId != null) {
- log.debug("Provisioning subscriber after meter {} " +
- "installation and all meters are present " +
- "upstream {} , downstream {} , oltUpstream {} " +
- "and oltDownstream {} on {}",
- meterId, upMeterId, downMeterId, upOltMeterId,
- downOltMeterId, fi.getUniPort());
- // put in the meterIds because when fi was first
- // created there may or may not have been a meterId
- // depending on whether the meter was created or
- // not at that time.
- //TODO possibly spawn this in a separate thread.
- fi.setUpMeterId(upMeterId);
- fi.setDownMeterId(downMeterId);
- fi.setUpOltMeterId(upOltMeterId);
- fi.setDownOltMeterId(downOltMeterId);
- handleSubFlowsWithMeters(fi);
- queue.remove(fi);
- } else {
- log.debug("Not all meters for {} are yet installed up {}, " +
- "down {}, oltUp {}, oltDown {}", fi, upMeterId,
- downMeterId, upOltMeterId, downOltMeterId);
- }
- oltMeterService.removeFromPendingMeters(deviceId, bwpInfo);
- } else {
- // meter install failed
- log.error("Addition of subscriber {} on {} failed due to meter " +
- "{} with result {}", fi, fi.getUniPort(), meterId, result);
- oltMeterService.removeFromPendingMeters(deviceId, bwpInfo);
- queue.remove(fi);
- }
+ if (!oltDeviceService.isLocalLeader(cp.deviceId())) {
+ // if we're not local leader for this device, ignore this queue
+ if (log.isTraceEnabled()) {
+ log.trace("Ignoring queue on CP {} as not master of the device", cp);
}
- } else {
- log.info("No pending subscribers on {}", deviceId);
- queue = new LinkedBlockingQueue<>();
- }
- return queue;
- });
- });
-
- }
-
- /**
- * Add subscriber flows given meter information for both upstream and
- * downstream directions.
- *
- * @param subscriberFlowInfo relevant information for subscriber
- */
- private void handleSubFlowsWithMeters(SubscriberFlowInfo subscriberFlowInfo) {
- log.info("Provisioning subscriber flows based on {}", subscriberFlowInfo);
- UniTagInformation tagInfo = subscriberFlowInfo.getTagInfo();
- if (tagInfo.getIsDhcpRequired()) {
- Optional<MacAddress> macAddress =
- getMacAddress(subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort(), tagInfo);
- if (subscriberFlowInfo.getTagInfo().getEnableMacLearning()) {
- ConnectPoint cp = new ConnectPoint(subscriberFlowInfo.getDevId(),
- subscriberFlowInfo.getUniPort().number());
- if (macAddress.isPresent()) {
- log.debug("MAC Address {} obtained for {}", macAddress.get(), subscriberFlowInfo);
- } else {
- waitingMacSubscribers.put(cp, subscriberFlowInfo);
- log.debug("Adding sub to waiting mac map: {}", subscriberFlowInfo);
+ return;
}
- CompletableFuture<ObjectiveError> dhcpFuture = new CompletableFuture<>();
- oltFlowService.processDhcpFilteringObjectives(subscriberFlowInfo.getUniPort(),
- subscriberFlowInfo.getUpId(), subscriberFlowInfo.getUpOltId(),
- tagInfo, true, true, Optional.of(dhcpFuture));
- dhcpFuture.thenAcceptAsync(dhcpStatus -> {
- if (dhcpStatus != null) {
- log.error("Dhcp Objective failed for {}: {}", subscriberFlowInfo, dhcpStatus);
- if (macAddress.isEmpty()) {
- waitingMacSubscribers.remove(cp, subscriberFlowInfo);
+ flowsExecutor.execute(() -> {
+ if (!eventsQueue.isEmpty()) {
+ // we do not remove the event from the queue until it has been processed
+ // in that way we guarantee that events are processed in order
+ DiscoveredSubscriber sub = eventsQueue.peek();
+ if (sub == null) {
+ // the queue is empty
+ return;
}
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED,
- subscriberFlowInfo.getDevId(), subscriberFlowInfo.getUniPort().port(),
- tagInfo.getPonSTag(), tagInfo.getPonCTag(), tagInfo.getTechnologyProfileId()));
- } else {
- log.debug("Dhcp Objective success for: {}", subscriberFlowInfo);
- if (macAddress.isPresent()) {
- continueProvisioningSubs(subscriberFlowInfo, macAddress);
+
+ if (log.isTraceEnabled()) {
+ log.trace("Processing subscriber on port {} with status {}",
+ portWithName(sub.port), sub.status);
+ }
+
+ if (sub.hasSubscriber) {
+ // this is a provision subscriber call
+ if (oltFlowService.handleSubscriberFlows(sub, defaultBpId, multicastServiceName)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Provisioning of subscriber on {} completed",
+ portWithName(sub.port));
+ }
+ removeSubscriberFromQueue(sub);
+ }
+ } else {
+ // this is a port event (ENABLED/DISABLED)
+ // means no subscriber was provisioned on that port
+
+ if (!deviceService.isAvailable(sub.device.id()) ||
+ deviceService.getPort(sub.device.id(), sub.port.number()) == null) {
+ // If the device is not connected or the port is not available do nothing
+ // This can happen when we disable and then immediately delete the device,
+ // the queue is populated but the meters and flows are already gone
+ // thus there is nothing left to do
+ return;
+ }
+
+ if (oltFlowService.handleBasicPortFlows(sub, defaultBpId, defaultBpId)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Processing of port {} completed",
+ portWithName(sub.port));
+ }
+ removeSubscriberFromQueue(sub);
+ }
}
}
});
- } else {
- log.debug("Dynamic MAC Learning disabled, so will not learn for: {}", subscriberFlowInfo);
- // dhcp flows will handle after data plane flows
- continueProvisioningSubs(subscriberFlowInfo, macAddress);
- }
- } else {
- // dhcp not required for this service
- continueProvisioningSubs(subscriberFlowInfo, Optional.empty());
- }
- }
+ });
- private void continueProvisioningSubs(SubscriberFlowInfo subscriberFlowInfo, Optional<MacAddress> macAddress) {
- AccessDevicePort uniPort = subscriberFlowInfo.getUniPort();
- log.debug("Provisioning subscriber flows on {} based on {}", uniPort, subscriberFlowInfo);
- UniTagInformation tagInfo = subscriberFlowInfo.getTagInfo();
- CompletableFuture<ObjectiveError> upFuture = new CompletableFuture<>();
- CompletableFuture<ObjectiveError> downFuture = new CompletableFuture<>();
-
- ForwardingObjective.Builder upFwd =
- oltFlowService.createUpBuilder(subscriberFlowInfo.getNniPort(), uniPort, subscriberFlowInfo.getUpId(),
- subscriberFlowInfo.getUpOltId(), subscriberFlowInfo.getTagInfo());
- flowObjectiveService.forward(subscriberFlowInfo.getDevId(), upFwd.add(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- log.debug("Upstream HSIA flow {} installed successfully on {}", subscriberFlowInfo, uniPort);
- upFuture.complete(null);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- upFuture.complete(error);
- }
- }));
-
- ForwardingObjective.Builder downFwd =
- oltFlowService.createDownBuilder(subscriberFlowInfo.getNniPort(), uniPort,
- subscriberFlowInfo.getDownId(), subscriberFlowInfo.getDownOltId(),
- subscriberFlowInfo.getTagInfo(), macAddress);
- flowObjectiveService.forward(subscriberFlowInfo.getDevId(), downFwd.add(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- log.debug("Downstream HSIA flow {} installed successfully on {}", subscriberFlowInfo, uniPort);
- downFuture.complete(null);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- downFuture.complete(error);
- }
- }));
-
- upFuture.thenAcceptBothAsync(downFuture, (upStatus, downStatus) -> {
- AccessDeviceEvent.Type type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTERED;
- if (downStatus != null) {
- log.error("Flow with innervlan {} and outerVlan {} on {} failed downstream installation: {}",
- tagInfo.getPonCTag(), tagInfo.getPonSTag(), uniPort, downStatus);
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED;
- } else if (upStatus != null) {
- log.error("Flow with innervlan {} and outerVlan {} on {} failed upstream installation: {}",
- tagInfo.getPonCTag(), tagInfo.getPonSTag(), uniPort, upStatus);
- type = AccessDeviceEvent.Type.SUBSCRIBER_UNI_TAG_REGISTRATION_FAILED;
- } else {
- log.debug("Upstream and downstream data plane flows are installed successfully on {}", uniPort);
- Optional<String> upstreamOltBw = tagInfo.getUpstreamOltBandwidthProfile() == null ?
- Optional.empty() : Optional.of(tagInfo.getUpstreamOltBandwidthProfile());
- oltFlowService.processEapolFilteringObjectives(uniPort, tagInfo.getUpstreamBandwidthProfile(),
- upstreamOltBw, null,
- tagInfo.getPonCTag(), true);
-
- if (!tagInfo.getEnableMacLearning()) {
- oltFlowService.processDhcpFilteringObjectives(uniPort, subscriberFlowInfo.getUpId(),
- subscriberFlowInfo.getUpOltId(), tagInfo, true, true, Optional.empty());
- }
-
- oltFlowService.processIgmpFilteringObjectives(uniPort, subscriberFlowInfo.getUpId(),
- subscriberFlowInfo.getUpOltId(), tagInfo, true, true);
-
- oltFlowService.processPPPoEDFilteringObjectives(uniPort, subscriberFlowInfo.getUpId(),
- subscriberFlowInfo.getUpOltId(), tagInfo, true, true);
-
- updateProgrammedSubscriber(uniPort, tagInfo, true);
- }
- post(new AccessDeviceEvent(type, subscriberFlowInfo.getDevId(), uniPort.port(),
- tagInfo.getPonSTag(), tagInfo.getPonCTag(),
- tagInfo.getTechnologyProfileId()));
- }, oltInstallers);
- }
-
- /**
- * Gets mac address from tag info if present, else checks the host service.
- *
- * @param deviceId device ID
- * @param port uni port
- * @param tagInformation tag info
- * @return MAC Address of subscriber
- */
- private Optional<MacAddress> getMacAddress(DeviceId deviceId, AccessDevicePort port,
- UniTagInformation tagInformation) {
- if (isMacAddressValid(tagInformation)) {
- log.debug("Got MAC Address {} from the uniTagInformation for {} and cTag {}",
- tagInformation.getConfiguredMacAddress(), port, tagInformation.getPonCTag());
- return Optional.of(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
- } else if (tagInformation.getEnableMacLearning()) {
- Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
- .stream().filter(host -> host.vlan().equals(tagInformation.getPonCTag())).findFirst();
- if (optHost.isPresent()) {
- log.debug("Got MAC Address {} from the hostService for {} and cTag {}",
- optHost.get().mac(), port, tagInformation.getPonCTag());
- return Optional.of(optHost.get().mac());
+ try {
+ TimeUnit.MILLISECONDS.sleep(requeueDelay);
+ } catch (InterruptedException e) {
+ continue;
}
}
- log.debug("Could not obtain MAC Address for {} and cTag {}", port, tagInformation.getPonCTag());
- return Optional.empty();
- }
-
- private boolean isMacAddressValid(UniTagInformation tagInformation) {
- return tagInformation.getConfiguredMacAddress() != null &&
- !tagInformation.getConfiguredMacAddress().trim().equals("") &&
- !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
}
/**
@@ -1253,26 +585,26 @@
* using the pon c tag, pon s tag and the technology profile id
* May return Optional<null>
*
- * @param port port of the subscriber
+ * @param portName port of the subscriber
* @param innerVlan pon c tag
* @param outerVlan pon s tag
* @param tpId the technology profile id
* @return the found uni tag information
*/
- private Optional<UniTagInformation> getUniTagInformation(AccessDevicePort port, VlanId innerVlan,
- VlanId outerVlan, int tpId) {
+ private UniTagInformation getUniTagInformation(String portName, VlanId innerVlan,
+ VlanId outerVlan, int tpId) {
log.debug("Getting uni tag information for {}, innerVlan: {}, outerVlan: {}, tpId: {}",
- port, innerVlan, outerVlan, tpId);
- SubscriberAndDeviceInformation subInfo = getSubscriber(new ConnectPoint(port.deviceId(), port.number()));
+ portName, innerVlan, outerVlan, tpId);
+ SubscriberAndDeviceInformation subInfo = subsService.get(portName);
if (subInfo == null) {
- log.warn("Subscriber information doesn't exist for {}", port);
- return Optional.empty();
+ log.warn("Subscriber information doesn't exist for {}", portName);
+ return null;
}
List<UniTagInformation> uniTagList = subInfo.uniTagList();
if (uniTagList == null) {
- log.warn("Uni tag list is not found for the subscriber {} on {}", subInfo.id(), port);
- return Optional.empty();
+ log.warn("Uni tag list is not found for the subscriber {} on {}", subInfo.id(), portName);
+ return null;
}
UniTagInformation service = null;
@@ -1286,438 +618,298 @@
if (service == null) {
log.warn("SADIS doesn't include the service with ponCtag {} ponStag {} and tpId {} on {}",
- innerVlan, outerVlan, tpId, port);
- return Optional.empty();
- }
-
- return Optional.of(service);
- }
-
- /**
- * Creates trap flows for device, including DHCP and LLDP trap on NNI and
- * EAPOL trap on the UNIs, if device is present in Sadis config.
- *
- * @param dev Device to look for
- */
- private void checkAndCreateDeviceFlows(Device dev) {
- // check if this device is provisioned in Sadis
- SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
- log.info("checkAndCreateDeviceFlows: deviceInfo {}", deviceInfo);
-
- if (deviceInfo != null) {
- log.debug("Driver for device {} is {}", dev.id(),
- driverService.getDriver(dev.id()));
- for (Port p : deviceService.getPorts(dev.id())) {
- if (PortNumber.LOCAL.equals(p.number()) || !p.isEnabled()) {
- continue;
- }
- if (isUniPort(dev, p)) {
- AccessDevicePort port = new AccessDevicePort(p, AccessDevicePort.Type.UNI);
- if (!programmedSubs.containsKey(new ConnectPoint(dev.id(), p.number()))) {
- log.info("Creating Eapol on {}", port);
- oltFlowService.processEapolFilteringObjectives(port, defaultBpId, Optional.empty(),
- null, VlanId.vlanId(EAPOL_DEFAULT_VLAN), true);
- } else {
- log.debug("Subscriber Eapol on {} is already provisioned, not installing default", port);
- }
- } else {
- AccessDevicePort port = new AccessDevicePort(p, AccessDevicePort.Type.NNI);
- oltFlowService.processNniFilteringObjectives(port, true);
- }
- }
- }
- }
-
-
- /**
- * Get the uplink for of the OLT device.
- * <p>
- * This assumes that the OLT has a single uplink port. When more uplink ports need to be supported
- * this logic needs to be changed
- *
- * @param dev Device to look for
- * @return The uplink Port of the OLT
- */
- private AccessDevicePort getUplinkPort(Device dev) {
- // check if this device is provisioned in Sadis
- SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
- log.trace("getUplinkPort: deviceInfo {}", deviceInfo);
- if (deviceInfo == null) {
- log.warn("Device {} is not configured in SADIS .. cannot fetch device"
- + " info", dev.id());
+ innerVlan, outerVlan, tpId, portName);
return null;
}
- // Return the port that has been configured as the uplink port of this OLT in Sadis
- Optional<Port> optionalPort = deviceService.getPorts(dev.id()).stream()
- .filter(port -> isNniPort(port) ||
- (port.number().toLong() == deviceInfo.uplinkPort()))
- .findFirst();
- if (optionalPort.isPresent()) {
- log.trace("getUplinkPort: Found port {}", optionalPort.get());
- return new AccessDevicePort(optionalPort.get(), AccessDevicePort.Type.NNI);
- }
- log.warn("getUplinkPort: " + NO_UPLINK_PORT, dev.id());
- return null;
+ return service;
}
- /**
- * Return the subscriber on a port.
- *
- * @param cp ConnectPoint on which to find the subscriber
- * @return subscriber if found else null
- */
- protected SubscriberAndDeviceInformation getSubscriber(ConnectPoint cp) {
- if (subsService == null) {
- log.warn(SADIS_NOT_RUNNING);
- return null;
- }
- Port port = deviceService.getPort(cp);
- checkNotNull(port, "Invalid connect point");
- String portName = port.annotations().value(AnnotationKeys.PORT_NAME);
- return subsService.get(portName);
+ protected void bindSadisService(SadisService service) {
+ sadisService = service;
+ subsService = sadisService.getSubscriberInfoService();
+
+ log.info("Sadis-service binds to onos.");
}
- /**
- * Checks whether the given port of the device is a uni port or not.
- *
- * @param d the access device
- * @param p the port of the device
- * @return true if the given port is a uni port
- */
- private boolean isUniPort(Device d, Port p) {
- AccessDevicePort ulPort = getUplinkPort(d);
- if (ulPort != null) {
- return (ulPort.number().toLong() != p.number().toLong());
- }
- //handles a special case where NNI port is misconfigured in SADIS and getUplinkPort(d) returns null
- //checks whether the port name starts with nni- which is the signature of an NNI Port
- if (p.annotations().value(AnnotationKeys.PORT_NAME) != null &&
- p.annotations().value(AnnotationKeys.PORT_NAME).startsWith(NNI)) {
- log.error("NNI port number {} is not matching with configured value", p.number().toLong());
- return false;
- }
- return true;
+ protected void unbindSadisService(SadisService service) {
+ deviceService.removeListener(deviceListener);
+ deviceListener = null;
+ sadisService = null;
+ subsService = null;
+ log.info("Sadis-service unbinds from onos.");
}
- /**
- * Gets the given device details from SADIS.
- * If the device is not found, returns null
- *
- * @param dev the access device
- * @return the olt information
- */
- private SubscriberAndDeviceInformation getOltInfo(Device dev) {
- if (subsService == null) {
- log.warn(SADIS_NOT_RUNNING);
- return null;
+ protected void addSubscriberToQueue(DiscoveredSubscriber sub) {
+ ConnectPoint cp = new ConnectPoint(sub.device.id(), sub.port.number());
+ LinkedBlockingQueue<DiscoveredSubscriber> q = null;
+ try {
+ queueReadLock.lock();
+ q = eventsQueues.getOrDefault(cp, new LinkedBlockingQueue<>());
+ } finally {
+ queueReadLock.unlock();
}
- String devSerialNo = dev.serialNumber();
- return subsService.get(devSerialNo);
- }
-
- /**
- * Checks for mastership or falls back to leadership on deviceId.
- * If the device is available use mastership,
- * otherwise fallback on leadership.
- * Leadership on the device topic is needed because the master can be NONE
- * in case the device went away, we still need to handle events
- * consistently
- */
- private boolean isLocalLeader(DeviceId deviceId) {
- if (deviceService.isAvailable(deviceId)) {
- return mastershipService.isLocalMaster(deviceId);
+ if (!q.contains(sub)) {
+ log.info("Adding subscriber to queue: {} with status {} and subscriber {}",
+ portWithName(sub.port), sub.status, sub.hasSubscriber);
+ q.add(sub);
} else {
- // Fallback with Leadership service - device id is used as topic
- NodeId leader = leadershipService.runForLeadership(
- deviceId.toString()).leaderNodeId();
- // Verify if this node is the leader
- return clusterService.getLocalNode().id().equals(leader);
+ log.debug("Not adding subscriber to queue as already present: {} with status {}",
+ portWithName(sub.port), sub.status);
+ // no need to update the queue in the map if nothing has changed
+ return;
+ }
+ try {
+ queueWriteLock.lock();
+ eventsQueues.put(cp, q);
+ } catch (UnsupportedOperationException | ClassCastException |
+ NullPointerException | IllegalArgumentException e) {
+ log.error("Cannot add subscriber to queue: {}", e.getMessage());
+ } finally {
+ queueWriteLock.unlock();
}
}
- private boolean isNniPort(Port port) {
- if (port.annotations().keys().contains(AnnotationKeys.PORT_NAME)) {
- return port.annotations().value(AnnotationKeys.PORT_NAME).contains(NNI);
+ protected void removeSubscriberFromQueue(DiscoveredSubscriber sub) {
+ ConnectPoint cp = new ConnectPoint(sub.device.id(), sub.port.number());
+ LinkedBlockingQueue<DiscoveredSubscriber> q = null;
+ if (log.isTraceEnabled()) {
+ log.trace("removing subscriber {} from queue", sub);
}
- return false;
- }
-
- private class InternalHostListener implements HostListener {
- @Override
- public void event(HostEvent event) {
- hostEventExecutor.execute(() -> {
- Host host = event.subject();
- switch (event.type()) {
- case HOST_ADDED:
- ConnectPoint cp = new ConnectPoint(host.location().deviceId(), host.location().port());
- Optional<SubscriberFlowInfo> optSubFlowInfo =
- getAndRemoveWaitingMacSubFlowInfoForCTag(cp, host.vlan());
- if (optSubFlowInfo.isPresent()) {
- log.debug("Continuing provisioning for waiting mac service. event: {}", event);
- continueProvisioningSubs(optSubFlowInfo.get(), Optional.of(host.mac()));
- } else {
- log.debug("There is no waiting mac sub. event: {}", event);
- }
- break;
- case HOST_UPDATED:
- if (event.prevSubject() != null && !event.prevSubject().mac().equals(event.subject().mac())) {
- log.debug("Subscriber's MAC address changed from {} to {}. " +
- "devId/portNumber: {}/{} vlan: {}", event.prevSubject().mac(),
- event.subject().mac(), host.location().deviceId(), host.location().port(),
- host.vlan());
- // TODO handle subscriber MAC Address changed
- } else {
- log.debug("Unhandled HOST_UPDATED event: {}", event);
- }
- break;
- default:
- log.debug("Unhandled host event received. event: {}", event);
- }
- });
+ try {
+ queueReadLock.lock();
+ q = eventsQueues.get(cp);
+ } finally {
+ queueReadLock.unlock();
+ }
+ if (q == null) {
+ log.warn("Cannot find queue for connectPoint {}", cp);
+ return;
+ }
+ boolean removed = q.remove(sub);
+ if (!removed) {
+ log.warn("Subscriber {} has not been removed from queue, is it still there? {}", sub, q);
+ return;
+ } else {
+ log.debug("Subscriber {} has been removed from the queue", sub);
}
- @Override
- public boolean isRelevant(HostEvent event) {
- return isLocalLeader(event.subject().location().deviceId());
+ try {
+ queueWriteLock.lock();
+ eventsQueues.remove(cp); // am I needed??
+ eventsQueues.put(cp, q);
+ } catch (UnsupportedOperationException | ClassCastException |
+ NullPointerException | IllegalArgumentException e) {
+ log.error("Cannot remove subscriber {} from queue: {}", sub, e.getMessage());
+ } finally {
+ queueWriteLock.unlock();
}
}
- private class InternalDeviceListener implements DeviceListener {
- private Set<DeviceId> programmedDevices = Sets.newConcurrentHashSet();
+ protected class OltDeviceListener
+ implements DeviceListener {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ protected ExecutorService eventExecutor;
+
+ /**
+ * Builds the listener with all the proper services and information needed.
+ */
+ public OltDeviceListener() {
+ this.eventExecutor = Executors.newFixedThreadPool(flowProcessingThreads,
+ groupedThreads("onos/olt-device-listener-event", "event-%d", log));
+ }
+
+ public void deactivate() {
+ this.eventExecutor.shutdown();
+ }
@Override
public void event(DeviceEvent event) {
eventExecutor.execute(() -> {
- DeviceId devId = event.subject().id();
- Device dev = event.subject();
- Port p = event.port();
- DeviceEvent.Type eventType = event.type();
-
- if (DeviceEvent.Type.PORT_STATS_UPDATED.equals(eventType) ||
- DeviceEvent.Type.DEVICE_SUSPENDED.equals(eventType) ||
- DeviceEvent.Type.DEVICE_UPDATED.equals(eventType)) {
- return;
- }
-
- boolean isLocalLeader = isLocalLeader(devId);
- // Only handle the event if the device belongs to us
- if (!isLocalLeader && event.type().equals(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED)
- && !deviceService.isAvailable(devId) && deviceService.getPorts(devId).isEmpty()) {
- log.info("Cleaning local state for non master instance upon " +
- "device disconnection {}", devId);
- // Since no mastership of the device is present upon disconnection
- // the method in the FlowRuleManager only empties the local copy
- // of the DeviceFlowTable thus this method needs to get called
- // on every instance, see how it's done in the InternalDeviceListener
- // in FlowRuleManager: no mastership check for purgeOnDisconnection
- handleDeviceDisconnection(dev, false, false);
- return;
- } else if (!isLocalLeader) {
- log.debug("Not handling event because instance is not leader for {}", devId);
- return;
- }
-
- log.debug("OLT got {} event for {}/{}", eventType, event.subject(), event.port());
-
- if (getOltInfo(dev) == null) {
- // it's possible that we got an event for a previously
- // programmed OLT that is no longer available in SADIS
- // we let such events go through
- if (!programmedDevices.contains(devId)) {
- log.warn("No device info found for {}, this is either "
- + "not an OLT or not known to sadis", dev);
- return;
- }
- }
- AccessDevicePort port = null;
- if (p != null) {
- if (isUniPort(dev, p)) {
- port = new AccessDevicePort(p, AccessDevicePort.Type.UNI);
- } else {
- port = new AccessDevicePort(p, AccessDevicePort.Type.NNI);
- }
- }
-
+ boolean isOlt = oltDeviceService.isOlt(event.subject());
+ DeviceId deviceId = event.subject().id();
switch (event.type()) {
- //TODO: Port handling and bookkeeping should be improved once
- // olt firmware handles correct behaviour.
- case PORT_ADDED:
- if (!deviceService.isAvailable(devId)) {
- log.warn("Received {} for disconnected device {}, ignoring", event, devId);
- return;
- }
- if (port.type().equals(AccessDevicePort.Type.UNI)) {
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, port.port()));
-
- if (port.isEnabled() && !port.number().equals(PortNumber.LOCAL)) {
- log.info("eapol will be sent for port added {}", port);
- oltFlowService.processEapolFilteringObjectives(port, defaultBpId, Optional.empty(),
- null,
- VlanId.vlanId(EAPOL_DEFAULT_VLAN),
- true);
- }
- } else {
- SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
- if (deviceInfo != null) {
- oltFlowService.processNniFilteringObjectives(port, true);
- }
- }
- break;
- case PORT_REMOVED:
- if (port.type().equals(AccessDevicePort.Type.UNI)) {
- // if no subscriber is provisioned we need to remove the default EAPOL
- // if a subscriber was provisioned the default EAPOL will not be there and we can skip.
- // The EAPOL with subscriber tag will be removed by removeSubscriber call.
- Collection<? extends UniTagInformation> uniTagInformationSet =
- programmedSubs.get(new ConnectPoint(port.deviceId(), port.number())).value();
- if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
- log.info("No subscriber provisioned on port {} in PORT_REMOVED event, " +
- "removing default EAPOL flow", port);
- oltFlowService.processEapolFilteringObjectives(port, defaultBpId, Optional.empty(),
- null, VlanId.vlanId(EAPOL_DEFAULT_VLAN), false);
- } else {
- removeSubscriber(port);
- }
-
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, port.port()));
- }
- break;
- case PORT_UPDATED:
- if (!deviceService.isAvailable(devId)) {
- log.warn("Received {} for disconnected device {}, ignoring", event, devId);
- return;
- }
- if (port.type().equals(AccessDevicePort.Type.NNI)) {
- SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
- if (deviceInfo != null && port.isEnabled()) {
- log.debug("NNI {} enabled", port);
- oltFlowService.processNniFilteringObjectives(port, true);
- }
- return;
- }
- ConnectPoint cp = new ConnectPoint(devId, port.number());
- Collection<? extends UniTagInformation> uniTagInformationSet = programmedSubs.get(cp).value();
- if (uniTagInformationSet == null || uniTagInformationSet.isEmpty()) {
- if (!port.number().equals(PortNumber.LOCAL)) {
- log.info("eapol will be {} updated for {} with default vlan {}",
- (port.isEnabled()) ? "added" : "removed", port, EAPOL_DEFAULT_VLAN);
- oltFlowService.processEapolFilteringObjectives(port, defaultBpId, Optional.empty(),
- null, VlanId.vlanId(EAPOL_DEFAULT_VLAN), port.isEnabled());
- }
- } else {
- log.info("eapol will be {} updated for {}", (port.isEnabled()) ? "added" : "removed",
- port);
- for (UniTagInformation uniTag : uniTagInformationSet) {
- oltFlowService.processEapolFilteringObjectives(port,
- uniTag.getUpstreamBandwidthProfile(),
- Optional.of(uniTag.getUpstreamOltBandwidthProfile()),
- null, uniTag.getPonCTag(), port.isEnabled());
- }
- }
- if (port.isEnabled()) {
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, devId, port.port()));
- } else {
- post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, devId, port.port()));
- }
- break;
+ case PORT_STATS_UPDATED:
case DEVICE_ADDED:
- handleDeviceConnection(dev, true);
- break;
- case DEVICE_REMOVED:
- handleDeviceDisconnection(dev, true, true);
- break;
- case DEVICE_AVAILABILITY_CHANGED:
- if (deviceService.isAvailable(devId)) {
- log.info("Handling available device: {}", dev.id());
- handleDeviceConnection(dev, false);
- } else {
- if (deviceService.getPorts(devId).isEmpty()) {
- log.info("Handling controlled device disconnection .. "
- + "flushing all state for dev:{}", devId);
- handleDeviceDisconnection(dev, true, false);
- } else {
- log.info("Disconnected device has available ports .. "
- + "assuming temporary disconnection, "
- + "retaining state for device {}", devId);
- }
- }
- break;
- default:
- log.debug("Not handling event {}", event);
return;
+ case PORT_ADDED:
+ case PORT_UPDATED:
+ case PORT_REMOVED:
+ if (!isOlt) {
+ log.trace("Ignoring event {}, this is not an OLT device", deviceId);
+ return;
+ }
+ if (!oltDeviceService.isLocalLeader(deviceId)) {
+ log.trace("Device {} is not local to this node", deviceId);
+ return;
+ }
+ // port added, updated and removed are treated in the same way as we only care whether the port
+ // is enabled or not
+ handleOltPort(event.type(), event.subject(), event.port());
+ return;
+ case DEVICE_AVAILABILITY_CHANGED:
+ if (!isOlt) {
+ log.trace("Ignoring event {}, this is not an OLT device", deviceId);
+ return;
+ }
+ if (deviceService.isAvailable(deviceId)) {
+ if (!oltDeviceService.isLocalLeader(deviceId)) {
+ if (log.isTraceEnabled()) {
+ log.trace("Device {} is not local to this node, not handling available device",
+ deviceId);
+ }
+ } else {
+ log.info("Handling available device: {}", deviceId);
+ handleExistingPorts();
+ }
+ } else if (!deviceService.isAvailable(deviceId) && deviceService.getPorts(deviceId).isEmpty()) {
+ // NOTE that upon disconnection there is no mastership on the device,
+ // and we should anyway clear the local cache of the flows/meters across instances.
+ // We're only clearing the device if there are no available ports,
+ // otherwise we assume it's a temporary disconnection
+ log.info("Device {} availability changed to false and ports are empty, " +
+ "purging meters and flows", deviceId);
+ //NOTE all the instances will call these methods
+ oltFlowService.purgeDeviceFlows(deviceId);
+ oltMeterService.purgeDeviceMeters(deviceId);
+ clearQueueForDevice(deviceId);
+ } else {
+ log.info("Device {} availability changed to false, but ports are still available, " +
+ "assuming temporary disconnection. Ports: {}",
+ deviceId, deviceService.getPorts(deviceId));
+ }
+ return;
+ case DEVICE_REMOVED:
+ if (!isOlt) {
+ log.trace("Ignoring event {}, this is not an OLT device", deviceId);
+ return;
+ }
+ log.info("Device {} Removed, purging meters and flows", deviceId);
+ oltFlowService.purgeDeviceFlows(deviceId);
+ oltMeterService.purgeDeviceMeters(deviceId);
+ clearQueueForDevice(deviceId);
+ return;
+ default:
+ if (log.isTraceEnabled()) {
+ log.trace("Not handling event: {}, ", event);
+ }
}
});
}
- private void sendUniEvent(Device device, AccessDeviceEvent.Type eventType) {
- deviceService.getPorts(device.id()).stream()
- .filter(p -> !PortNumber.LOCAL.equals(p.number()))
- .filter(p -> isUniPort(device, p))
- .forEach(p -> post(new AccessDeviceEvent(eventType, device.id(), p)));
- }
-
- private void handleDeviceDisconnection(Device device, boolean sendDisconnectedEvent, boolean sendUniEvent) {
- programmedDevices.remove(device.id());
- removeAllSubscribers(device.id());
- removeWaitingMacSubs(device.id());
- //Handle case where OLT disconnects during subscriber provisioning
- pendingSubscribersForDevice.remove(device.id());
- oltFlowService.clearDeviceState(device.id());
-
- //Complete meter and flow purge
- flowRuleService.purgeFlowRules(device.id());
- oltMeterService.clearMeters(device.id());
- if (sendDisconnectedEvent) {
- post(new AccessDeviceEvent(
- AccessDeviceEvent.Type.DEVICE_DISCONNECTED, device.id(),
- null, null, null));
- }
- if (sendUniEvent) {
- sendUniEvent(device, AccessDeviceEvent.Type.UNI_REMOVED);
+ protected void clearQueueForDevice(DeviceId devId) {
+ try {
+ queueWriteLock.lock();
+ Iterator<Map.Entry<ConnectPoint, LinkedBlockingQueue<DiscoveredSubscriber>>> iter =
+ eventsQueues.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<ConnectPoint, LinkedBlockingQueue<DiscoveredSubscriber>> entry = iter.next();
+ if (entry.getKey().deviceId().equals(devId)) {
+ eventsQueues.remove(entry.getKey());
+ }
+ }
+ } finally {
+ queueWriteLock.unlock();
}
}
- private void handleDeviceConnection(Device dev, boolean sendUniEvent) {
- post(new AccessDeviceEvent(
- AccessDeviceEvent.Type.DEVICE_CONNECTED, dev.id(),
- null, null, null));
- programmedDevices.add(dev.id());
- checkAndCreateDeviceFlows(dev);
- if (sendUniEvent) {
- sendUniEvent(dev, AccessDeviceEvent.Type.UNI_ADDED);
+ protected void handleOltPort(DeviceEvent.Type type, Device device, Port port) {
+ log.info("OltDeviceListener receives event {} for port {} with status {} on device {}", type,
+ portWithName(port), port.isEnabled() ? "ENABLED" : "DISABLED", device.id());
+ if (port.isEnabled()) {
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ // NOTE in the NNI case we receive a PORT_REMOVED event with status ENABLED, thus we need to
+ // pass the floeAction to the handleNniFlows method
+ OltFlowService.FlowOperation action = OltFlowService.FlowOperation.ADD;
+ if (type == DeviceEvent.Type.PORT_REMOVED) {
+ action = OltFlowService.FlowOperation.REMOVE;
+ }
+ oltFlowService.handleNniFlows(device, port, action);
+ } else {
+ post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_ADDED, device.id(), port));
+ // NOTE if the subscriber was previously provisioned,
+ // then add it back to the queue to be re-provisioned
+ boolean provisionSubscriber = oltFlowService.
+ isSubscriberServiceProvisioned(new AccessDevicePort(port));
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(port));
+ if (si == null) {
+ //NOTE this should not happen given that the subscriber was provisioned before
+ log.error("Subscriber information not found in sadis for port {}",
+ portWithName(port));
+ return;
+ }
+
+ DiscoveredSubscriber.Status status = DiscoveredSubscriber.Status.ADDED;
+ if (type == DeviceEvent.Type.PORT_REMOVED) {
+ status = DiscoveredSubscriber.Status.REMOVED;
+ }
+
+ DiscoveredSubscriber sub =
+ new DiscoveredSubscriber(device, port,
+ status, provisionSubscriber, si);
+ addSubscriberToQueue(sub);
+ }
+ } else {
+ if (oltDeviceService.isNniPort(device, port.number())) {
+ // NOTE this may need to be handled on DEVICE_REMOVE as we don't disable the NNI
+ oltFlowService.handleNniFlows(device, port, OltFlowService.FlowOperation.REMOVE);
+ } else {
+ post(new AccessDeviceEvent(AccessDeviceEvent.Type.UNI_REMOVED, device.id(), port));
+ // NOTE we are assuming that if a subscriber has default eapol
+ // it does not have subscriber flows
+ if (oltFlowService.hasDefaultEapol(port)) {
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(port));
+ if (si == null) {
+ //NOTE this should not happen given that the subscriber was provisioned before
+ log.error("Subscriber information not found in sadis for port {}",
+ portWithName(port));
+ return;
+ }
+ DiscoveredSubscriber sub =
+ new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.REMOVED, false, si);
+
+ addSubscriberToQueue(sub);
+
+ } else if (oltFlowService.isSubscriberServiceProvisioned(new AccessDevicePort(port))) {
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(port));
+ if (si == null) {
+ //NOTE this should not happen given that the subscriber was provisioned before
+ log.error("Subscriber information not found in sadis for port {}",
+ portWithName(port));
+ return;
+ }
+ DiscoveredSubscriber sub =
+ new DiscoveredSubscriber(device, port,
+ DiscoveredSubscriber.Status.REMOVED, true, si);
+ addSubscriberToQueue(sub);
+ }
+ }
}
}
- private void removeAllSubscribers(DeviceId deviceId) {
- List<Map.Entry<ConnectPoint, UniTagInformation>> subs = programmedSubs.stream()
- .filter(e -> e.getKey().deviceId().equals(deviceId))
- .collect(toList());
-
- subs.forEach(e -> programmedSubs.remove(e.getKey(), e.getValue()));
- }
-
- private void removeWaitingMacSubs(DeviceId deviceId) {
- List<ConnectPoint> waitingMacKeys = waitingMacSubscribers.stream()
- .filter(cp -> cp.getKey().deviceId().equals(deviceId))
- .map(Map.Entry::getKey)
- .collect(toList());
- waitingMacKeys.forEach(cp -> waitingMacSubscribers.removeAll(cp));
- }
-
- }
-
- private class InternalClusterListener implements ClusterEventListener {
-
- @Override
- public void event(ClusterEvent event) {
- if (event.type() == ClusterEvent.Type.INSTANCE_READY) {
- hasher.addServer(event.subject().id());
- }
- if (event.type() == ClusterEvent.Type.INSTANCE_DEACTIVATED) {
- hasher.removeServer(event.subject().id());
+ /**
+ * This method is invoked on app activation in order to deal
+ * with devices and ports that are already existing in the system
+ * and thus won't trigger an event.
+ * It is also needed on instance reboot and device reconnect
+ */
+ protected void handleExistingPorts() {
+ Iterable<DeviceId> devices = getConnectedOlts();
+ for (DeviceId deviceId : devices) {
+ log.info("Handling existing OLT Ports for device {}", deviceId);
+ if (oltDeviceService.isLocalLeader(deviceId)) {
+ List<Port> ports = deviceService.getPorts(deviceId);
+ for (Port p : ports) {
+ if (PortNumber.LOCAL.equals(p.number()) || !p.isEnabled()) {
+ continue;
+ }
+ Device device = deviceService.getDevice(deviceId);
+ deviceListener.handleOltPort(DeviceEvent.Type.PORT_UPDATED, device, p);
+ }
+ }
}
}
}
-
}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltDeviceService.java b/impl/src/main/java/org/opencord/olt/impl/OltDeviceService.java
new file mode 100644
index 0000000..b336c28
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltDeviceService.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceService;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.slf4j.Logger;
+
+import java.util.List;
+import java.util.Optional;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * The implementation of the OltDeviceService.
+ */
+@Component(immediate = true)
+public class OltDeviceService implements OltDeviceServiceInterface {
+
+ protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
+ private final Logger log = getLogger(getClass());
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL,
+ bind = "bindSadisService",
+ unbind = "unbindSadisService",
+ policy = ReferencePolicy.DYNAMIC)
+ protected volatile SadisService sadisService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected ClusterService clusterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected MastershipService mastershipService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LeadershipService leadershipService;
+
+ @Activate
+ public void activate() {
+ log.info("Activated");
+ }
+
+ /**
+ * Returns true if the device is an OLT.
+ *
+ * @param device the Device to be checked
+ * @return boolean
+ */
+ public boolean isOlt(Device device) {
+ return getOltInfo(device) != null;
+ }
+
+ private SubscriberAndDeviceInformation getOltInfo(Device dev) {
+ if (subsService == null) {
+ return null;
+ }
+ String devSerialNo = dev.serialNumber();
+ return subsService.get(devSerialNo);
+ }
+
+
+ /**
+ * Returns true if the port is an NNI Port on the OLT.
+ * NOTE: We can check if a port is a NNI based on the SADIS config, specifically the uplinkPort section
+ *
+ * @param dev the Device this port belongs to
+ * @param portNumber the PortNumber to be checked
+ * @return boolean
+ */
+ @Override
+ public boolean isNniPort(Device dev, PortNumber portNumber) {
+ SubscriberAndDeviceInformation deviceInfo = getOltInfo(dev);
+ return deviceInfo != null && portNumber.toLong() == deviceInfo.uplinkPort();
+ }
+
+ @Override
+ public Optional<Port> getNniPort(Device device) {
+ SubscriberAndDeviceInformation deviceInfo = getOltInfo(device);
+ if (deviceInfo == null) {
+ return Optional.empty();
+ }
+ List<Port> ports = deviceService.getPorts(device.id());
+ if (log.isTraceEnabled()) {
+ log.trace("Get NNI Port looks for NNI in: {}", ports);
+ }
+ return ports.stream()
+ .filter(p -> p.number().toLong() == deviceInfo.uplinkPort())
+ .findFirst();
+ }
+
+ protected void bindSadisService(SadisService service) {
+ this.subsService = service.getSubscriberInfoService();
+ log.info("Sadis service is loaded");
+ }
+
+ protected void unbindSadisService(SadisService service) {
+ this.subsService = null;
+ log.info("Sadis service is unloaded");
+ }
+
+ /**
+ * Checks for mastership or falls back to leadership on deviceId.
+ * If the device is available use mastership,
+ * otherwise fallback on leadership.
+ * Leadership on the device topic is needed because the master can be NONE
+ * in case the device went away, we still need to handle events
+ * consistently
+ *
+ * @param deviceId The device ID to check.
+ * @return boolean (true if the current instance is managing the device)
+ */
+ @Override
+ public boolean isLocalLeader(DeviceId deviceId) {
+ if (deviceService.isAvailable(deviceId)) {
+ return mastershipService.isLocalMaster(deviceId);
+ } else {
+ // Fallback with Leadership service - device id is used as topic
+ NodeId leader = leadershipService.runForLeadership(
+ deviceId.toString()).leaderNodeId();
+ // Verify if this node is the leader
+ return clusterService.getLocalNode().id().equals(leader);
+ }
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltDeviceServiceInterface.java b/impl/src/main/java/org/opencord/olt/impl/OltDeviceServiceInterface.java
new file mode 100644
index 0000000..e8fcccb
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltDeviceServiceInterface.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+
+import java.util.Optional;
+
+/**
+ * Service for olt device handling.
+ */
+public interface OltDeviceServiceInterface {
+ /**
+ * Returns true if the device is a known OLT to sadis/config.
+ * @param device the device
+ * @return true if a configured olt
+ */
+ boolean isOlt(Device device);
+
+ /**
+ * Returns true if the port is an NNI port of the OLT device.
+ * @param device the device
+ * @param port the port
+ * @return true if an NNI port of that OLT
+ */
+ boolean isNniPort(Device device, PortNumber port);
+
+ /**
+ * Returns the NNi port fo the OLT device if present.
+ * @param device the device
+ * @return the nni Port, if present
+ */
+ Optional<Port> getNniPort(Device device);
+
+ /**
+ * Returns true if the instance is leader for the OLT device.
+ * @param deviceId the device
+ * @return true if master, false otherwise.
+ */
+ boolean isLocalLeader(DeviceId deviceId);
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
index 1868421..ee184a5 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OltFlowService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,8 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.opencord.olt.impl;
+import com.google.common.collect.ImmutableMap;
import org.onlab.packet.EthType;
import org.onlab.packet.IPv4;
import org.onlab.packet.IPv6;
@@ -26,18 +28,31 @@
import org.onosproject.cfg.ComponentConfigService;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
import org.onosproject.net.Annotations;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
+import org.onosproject.net.Host;
+import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleListener;
+import org.onosproject.net.flow.FlowRuleService;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flow.criteria.Criterion;
+import org.onosproject.net.flow.criteria.EthTypeCriterion;
+import org.onosproject.net.flow.criteria.IPProtocolCriterion;
+import org.onosproject.net.flow.criteria.PortCriterion;
+import org.onosproject.net.flow.criteria.UdpPortCriterion;
+import org.onosproject.net.flow.criteria.VlanIdCriterion;
+import org.onosproject.net.flow.instructions.L2ModificationInstruction;
import org.onosproject.net.flowobjective.DefaultFilteringObjective;
import org.onosproject.net.flowobjective.DefaultForwardingObjective;
import org.onosproject.net.flowobjective.FilteringObjective;
@@ -46,13 +61,11 @@
import org.onosproject.net.flowobjective.Objective;
import org.onosproject.net.flowobjective.ObjectiveContext;
import org.onosproject.net.flowobjective.ObjectiveError;
+import org.onosproject.net.host.HostService;
import org.onosproject.net.meter.MeterId;
import org.onosproject.store.serializers.KryoNamespaces;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
-import org.opencord.olt.AccessDevicePort;
-import org.opencord.olt.internalapi.AccessDeviceFlowService;
-import org.opencord.olt.internalapi.AccessDeviceMeterService;
import org.opencord.sadis.BandwidthProfileInformation;
import org.opencord.sadis.BaseInformationService;
import org.opencord.sadis.SadisService;
@@ -67,26 +80,46 @@
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
-import java.util.Arrays;
import java.util.Dictionary;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Properties;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.onlab.util.Tools.get;
-import static org.opencord.olt.impl.OsgiPropertyConstants.*;
-import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.net.flow.instructions.Instruction.Type.L2MODIFICATION;
+import static org.opencord.olt.impl.OltUtils.completeFlowOpToString;
+import static org.opencord.olt.impl.OltUtils.flowOpToString;
+import static org.opencord.olt.impl.OltUtils.getPortName;
+import static org.opencord.olt.impl.OltUtils.portWithName;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_TP_ID_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_OLT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DOWNSTREAM_ONU;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_ON_NNI_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V4_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_DHCP_V6_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_EAPOL_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_IGMP_ON_NNI_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE;
+import static org.opencord.olt.impl.OsgiPropertyConstants.ENABLE_PPPOE_DEFAULT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_OLT;
+import static org.opencord.olt.impl.OsgiPropertyConstants.UPSTREAM_ONU;
+import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL;
+import static org.opencord.olt.impl.OsgiPropertyConstants.WAIT_FOR_REMOVAL_DEFAULT;
-/**
- * Provisions flow rules on access devices.
- */
@Component(immediate = true, property = {
ENABLE_DHCP_ON_NNI + ":Boolean=" + ENABLE_DHCP_ON_NNI_DEFAULT,
ENABLE_DHCP_V4 + ":Boolean=" + ENABLE_DHCP_V4_DEFAULT,
@@ -94,32 +127,20 @@
ENABLE_IGMP_ON_NNI + ":Boolean=" + ENABLE_IGMP_ON_NNI_DEFAULT,
ENABLE_EAPOL + ":Boolean=" + ENABLE_EAPOL_DEFAULT,
ENABLE_PPPOE + ":Boolean=" + ENABLE_PPPOE_DEFAULT,
- DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT
+ DEFAULT_TP_ID + ":Integer=" + DEFAULT_TP_ID_DEFAULT,
+ // FIXME remove this option as potentially dangerous in production
+ WAIT_FOR_REMOVAL + ":Boolean=" + WAIT_FOR_REMOVAL_DEFAULT
})
-public class OltFlowService implements AccessDeviceFlowService {
- private static final String SADIS_NOT_RUNNING = "Sadis is not running.";
- private static final String APP_NAME = "org.opencord.olt";
- private static final int NONE_TP_ID = -1;
- private static final int NO_PCP = -1;
- private static final Integer MAX_PRIORITY = 10000;
- private static final Integer MIN_PRIORITY = 1000;
- private static final String INSTALLED = "installed";
- private static final String REMOVED = "removed";
- private static final String INSTALLATION = "installation";
- private static final String REMOVAL = "removal";
- private static final String V4 = "V4";
- private static final String V6 = "V6";
-
- private final Logger log = getLogger(getClass());
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected FlowObjectiveService flowObjectiveService;
+public class OltFlowService implements OltFlowServiceInterface {
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected MastershipService mastershipService;
+ protected ComponentConfigService cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected FlowObjectiveService flowObjectiveService;
@Reference(cardinality = ReferenceCardinality.OPTIONAL,
bind = "bindSadisService",
@@ -128,17 +149,65 @@
protected volatile SadisService sadisService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected OltMeterServiceInterface oltMeterService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected OltDeviceServiceInterface oltDeviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected FlowRuleService flowRuleService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected HostService hostService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected AccessDeviceMeterService oltMeterService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected ComponentConfigService componentConfigService;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
protected StorageService storageService;
+ protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
+ protected BaseInformationService<BandwidthProfileInformation> bpService;
+
+ private static final String APP_NAME = "org.opencord.olt";
+ protected ApplicationId appId;
+ private static final Integer MAX_PRIORITY = 10000;
+ private static final Integer MIN_PRIORITY = 1000;
+ private static final short EAPOL_DEFAULT_VLAN = 4091;
+ private static final int NONE_TP_ID = -1;
+ private static final String V4 = "V4";
+ private static final String V6 = "V6";
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ protected UniTagInformation defaultEapolUniTag = new UniTagInformation.Builder()
+ .setServiceName("defaultEapol").build();
+ protected UniTagInformation nniUniTag = new UniTagInformation.Builder()
+ .setServiceName("nni")
+ .setTechnologyProfileId(NONE_TP_ID)
+ .setPonCTag(VlanId.NONE)
+ .setUniTagMatch(VlanId.ANY)
+ .setUsPonCTagPriority(-1)
+ .build();
+
+ /**
+ * Connect Point status map.
+ * Used to keep track of which cp has flows that needs to be removed when the status changes.
+ */
+ protected Map<ServiceKey, OltPortStatus> cpStatus;
+ private final ReentrantReadWriteLock cpStatusLock = new ReentrantReadWriteLock();
+ private final Lock cpStatusWriteLock = cpStatusLock.writeLock();
+ private final Lock cpStatusReadLock = cpStatusLock.readLock();
+
+ /**
+ * This map contains the subscriber that have been provisioned by the operator.
+ * They may or may not have flows, depending on the port status.
+ * The map is used to define whether flows need to be provisioned when a port comes up.
+ */
+ protected Map<ServiceKey, Boolean> provisionedSubscribers;
+ private final ReentrantReadWriteLock provisionedSubscribersLock = new ReentrantReadWriteLock();
+ private final Lock provisionedSubscribersWriteLock = provisionedSubscribersLock.writeLock();
+ private final Lock provisionedSubscribersReadLock = provisionedSubscribersLock.readLock();
+
/**
* Create DHCP trap flow on NNI port(s).
*/
@@ -174,42 +243,76 @@
**/
protected int defaultTechProfileId = DEFAULT_TP_ID_DEFAULT;
- protected ApplicationId appId;
- protected BaseInformationService<BandwidthProfileInformation> bpService;
- protected BaseInformationService<SubscriberAndDeviceInformation> subsService;
- protected Map<DeviceId, BlockingQueue<SubscriberFlowInfo>> pendingEapolForDevice;
+ protected boolean waitForRemoval = WAIT_FOR_REMOVAL_DEFAULT;
+
+ public enum FlowOperation {
+ ADD,
+ REMOVE;
+
+
+ @Override
+ public String toString() {
+ return super.toString().toLowerCase();
+ }
+ }
+
+ public enum FlowDirection {
+ UPSTREAM,
+ DOWNSTREAM,
+ }
+
+ public enum OltFlowsStatus {
+ NONE,
+ PENDING_ADD,
+ ADDED,
+ PENDING_REMOVE,
+ REMOVED,
+ ERROR
+ }
+
+ protected InternalFlowListener internalFlowListener;
@Activate
public void activate(ComponentContext context) {
- if (sadisService != null) {
- bpService = sadisService.getBandwidthProfileService();
- subsService = sadisService.getSubscriberInfoService();
- } else {
- log.warn(SADIS_NOT_RUNNING);
- }
- componentConfigService.registerProperties(getClass());
- appId = coreService.getAppId(APP_NAME);
+ cfgService.registerProperties(getClass());
+ appId = coreService.registerApplication(APP_NAME);
+ internalFlowListener = new InternalFlowListener();
+
+ modified(context);
+
KryoNamespace serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
- .register(UniTagInformation.class)
- .register(SubscriberFlowInfo.class)
+ .register(OltFlowsStatus.class)
+ .register(FlowDirection.class)
+ .register(OltPortStatus.class)
+ .register(OltFlowsStatus.class)
.register(AccessDevicePort.class)
- .register(AccessDevicePort.Type.class)
- .register(LinkedBlockingQueue.class)
+ .register(new ServiceKeySerializer(), ServiceKey.class)
+ .register(UniTagInformation.class)
.build();
- pendingEapolForDevice = storageService.<DeviceId, BlockingQueue<SubscriberFlowInfo>>consistentMapBuilder()
- .withName("volt-pending-eapol")
- .withSerializer(Serializer.using(serializer))
- .withApplicationId(appId)
- .build().asJavaMap();
- log.info("started");
- }
+ cpStatus = storageService.<ServiceKey, OltPortStatus>consistentMapBuilder()
+ .withName("volt-cp-status")
+ .withApplicationId(appId)
+ .withSerializer(Serializer.using(serializer))
+ .build().asJavaMap();
+
+ provisionedSubscribers = storageService.<ServiceKey, Boolean>consistentMapBuilder()
+ .withName("volt-provisioned-subscriber")
+ .withApplicationId(appId)
+ .withSerializer(Serializer.using(serializer))
+ .build().asJavaMap();
+
+ flowRuleService.addListener(internalFlowListener);
+
+ log.info("Started");
+ }
@Deactivate
public void deactivate(ComponentContext context) {
- componentConfigService.unregisterProperties(getClass(), false);
- log.info("stopped");
+ cfgService.unregisterProperties(getClass(), false);
+ flowRuleService.removeListener(internalFlowListener);
+ log.info("Stopped");
}
@Modified
@@ -247,106 +350,712 @@
enablePppoe = pppoe;
}
+ Boolean wait = Tools.isPropertyEnabled(properties, WAIT_FOR_REMOVAL);
+ if (wait != null) {
+ waitForRemoval = wait;
+ }
+
String tpId = get(properties, DEFAULT_TP_ID);
defaultTechProfileId = isNullOrEmpty(tpId) ? DEFAULT_TP_ID_DEFAULT : Integer.parseInt(tpId.trim());
- log.info("modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
- "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
- "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}",
- enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
- enableIgmpOnNni, enableEapol, enablePppoe,
- defaultTechProfileId);
+ log.info("Modified. Values = enableDhcpOnNni: {}, enableDhcpV4: {}, " +
+ "enableDhcpV6:{}, enableIgmpOnNni:{}, " +
+ "enableEapol:{}, enablePppoe:{}, defaultTechProfileId:{}," +
+ "waitForRemoval:{}",
+ enableDhcpOnNni, enableDhcpV4, enableDhcpV6,
+ enableIgmpOnNni, enableEapol, enablePppoe,
+ defaultTechProfileId, waitForRemoval);
}
- protected void bindSadisService(SadisService service) {
- sadisService = service;
- bpService = sadisService.getBandwidthProfileService();
- subsService = sadisService.getSubscriberInfoService();
- log.info("Sadis-service binds to onos.");
- }
-
- protected void unbindSadisService(SadisService service) {
- sadisService = null;
- bpService = null;
- subsService = null;
- log.info("Sadis-service unbinds from onos.");
- }
-
@Override
- public void processDhcpFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream,
- Optional<CompletableFuture<ObjectiveError>> dhcpFuture) {
- if (upstream) {
- // for UNI ports
- if (tagInformation != null && !tagInformation.getIsDhcpRequired()) {
- log.debug("Dhcp provisioning is disabled for UNI port {} for service {}",
- port, tagInformation.getServiceName());
- dhcpFuture.ifPresent(f -> f.complete(null));
- return;
- }
- } else {
- // for NNI ports
- if (!enableDhcpOnNni) {
- log.debug("Dhcp provisioning is disabled for NNI port {}", port);
- dhcpFuture.ifPresent(f -> f.complete(null));
- return;
- }
- }
- int techProfileId = tagInformation != null ? tagInformation.getTechnologyProfileId() : NONE_TP_ID;
- VlanId cTag = tagInformation != null ? tagInformation.getPonCTag() : VlanId.NONE;
- VlanId unitagMatch = tagInformation != null ? tagInformation.getUniTagMatch() : VlanId.ANY;
- Byte vlanPcp = tagInformation != null && tagInformation.getUsPonCTagPriority() != NO_PCP
- ? (byte) tagInformation.getUsPonCTagPriority() : null;
-
-
- if (enableDhcpV4) {
- int udpSrc = (upstream) ? 68 : 67;
- int udpDst = (upstream) ? 67 : 68;
-
- EthType ethType = EthType.EtherType.IPV4.ethType();
- byte protocol = IPv4.PROTOCOL_UDP;
-
- addDhcpFilteringObjectives(port, udpSrc, udpDst, ethType,
- upstreamMeterId, upstreamOltMeterId, techProfileId, protocol, cTag, unitagMatch,
- vlanPcp, upstream, install, dhcpFuture);
- }
-
- if (enableDhcpV6) {
- int udpSrc = (upstream) ? 547 : 546;
- int udpDst = (upstream) ? 546 : 547;
-
- EthType ethType = EthType.EtherType.IPV6.ethType();
- byte protocol = IPv6.PROTOCOL_UDP;
-
- addDhcpFilteringObjectives(port, udpSrc, udpDst, ethType,
- upstreamMeterId, upstreamOltMeterId, techProfileId, protocol, cTag, unitagMatch,
- vlanPcp, upstream, install, dhcpFuture);
+ public ImmutableMap<ServiceKey, OltPortStatus> getConnectPointStatus() {
+ try {
+ cpStatusReadLock.lock();
+ return ImmutableMap.copyOf(cpStatus);
+ } finally {
+ cpStatusReadLock.unlock();
}
}
- private void addDhcpFilteringObjectives(AccessDevicePort port, int udpSrc, int udpDst,
- EthType ethType, MeterId upstreamMeterId, MeterId upstreamOltMeterId,
- int techProfileId, byte protocol, VlanId cTag, VlanId unitagMatch,
- Byte vlanPcp, boolean upstream,
- boolean install, Optional<CompletableFuture<ObjectiveError>> dhcpFuture) {
+ @Override
+ public ImmutableMap<ServiceKey, UniTagInformation> getProgrammedSubscribers() {
+ // NOTE we might want to remove this conversion and directly use cpStatus as it contains
+ // all the required information about suscribers
+ Map<ServiceKey, UniTagInformation> subscribers =
+ new HashMap<>();
+ try {
+ cpStatusReadLock.lock();
+
+ cpStatus.forEach((sk, status) -> {
+ if (
+ // not NNI Port
+ !oltDeviceService.isNniPort(deviceService.getDevice(sk.getPort().connectPoint().deviceId()),
+ sk.getPort().connectPoint().port()) &&
+ // not EAPOL flow
+ !sk.getService().equals(defaultEapolUniTag)
+ ) {
+ subscribers.put(sk, sk.getService());
+ }
+ });
+
+ return ImmutableMap.copyOf(subscribers);
+ } finally {
+ cpStatusReadLock.unlock();
+ }
+ }
+
+ @Override
+ public Map<ServiceKey, Boolean> getRequestedSubscribers() {
+ try {
+ provisionedSubscribersReadLock.lock();
+ return ImmutableMap.copyOf(provisionedSubscribers);
+ } finally {
+ provisionedSubscribersReadLock.unlock();
+ }
+ }
+
+ @Override
+ public void handleNniFlows(Device device, Port port, FlowOperation action) {
+
+ // always handle the LLDP flow
+ processLldpFilteringObjective(device.id(), port, action);
+
+ if (enableDhcpOnNni) {
+ if (enableDhcpV4) {
+ log.debug("Installing DHCPv4 trap flow on NNI {} for device {}", portWithName(port), device.id());
+ processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
+ 67, 68, EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP,
+ null, null, nniUniTag);
+ }
+ if (enableDhcpV6) {
+ log.debug("Installing DHCPv6 trap flow on NNI {} for device {}", portWithName(port), device.id());
+ processDhcpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
+ 546, 547, EthType.EtherType.IPV6.ethType(), IPv6.PROTOCOL_UDP,
+ null, null, nniUniTag);
+ }
+ } else {
+ log.info("DHCP is not required on NNI {} for device {}", portWithName(port), device.id());
+ }
+
+ if (enableIgmpOnNni) {
+ log.debug("Installing IGMP flow on NNI {} for device {}", portWithName(port), device.id());
+ processIgmpFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
+ null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, -1);
+ }
+
+ if (enablePppoe) {
+ log.debug("Installing PPPoE flow on NNI {} for device {}", port.number(), device.id());
+ processPPPoEDFilteringObjectives(device.id(), port, action, FlowDirection.DOWNSTREAM,
+ null, null, NONE_TP_ID, VlanId.NONE, VlanId.ANY, null);
+ }
+ }
+
+ @Override
+ public boolean handleBasicPortFlows(DiscoveredSubscriber sub, String bandwidthProfileId,
+ String oltBandwidthProfileId) {
+
+ // we only need to something if EAPOL is enabled
+ if (!enableEapol) {
+ return true;
+ }
+
+ if (sub.status == DiscoveredSubscriber.Status.ADDED) {
+ return addDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
+ } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
+ return removeDefaultFlows(sub, bandwidthProfileId, oltBandwidthProfileId);
+ } else {
+ log.error("Unknown Status {} on DiscoveredSubscriber {}", sub.status, sub);
+ return false;
+ }
+
+ }
+
+ private boolean addDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfileId, String oltBandwidthProfileId) {
+
+ if (!oltMeterService.createMeter(sub.device.id(), bandwidthProfileId)) {
+ if (log.isTraceEnabled()) {
+ log.trace("waiting on meter for bp {} and sub {}", bandwidthProfileId, sub);
+ }
+ return false;
+ }
+ if (hasDefaultEapol(sub.port)) {
+ return true;
+ }
+ return handleEapolFlow(sub, bandwidthProfileId,
+ oltBandwidthProfileId, FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
+
+ }
+
+ private boolean removeDefaultFlows(DiscoveredSubscriber sub, String bandwidthProfile, String oltBandwidthProfile) {
+ // NOTE that we are not checking for meters as they must have been created to install the flow in first place
+ return handleEapolFlow(sub, bandwidthProfile, oltBandwidthProfile,
+ FlowOperation.REMOVE, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
+ }
+
+ @Override
+ public boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwidthProfile,
+ String multicastServiceName) {
+ // NOTE that we are taking defaultBandwithProfile as a parameter as that can be configured in the Olt component
+ if (sub.status == DiscoveredSubscriber.Status.ADDED) {
+ return addSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
+ } else if (sub.status == DiscoveredSubscriber.Status.REMOVED) {
+ return removeSubscriberFlows(sub, defaultBandwidthProfile, multicastServiceName);
+ } else {
+ log.error("don't know how to handle {}", sub);
+ return false;
+ }
+ }
+
+ private boolean addSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
+ String multicastServiceName) {
+ if (log.isTraceEnabled()) {
+ log.trace("Provisioning of subscriber on {} started", portWithName(sub.port));
+ }
+ if (enableEapol) {
+ if (hasDefaultEapol(sub.port)) {
+ // remove EAPOL flow and throw exception so that we'll retry later
+ if (!isDefaultEapolPendingRemoval(sub.port)) {
+ removeDefaultFlows(sub, defaultBandwithProfile, defaultBandwithProfile);
+ }
+
+ if (waitForRemoval) {
+ // NOTE wait for removal is a flag only needed to make sure VOLTHA
+ // does not explode with the flows remove/add in the same batch
+ log.debug("Awaiting for default flows removal for {}", portWithName(sub.port));
+ return false;
+ } else {
+ log.warn("continuing provisioning on {}", portWithName(sub.port));
+ }
+ }
+
+ }
+
+ // NOTE createMeters will return if the meters are not installed
+ if (!oltMeterService.createMeters(sub.device.id(),
+ sub.subscriberAndDeviceInformation)) {
+ return false;
+ }
+
+ // NOTE we need to add the DHCP flow regardless so that the host can be discovered and the MacAddress added
+ handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.ADD,
+ sub.subscriberAndDeviceInformation);
+
+ if (isMacLearningEnabled(sub.subscriberAndDeviceInformation)
+ && !isMacAddressAvailable(sub.device.id(), sub.port,
+ sub.subscriberAndDeviceInformation)) {
+ log.debug("Awaiting for macAddress on {}", portWithName(sub.port));
+ return false;
+ }
+
+ handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.ADD,
+ sub.subscriberAndDeviceInformation, multicastServiceName);
+
+ handleSubscriberEapolFlows(sub, FlowOperation.ADD, sub.subscriberAndDeviceInformation);
+
+ handleSubscriberIgmpFlows(sub, FlowOperation.ADD);
+
+ log.info("Provisioning of subscriber on {} completed", portWithName(sub.port));
+ return true;
+ }
+
+ private boolean removeSubscriberFlows(DiscoveredSubscriber sub, String defaultBandwithProfile,
+ String multicastServiceName) {
+
+ if (log.isTraceEnabled()) {
+ log.trace("Removal of subscriber on {} started",
+ portWithName(sub.port));
+ }
+ SubscriberAndDeviceInformation si = subsService.get(sub.portName());
+ if (si == null) {
+ log.error("Subscriber information not found in sadis for port {} during subscriber removal",
+ portWithName(sub.port));
+ // NOTE that we are returning true so that the subscriber is removed from the queue
+ // and we can move on provisioning others
+ return true;
+ }
+
+ handleSubscriberDhcpFlows(sub.device.id(), sub.port, FlowOperation.REMOVE, si);
+
+ if (enableEapol) {
+ // remove the tagged eapol
+ handleSubscriberEapolFlows(sub, FlowOperation.REMOVE, si);
+
+ // and add the default one back
+ if (sub.port.isEnabled()) {
+ // NOTE we remove the subscriber when the port goes down
+ // but in that case we don't need to add default eapol
+ handleEapolFlow(sub, defaultBandwithProfile, defaultBandwithProfile,
+ FlowOperation.ADD, VlanId.vlanId(EAPOL_DEFAULT_VLAN));
+ }
+ }
+ handleSubscriberDataFlows(sub.device, sub.port, FlowOperation.REMOVE, si, multicastServiceName);
+
+ handleSubscriberIgmpFlows(sub, FlowOperation.REMOVE);
+
+ // FIXME check the return status of the flow and return accordingly
+ log.info("Removal of subscriber on {} completed", portWithName(sub.port));
+ return true;
+ }
+
+ @Override
+ public boolean hasDefaultEapol(Port port) {
+ OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
+ // NOTE we consider ERROR as a present EAPOL flow as ONOS
+ // will keep trying to add it
+ return status != null && (status.defaultEapolStatus == OltFlowsStatus.ADDED ||
+ status.defaultEapolStatus == OltFlowsStatus.PENDING_ADD ||
+ status.defaultEapolStatus == OltFlowsStatus.ERROR);
+ }
+
+ private OltPortStatus getOltPortStatus(Port port, UniTagInformation uniTagInformation) {
+ try {
+ cpStatusReadLock.lock();
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uniTagInformation);
+ OltPortStatus status = cpStatus.get(sk);
+ return status;
+ } finally {
+ cpStatusReadLock.unlock();
+ }
+ }
+
+ public boolean isDefaultEapolPendingRemoval(Port port) {
+ OltPortStatus status = getOltPortStatus(port, defaultEapolUniTag);
+ if (log.isTraceEnabled()) {
+ log.trace("Status during EAPOL flow check {} for port {} and UniTagInformation {}",
+ status, portWithName(port), defaultEapolUniTag);
+ }
+ return status != null && status.defaultEapolStatus == OltFlowsStatus.PENDING_REMOVE;
+ }
+
+ @Override
+ public boolean hasDhcpFlows(Port port, UniTagInformation uti) {
+ OltPortStatus status = getOltPortStatus(port, uti);
+ if (log.isTraceEnabled()) {
+ log.trace("Status during DHCP flow check {} for port {} and service {}",
+ status, portWithName(port), uti.getServiceName());
+ }
+ return status != null &&
+ (status.dhcpStatus == OltFlowsStatus.ADDED ||
+ status.dhcpStatus == OltFlowsStatus.PENDING_ADD);
+ }
+
+ @Override
+ public boolean hasSubscriberFlows(Port port, UniTagInformation uti) {
+
+ OltPortStatus status = getOltPortStatus(port, uti);
+ if (log.isTraceEnabled()) {
+ log.trace("Status during subscriber flow check {} for port {} and service {}",
+ status, portWithName(port), uti.getServiceName());
+ }
+ return status != null && (status.subscriberFlowsStatus == OltFlowsStatus.ADDED ||
+ status.subscriberFlowsStatus == OltFlowsStatus.PENDING_ADD);
+ }
+
+ @Override
+ public void purgeDeviceFlows(DeviceId deviceId) {
+ log.debug("Purging flows on device {}", deviceId);
+ flowRuleService.purgeFlowRules(deviceId);
+
+ // removing the status from the cpStatus map
+ try {
+ cpStatusWriteLock.lock();
+ Iterator<Map.Entry<ServiceKey, OltPortStatus>> iter = cpStatus.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<ServiceKey, OltPortStatus> entry = iter.next();
+ if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
+ cpStatus.remove(entry.getKey());
+ }
+ }
+ } finally {
+ cpStatusWriteLock.unlock();
+ }
+
+ // removing subscribers from the provisioned map
+ try {
+ provisionedSubscribersWriteLock.lock();
+ Iterator<Map.Entry<ServiceKey, Boolean>> iter = provisionedSubscribers.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<ServiceKey, Boolean> entry = iter.next();
+ if (entry.getKey().getPort().connectPoint().deviceId().equals(deviceId)) {
+ provisionedSubscribers.remove(entry.getKey());
+ }
+ }
+ } finally {
+ provisionedSubscribersWriteLock.unlock();
+ }
+ }
+
+ @Override
+ public boolean isSubscriberServiceProvisioned(AccessDevicePort cp) {
+ // if any service is programmed on this port, returns true
+ AtomicBoolean provisioned = new AtomicBoolean(false);
+ try {
+ provisionedSubscribersReadLock.lock();
+ for (Map.Entry<ServiceKey, Boolean> entry : provisionedSubscribers.entrySet()) {
+ if (entry.getKey().getPort().equals(cp) && entry.getValue()) {
+ provisioned.set(true);
+ break;
+ }
+ }
+ } finally {
+ provisionedSubscribersReadLock.unlock();
+ }
+ return provisioned.get();
+ }
+
+ @Override
+ public boolean isSubscriberServiceProvisioned(ServiceKey sk) {
+ try {
+ provisionedSubscribersReadLock.lock();
+ Boolean provisioned = provisionedSubscribers.get(sk);
+ if (provisioned == null || !provisioned) {
+ return false;
+ }
+ } finally {
+ provisionedSubscribersReadLock.unlock();
+ }
+ return true;
+ }
+
+ @Override
+ public void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status) {
+ try {
+ provisionedSubscribersWriteLock.lock();
+ provisionedSubscribers.put(sk, status);
+ } finally {
+ provisionedSubscribersWriteLock.unlock();
+ }
+ }
+
+ private boolean handleEapolFlow(DiscoveredSubscriber sub, String bandwidthProfile,
+ String oltBandwidthProfile, FlowOperation action, VlanId vlanId) {
+
+ // create a subscriberKey for the EAPOL flow
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(sub.port), defaultEapolUniTag);
+
+ // NOTE we only need to keep track of the default EAPOL flow in the
+ // connectpoint status map
+ if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
+ OltFlowsStatus status = action == FlowOperation.ADD ?
+ OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
+ updateConnectPointStatus(sk, status, OltFlowsStatus.NONE, OltFlowsStatus.NONE);
+
+ }
+
+ DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+
+ int techProfileId = getDefaultTechProfileId(sub.port);
+ MeterId meterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), bandwidthProfile);
+
+ // in the delete case the meter should still be there as we remove
+ // the meters only if no flows are pointing to them
+ if (meterId == null) {
+ log.debug("MeterId is null for BandwidthProfile {} on device {}",
+ bandwidthProfile, sub.device.id());
+ return false;
+ }
+
+ MeterId oltMeterId = oltMeterService.getMeterIdForBandwidthProfile(sub.device.id(), oltBandwidthProfile);
+ if (oltMeterId == null) {
+ log.debug("MeterId is null for OltBandwidthProfile {} on device {}",
+ oltBandwidthProfile, sub.device.id());
+ return false;
+ }
+
+ log.info("{} EAPOL flow for {} with vlanId {} and BandwidthProfile {} (meterId {})",
+ flowOpToString(action), portWithName(sub.port), vlanId, bandwidthProfile, meterId);
+
+ FilteringObjective.Builder eapolAction;
+
+ if (action == FlowOperation.ADD) {
+ eapolAction = filterBuilder.permit();
+ } else if (action == FlowOperation.REMOVE) {
+ eapolAction = filterBuilder.deny();
+ } else {
+ log.error("Operation {} not supported", action);
+ return false;
+ }
+
+ FilteringObjective.Builder baseEapol = eapolAction
+ .withKey(Criteria.matchInPort(sub.port.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()));
+
+ // NOTE we only need to add the treatment to install the flow,
+ // we can remove it based in the match
+ FilteringObjective.Builder eapol;
+
+ TrafficTreatment treatment = treatmentBuilder
+ .meter(meterId)
+ .writeMetadata(createTechProfValueForWriteMetadata(
+ vlanId,
+ techProfileId, oltMeterId), 0)
+ .setOutput(PortNumber.CONTROLLER)
+ .pushVlan()
+ .setVlanId(vlanId)
+ .build();
+ eapol = baseEapol
+ .withMeta(treatment);
+
+ FilteringObjective eapolObjective = eapol
+ .fromApp(appId)
+ .withPriority(MAX_PRIORITY)
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("EAPOL flow objective {} for {}",
+ completeFlowOpToString(action), portWithName(sub.port));
+ if (log.isTraceEnabled()) {
+ log.trace("EAPOL flow details for port {}: {}", portWithName(sub.port), objective);
+ }
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.error("Cannot {} eapol flow for {} : {}", action,
+ portWithName(sub.port), error);
+
+ if (vlanId.id().equals(EAPOL_DEFAULT_VLAN)) {
+ updateConnectPointStatus(sk,
+ OltFlowsStatus.ERROR, null, null);
+ }
+ }
+ });
+
+ flowObjectiveService.filter(sub.device.id(), eapolObjective);
+
+ log.info("{} EAPOL filter for {}", completeFlowOpToString(action), portWithName(sub.port));
+ return true;
+ }
+
+ // FIXME it's confusing that si is not necessarily inside the DiscoveredSubscriber
+ private boolean handleSubscriberEapolFlows(DiscoveredSubscriber sub, FlowOperation action,
+ SubscriberAndDeviceInformation si) {
+ if (!enableEapol) {
+ return true;
+ }
+ // TODO verify we need an EAPOL flow for EACH service
+ AtomicBoolean success = new AtomicBoolean(true);
+ si.uniTagList().forEach(u -> {
+ // we assume that if subscriber flows are installed, tagged EAPOL is installed as well
+ boolean hasFlows = hasSubscriberFlows(sub.port, u);
+
+ // if the subscriber flows are present the EAPOL flow is present thus we don't need to install it,
+ // if the subscriber does not have flows the EAPOL flow is not present thus we don't need to remove it
+ if (action == FlowOperation.ADD && hasFlows ||
+ action == FlowOperation.REMOVE && !hasFlows) {
+ log.debug("Not {} EAPOL on {}({}) as subscriber flow status is {}", flowOpToString(action),
+ portWithName(sub.port), u.getServiceName(), hasFlows);
+ return;
+ }
+ log.info("{} EAPOL flows for subscriber {} on {} and service {}",
+ flowOpToString(action), si.id(), portWithName(sub.port), u.getServiceName());
+ if (!handleEapolFlow(sub, u.getUpstreamBandwidthProfile(),
+ u.getUpstreamOltBandwidthProfile(),
+ action, u.getPonCTag())) {
+ //
+ log.error("Failed to {} EAPOL with suscriber tags", action);
+ //TODO this sets it for all services, maybe some services succeeded.
+ success.set(false);
+ }
+ });
+ return success.get();
+ }
+
+ private void handleSubscriberIgmpFlows(DiscoveredSubscriber sub, FlowOperation action) {
+ sub.subscriberAndDeviceInformation.uniTagList().forEach(uti -> {
+ if (uti.getIsIgmpRequired()) {
+ DeviceId deviceId = sub.device.id();
+ // if we reached here a meter already exists
+ MeterId meterId = oltMeterService
+ .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
+ MeterId oltMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
+
+ processIgmpFilteringObjectives(deviceId, sub.port,
+ action, FlowDirection.UPSTREAM, meterId, oltMeterId, uti.getTechnologyProfileId(),
+ uti.getPonCTag(), uti.getUniTagMatch(), uti.getUsPonCTagPriority());
+ }
+ });
+ }
+
+ private boolean checkSadisRunning() {
+ if (bpService == null) {
+ log.warn("Sadis is not running");
+ return false;
+ }
+ return true;
+ }
+
+ private int getDefaultTechProfileId(Port port) {
+ if (!checkSadisRunning()) {
+ return defaultTechProfileId;
+ }
+ if (port != null) {
+ SubscriberAndDeviceInformation info = subsService.get(getPortName(port));
+ if (info != null && info.uniTagList().size() == 1) {
+ return info.uniTagList().get(0).getTechnologyProfileId();
+ }
+ }
+ return defaultTechProfileId;
+ }
+
+ protected Long createTechProfValueForWriteMetadata(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
+ Long writeMetadata;
+
+ if (cVlan == null || VlanId.NONE.equals(cVlan)) {
+ writeMetadata = (long) techProfileId << 32;
+ } else {
+ writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
+ }
+ if (upstreamOltMeterId == null) {
+ return writeMetadata;
+ } else {
+ return writeMetadata | upstreamOltMeterId.id();
+ }
+ }
+
+ private void processLldpFilteringObjective(DeviceId deviceId, Port port, FlowOperation action) {
+ DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
+
+ FilteringObjective lldp = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
+ .withKey(Criteria.matchInPort(port.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
+ .withMeta(DefaultTrafficTreatment.builder()
+ .setOutput(PortNumber.CONTROLLER).build())
+ .fromApp(appId)
+ .withPriority(MAX_PRIORITY)
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("{} LLDP filter for {}.", completeFlowOpToString(action), portWithName(port));
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.error("Falied to {} LLDP filter on {} because {}", action, portWithName(port),
+ error);
+ }
+ });
+
+ flowObjectiveService.filter(deviceId, lldp);
+ }
+
+ protected void handleSubscriberDhcpFlows(DeviceId deviceId, Port port,
+ FlowOperation action,
+ SubscriberAndDeviceInformation si) {
+ si.uniTagList().forEach(uti -> {
+
+ if (!uti.getIsDhcpRequired()) {
+ return;
+ }
+
+ // if it's an ADD skip if flows are there,
+ // if it's a DELETE skip if flows are not there
+ boolean hasFlows = hasDhcpFlows(port, uti);
+ if (action == FlowOperation.ADD && hasFlows ||
+ action == FlowOperation.REMOVE && !hasFlows) {
+ log.debug("Not dealing with DHCP {} on {} as DHCP flow status is {}", action,
+ uti.getServiceName(), hasFlows);
+ return;
+ }
+
+ log.info("{} DHCP flows for subscriber on {} and service {}",
+ flowOpToString(action), portWithName(port), uti.getServiceName());
+
+ // if we reached here a meter already exists
+ MeterId meterId = oltMeterService
+ .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamBandwidthProfile());
+ MeterId oltMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(deviceId, uti.getUpstreamOltBandwidthProfile());
+
+ if (enableDhcpV4) {
+ processDhcpFilteringObjectives(deviceId, port, action, FlowDirection.UPSTREAM, 68, 67,
+ EthType.EtherType.IPV4.ethType(), IPv4.PROTOCOL_UDP, meterId, oltMeterId,
+ uti);
+ }
+ if (enableDhcpV6) {
+ log.error("DHCP V6 not supported for subscribers");
+ }
+ });
+ }
+
+ // FIXME return boolean, if this fails we need to retry
+ protected void handleSubscriberDataFlows(Device device, Port port,
+ FlowOperation action,
+ SubscriberAndDeviceInformation si, String multicastServiceName) {
+
+ Optional<Port> nniPort = oltDeviceService.getNniPort(device);
+ if (nniPort.isEmpty()) {
+ log.error("Cannot configure DP flows as upstream port is not configured for subscriber {} on {}",
+ si.id(), portWithName(port));
+ return;
+ }
+ si.uniTagList().forEach(uti -> {
+
+ boolean hasFlows = hasSubscriberFlows(port, uti);
+ if (action == FlowOperation.ADD && hasFlows ||
+ action == FlowOperation.REMOVE && !hasFlows) {
+ log.debug("Not dealing with DP flows {} on {} as subscriber flow status is {}", action,
+ uti.getServiceName(), hasFlows);
+ return;
+ }
+
+ if (multicastServiceName.equals(uti.getServiceName())) {
+ log.debug("This is the multicast service ({}) for subscriber {} on {}, " +
+ "dataplane flows are not needed",
+ uti.getServiceName(), si.id(), portWithName(port));
+ return;
+ }
+
+ log.info("{} Data plane flows for subscriber {} on {} and service {}",
+ flowOpToString(action), si.id(), portWithName(port), uti.getServiceName());
+
+ // upstream flows
+ MeterId usMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamBandwidthProfile());
+ MeterId oltUsMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(device.id(), uti.getUpstreamOltBandwidthProfile());
+ processUpstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, usMeterId,
+ oltUsMeterId, uti);
+
+ // downstream flows
+ MeterId dsMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamBandwidthProfile());
+ MeterId oltDsMeterId = oltMeterService
+ .getMeterIdForBandwidthProfile(device.id(), uti.getDownstreamOltBandwidthProfile());
+ processDownstreamDataFilteringObjects(device.id(), port, nniPort.get(), action, dsMeterId,
+ oltDsMeterId, uti, getMacAddress(device.id(), port, uti));
+ });
+ }
+
+ private void processDhcpFilteringObjectives(DeviceId deviceId, Port port,
+ FlowOperation action, FlowDirection direction,
+ int udpSrc, int udpDst, EthType ethType, byte protocol,
+ MeterId meterId, MeterId oltMeterId, UniTagInformation uti) {
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
+ log.debug("{} DHCP filtering objectives on {}", flowOpToString(action), sk);
+
+
+ OltFlowsStatus status = action.equals(FlowOperation.ADD) ?
+ OltFlowsStatus.PENDING_ADD : OltFlowsStatus.PENDING_REMOVE;
+ updateConnectPointStatus(sk, null, null, status);
DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
- if (upstreamMeterId != null) {
- treatmentBuilder.meter(upstreamMeterId);
+ if (meterId != null) {
+ treatmentBuilder.meter(meterId);
}
- if (techProfileId != NONE_TP_ID) {
- treatmentBuilder.writeMetadata(createTechProfValueForWm(unitagMatch, techProfileId, upstreamOltMeterId), 0);
+ if (uti.getTechnologyProfileId() != NONE_TP_ID) {
+ treatmentBuilder.writeMetadata(
+ createTechProfValueForWriteMetadata(uti.getUniTagMatch(),
+ uti.getTechnologyProfileId(), oltMeterId), 0);
}
- FilteringObjective.Builder dhcpUpstreamBuilder = (install ? builder.permit() : builder.deny())
+ FilteringObjective.Builder dhcpBuilder = (action == FlowOperation.ADD ? builder.permit() : builder.deny())
.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthType(ethType))
.addCondition(Criteria.matchIPProtocol(protocol))
@@ -356,87 +1065,124 @@
.withPriority(MAX_PRIORITY);
//VLAN changes and PCP matching need to happen only in the upstream directions
- if (upstream) {
- treatmentBuilder.setVlanId(cTag);
- if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
- dhcpUpstreamBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
+ if (direction == FlowDirection.UPSTREAM) {
+ treatmentBuilder.setVlanId(uti.getPonCTag());
+ if (!VlanId.vlanId(VlanId.NO_VID).equals(uti.getUniTagMatch())) {
+ dhcpBuilder.addCondition(Criteria.matchVlanId(uti.getUniTagMatch()));
}
- if (vlanPcp != null) {
- treatmentBuilder.setVlanPcp(vlanPcp);
+ if (uti.getUsPonCTagPriority() != -1) {
+ treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
}
}
- dhcpUpstreamBuilder.withMeta(treatmentBuilder
- .setOutput(PortNumber.CONTROLLER).build());
+ dhcpBuilder.withMeta(treatmentBuilder
+ .setOutput(PortNumber.CONTROLLER).build());
- FilteringObjective dhcpUpstream = dhcpUpstreamBuilder.add(new ObjectiveContext() {
+ FilteringObjective dhcpUpstream = dhcpBuilder.add(new ObjectiveContext() {
@Override
public void onSuccess(Objective objective) {
- log.info("DHCP {} filter for {} {}.",
- (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6, port,
- (install) ? INSTALLED : REMOVED);
- dhcpFuture.ifPresent(f -> f.complete(null));
+ log.info("{} DHCP {} filter for {}.",
+ completeFlowOpToString(action), (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
+ portWithName(port));
}
@Override
public void onError(Objective objective, ObjectiveError error) {
log.error("DHCP {} filter for {} failed {} because {}",
- (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6, port,
- (install) ? INSTALLATION : REMOVAL,
+ (ethType.equals(EthType.EtherType.IPV4.ethType())) ? V4 : V6,
+ portWithName(port),
+ action,
error);
- dhcpFuture.ifPresent(f -> f.complete(error));
+ updateConnectPointStatus(sk, null, null, OltFlowsStatus.ERROR);
}
});
- flowObjectiveService.filter(port.deviceId(), dhcpUpstream);
+ flowObjectiveService.filter(deviceId, dhcpUpstream);
+ }
+
+ private void processIgmpFilteringObjectives(DeviceId deviceId, Port port,
+ FlowOperation action, FlowDirection direction,
+ MeterId meterId, MeterId oltMeterId, int techProfileId,
+ VlanId cTag, VlanId unitagMatch, int vlanPcp) {
+
+ DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
+ TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
+ if (direction == FlowDirection.UPSTREAM) {
+
+ if (techProfileId != NONE_TP_ID) {
+ treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(null,
+ techProfileId, oltMeterId), 0);
+ }
+
+
+ if (meterId != null) {
+ treatmentBuilder.meter(meterId);
+ }
+
+ if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
+ filterBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
+ }
+
+ if (!VlanId.vlanId(VlanId.NO_VID).equals(cTag)) {
+ treatmentBuilder.setVlanId(cTag);
+ }
+
+ if (vlanPcp != -1) {
+ treatmentBuilder.setVlanPcp((byte) vlanPcp);
+ }
+ }
+
+ filterBuilder = (action == FlowOperation.ADD) ? filterBuilder.permit() : filterBuilder.deny();
+
+ FilteringObjective igmp = filterBuilder
+ .withKey(Criteria.matchInPort(port.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
+ .withMeta(treatmentBuilder
+ .setOutput(PortNumber.CONTROLLER).build())
+ .fromApp(appId)
+ .withPriority(MAX_PRIORITY)
+ .add(new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("Igmp filter for {} {}.", portWithName(port), action);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.error("Igmp filter for {} failed {} because {}.", portWithName(port), action,
+ error);
+ }
+ });
+
+ flowObjectiveService.filter(deviceId, igmp);
}
- @Override
- public void processPPPoEDFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream) {
- if (!enablePppoe) {
- log.debug("PPPoED filtering is disabled. Ignoring request.");
- return;
- }
-
- int techProfileId = NONE_TP_ID;
- VlanId cTag = VlanId.NONE;
- VlanId unitagMatch = VlanId.ANY;
- Byte vlanPcp = null;
-
- if (tagInformation != null) {
- techProfileId = tagInformation.getTechnologyProfileId();
- cTag = tagInformation.getPonCTag();
- unitagMatch = tagInformation.getUniTagMatch();
- if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
- vlanPcp = (byte) tagInformation.getUsPonCTagPriority();
- }
- }
+ private void processPPPoEDFilteringObjectives(DeviceId deviceId, Port port,
+ FlowOperation action, FlowDirection direction,
+ MeterId meterId, MeterId oltMeterId, int techProfileId,
+ VlanId cTag, VlanId unitagMatch, Byte vlanPcp) {
DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
- CompletableFuture<Object> meterFuture = new CompletableFuture<>();
- if (upstreamMeterId != null) {
- treatmentBuilder.meter(upstreamMeterId);
+ if (meterId != null) {
+ treatmentBuilder.meter(meterId);
}
if (techProfileId != NONE_TP_ID) {
- treatmentBuilder.writeMetadata(createTechProfValueForWm(cTag, techProfileId, upstreamOltMeterId), 0);
+ treatmentBuilder.writeMetadata(createTechProfValueForWriteMetadata(cTag, techProfileId, oltMeterId), 0);
}
- DefaultFilteringObjective.Builder pppoedBuilder = (install ? builder.permit() : builder.deny())
+ DefaultFilteringObjective.Builder pppoedBuilder = ((action == FlowOperation.ADD)
+ ? builder.permit() : builder.deny())
.withKey(Criteria.matchInPort(port.number()))
.addCondition(Criteria.matchEthType(EthType.EtherType.PPPoED.ethType()))
.fromApp(appId)
.withPriority(10000);
- if (upstream) {
+ if (direction == FlowDirection.UPSTREAM) {
treatmentBuilder.setVlanId(cTag);
if (!VlanId.vlanId(VlanId.NO_VID).equals(unitagMatch)) {
pppoedBuilder.addCondition(Criteria.matchVlanId(unitagMatch));
@@ -451,398 +1197,50 @@
.add(new ObjectiveContext() {
@Override
public void onSuccess(Objective objective) {
- log.info("PPPoED filter for {} {}.", port, (install) ? INSTALLED : REMOVED);
+ log.info("PPPoED filter for {} {}.", portWithName(port), action);
}
@Override
public void onError(Objective objective, ObjectiveError error) {
- log.info("PPPoED filter for {} failed {} because {}", port,
- (install) ? INSTALLATION : REMOVAL, error);
+ log.info("PPPoED filter for {} failed {} because {}", portWithName(port),
+ action, error);
}
});
- flowObjectiveService.filter(port.deviceId(), pppoed);
+ flowObjectiveService.filter(deviceId, pppoed);
}
- @Override
- public void processIgmpFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream) {
- if (upstream) {
- // for UNI ports
- if (tagInformation != null && !tagInformation.getIsIgmpRequired()) {
- log.debug("Igmp provisioning is disabled for UNI port {} for service {}",
- port, tagInformation.getServiceName());
- return;
- }
- } else {
- // for NNI ports
- if (!enableIgmpOnNni) {
- log.debug("Igmp provisioning is disabled for NNI port {}", port);
- return;
- }
- }
-
- log.debug("{} IGMP flows on {}", (install) ? "Installing" : "Removing", port);
- DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
- TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
- if (upstream) {
-
- if (tagInformation.getTechnologyProfileId() != NONE_TP_ID) {
- treatmentBuilder.writeMetadata(createTechProfValueForWm(null,
- tagInformation.getTechnologyProfileId(), upstreamOltMeterId), 0);
- }
-
-
- if (upstreamMeterId != null) {
- treatmentBuilder.meter(upstreamMeterId);
- }
-
- if (!VlanId.vlanId(VlanId.NO_VID).equals(tagInformation.getUniTagMatch())) {
- filterBuilder.addCondition(Criteria.matchVlanId(tagInformation.getUniTagMatch()));
- }
-
- if (!VlanId.vlanId(VlanId.NO_VID).equals(tagInformation.getPonCTag())) {
- treatmentBuilder.setVlanId(tagInformation.getPonCTag());
- }
-
- if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
- treatmentBuilder.setVlanPcp((byte) tagInformation.getUsPonCTagPriority());
- }
- }
-
- filterBuilder = install ? filterBuilder.permit() : filterBuilder.deny();
-
- FilteringObjective igmp = filterBuilder
- .withKey(Criteria.matchInPort(port.number()))
- .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
- .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
- .withMeta(treatmentBuilder
- .setOutput(PortNumber.CONTROLLER).build())
- .fromApp(appId)
- .withPriority(MAX_PRIORITY)
- .add(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- log.info("Igmp filter for {} {}.", port, (install) ? INSTALLED : REMOVED);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- log.error("Igmp filter for {} failed {} because {}.", port, (install) ? INSTALLATION : REMOVAL,
- error);
- }
- });
-
- flowObjectiveService.filter(port.deviceId(), igmp);
- }
-
- @Override
- public void processEapolFilteringObjectives(AccessDevicePort port, String bpId, Optional<String> oltBpId,
- CompletableFuture<ObjectiveError> filterFuture,
- VlanId vlanId, boolean install) {
-
- if (!enableEapol) {
- log.debug("Eapol filtering is disabled. Completing filteringFuture immediately for the device {}",
- port.deviceId());
- if (filterFuture != null) {
- filterFuture.complete(null);
- }
- return;
- }
- log.info("Processing EAPOL with Bandwidth profile {} on {}", bpId, port);
- BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bpId);
- BandwidthProfileInformation oltBpInfo;
- if (oltBpId.isPresent()) {
- oltBpInfo = getBandwidthProfileInformation(oltBpId.get());
- } else {
- oltBpInfo = bpInfo;
- }
- if (bpInfo == null) {
- log.warn("Bandwidth profile {} is not found. Authentication flow"
- + " will not be installed on {}", bpId, port);
- if (filterFuture != null) {
- filterFuture.complete(ObjectiveError.BADPARAMS);
- }
- return;
- }
-
- ConnectPoint cp = new ConnectPoint(port.deviceId(), port.number());
- DefaultFilteringObjective.Builder filterBuilder = DefaultFilteringObjective.builder();
- TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
- // check if meter exists and create it only for an install
- final MeterId meterId = oltMeterService.getMeterIdFromBpMapping(port.deviceId(), bpInfo.id());
- MeterId oltMeterId = null;
- if (oltBpId.isPresent()) {
- oltMeterId = oltBpId.map(id -> oltMeterService.getMeterIdFromBpMapping(port.deviceId(), id)).orElse(null);
- }
- log.info("Meter id {} for Bandwidth profile {} and OLT meter id {} for OLT Bandwidth profile {} " +
- "associated to EAPOL on {}", meterId, bpInfo.id(), oltMeterId, oltBpId, port.deviceId());
- if (meterId == null || (oltBpId.isPresent() && oltMeterId == null)) {
- if (install) {
- log.debug("Need to install meter for EAPOL with bwp {} on {}", bpInfo.id(), port);
- SubscriberFlowInfo fi = new SubscriberFlowInfo(null, port,
- new UniTagInformation.Builder()
- .setPonCTag(vlanId).build(),
- null, meterId, null, oltMeterId,
- null, bpInfo.id(), null, oltBpInfo.id());
- pendingEapolForDevice.compute(port.deviceId(), (id, queue) -> {
- if (queue == null) {
- queue = new LinkedBlockingQueue<>();
- }
- queue.add(fi);
- return queue;
- });
-
- //If false the meter is already being installed, skipping installation
- if (!oltMeterService.checkAndAddPendingMeter(port.deviceId(), bpInfo) &&
- !oltMeterService.checkAndAddPendingMeter(port.deviceId(), oltBpInfo)) {
- return;
- }
- List<BandwidthProfileInformation> bwpList = Arrays.asList(bpInfo, oltBpInfo);
- bwpList.stream().distinct().filter(Objects::nonNull)
- .forEach(bwp -> createMeterAndProceedEapol(port, bwp, filterFuture, install,
- cp, filterBuilder, treatmentBuilder));
- } else {
- // this case should not happen as the request to remove an eapol
- // flow should mean that the flow points to a meter that exists.
- // Nevertheless we can still delete the flow as we only need the
- // correct 'match' to do so.
- log.warn("Unknown meter id for bp {}, still proceeding with "
- + "delete of eapol flow on {}", bpInfo.id(), port);
- SubscriberFlowInfo fi = new SubscriberFlowInfo(null, port,
- new UniTagInformation.Builder().setPonCTag(vlanId).build(),
- null, meterId, null, oltMeterId, null,
- bpInfo.id(), null, oltBpInfo.id());
- handleEapol(filterFuture, install, cp, filterBuilder, treatmentBuilder, fi, meterId, oltMeterId);
- }
- } else {
- log.debug("Meter {} was previously created for bp {} on {}", meterId, bpInfo.id(), port);
- SubscriberFlowInfo fi = new SubscriberFlowInfo(null, port,
- new UniTagInformation.Builder().setPonCTag(vlanId).build(),
- null, meterId, null, oltMeterId, null,
- bpInfo.id(), null, oltBpInfo.id());
- handleEapol(filterFuture, install, cp, filterBuilder, treatmentBuilder, fi, meterId, oltMeterId);
- //No need for the future, meter is present.
- return;
- }
- }
-
- private void createMeterAndProceedEapol(AccessDevicePort port, BandwidthProfileInformation bwpInfo,
- CompletableFuture<ObjectiveError> filterFuture,
- boolean install, ConnectPoint cp,
- DefaultFilteringObjective.Builder filterBuilder,
- TrafficTreatment.Builder treatmentBuilder) {
- CompletableFuture<Object> meterFuture = new CompletableFuture<>();
- MeterId meterId = oltMeterService.createMeter(port.deviceId(), bwpInfo, meterFuture);
- DeviceId deviceId = port.deviceId();
- meterFuture.thenAccept(result -> {
- //for each pending eapol flow we check if the meter is there.
- pendingEapolForDevice.compute(deviceId, (id, queue) -> {
- if (queue != null && !queue.isEmpty()) {
- while (true) {
- //TODO this might return the reference and not the actual object
- // so it can be actually swapped underneath us.
- SubscriberFlowInfo fi = queue.peek();
- if (fi == null) {
- log.debug("No more subscribers eapol flows on {}", deviceId);
- queue = new LinkedBlockingQueue<>();
- break;
- }
- log.debug("handing pending eapol on {} for {}", fi.getUniPort(), fi);
- if (result == null) {
- MeterId mId = oltMeterService
- .getMeterIdFromBpMapping(port.deviceId(), fi.getUpBpInfo());
- MeterId oltMeterId = oltMeterService
- .getMeterIdFromBpMapping(port.deviceId(), fi.getUpOltBpInfo());
- if (mId != null && oltMeterId != null) {
- log.debug("Meter installation completed for subscriber on {}, " +
- "handling EAPOL trap flow", port);
- fi.setUpMeterId(mId);
- fi.setUpOltMeterId(oltMeterId);
- handleEapol(filterFuture, install, cp, filterBuilder, treatmentBuilder, fi,
- mId, oltMeterId);
- queue.remove(fi);
- } else {
- log.debug("Not all meters for {} are yet installed for EAPOL meterID {}, " +
- "oltMeterId {}", fi, meterId, oltMeterId);
- }
- } else {
- log.warn("Meter installation error while sending EAPOL trap flow to {}. " +
- "Result {} and MeterId {}", port, result, meterId);
- queue.remove(fi);
- }
- oltMeterService.removeFromPendingMeters(port.deviceId(), bwpInfo);
- }
- } else {
- log.info("No pending EAPOLs on {}", port.deviceId());
- queue = new LinkedBlockingQueue<>();
- }
- return queue;
- });
- });
- }
-
- private void handleEapol(CompletableFuture<ObjectiveError> filterFuture,
- boolean install, ConnectPoint cp,
- DefaultFilteringObjective.Builder filterBuilder,
- TrafficTreatment.Builder treatmentBuilder,
- SubscriberFlowInfo fi, MeterId mId, MeterId oltMeterId) {
- log.info("Meter {} for {} on {} exists. {} EAPOL trap flow",
- mId, fi.getUpBpInfo(), fi.getUniPort(),
- (install) ? "Installing" : "Removing");
- int techProfileId = getDefaultTechProfileId(fi.getUniPort());
- // can happen in case of removal
- if (mId != null) {
- treatmentBuilder.meter(mId);
- }
- //Authentication trap flow uses only tech profile id as write metadata value
- FilteringObjective eapol = (install ? filterBuilder.permit() : filterBuilder.deny())
- .withKey(Criteria.matchInPort(fi.getUniPort().number()))
- .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
- .withMeta(treatmentBuilder
- .writeMetadata(createTechProfValueForWm(
- fi.getTagInfo().getPonCTag(),
- techProfileId, oltMeterId), 0)
- .setOutput(PortNumber.CONTROLLER)
- .pushVlan()
- .setVlanId(fi.getTagInfo().getPonCTag())
- .build())
- .fromApp(appId)
- .withPriority(MAX_PRIORITY)
- .add(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- log.info("Eapol filter {} for {} on {} with meter {}.",
- objective.id(), (install) ? INSTALLED : REMOVED, fi.getUniPort(), mId);
- if (filterFuture != null) {
- filterFuture.complete(null);
- }
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- log.error("Eapol filter {} for {} with meter {} " +
- "failed {} because {}", objective.id(), fi.getUniPort(), mId,
- (install) ? INSTALLATION : REMOVAL,
- error);
- if (filterFuture != null) {
- filterFuture.complete(error);
- }
- }
- });
- flowObjectiveService.filter(fi.getDevId(), eapol);
- }
-
- /**
- * Installs trap filtering objectives for particular traffic types on an
- * NNI port.
- *
- * @param nniPort NNI port
- * @param install true to install, false to remove
- */
- @Override
- public void processNniFilteringObjectives(AccessDevicePort nniPort, boolean install) {
- log.info("{} flows for NNI port {}",
- install ? "Adding" : "Removing", nniPort);
- processLldpFilteringObjective(nniPort, install);
- processDhcpFilteringObjectives(nniPort, null, null, null, install, false, Optional.empty());
- processIgmpFilteringObjectives(nniPort, null, null, null, install, false);
- processPPPoEDFilteringObjectives(nniPort, null, null, null, install, false);
- }
-
-
- @Override
- public void processLldpFilteringObjective(AccessDevicePort port, boolean install) {
- DefaultFilteringObjective.Builder builder = DefaultFilteringObjective.builder();
-
- FilteringObjective lldp = (install ? builder.permit() : builder.deny())
- .withKey(Criteria.matchInPort(port.number()))
- .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
- .withMeta(DefaultTrafficTreatment.builder()
- .setOutput(PortNumber.CONTROLLER).build())
- .fromApp(appId)
- .withPriority(MAX_PRIORITY)
- .add(new ObjectiveContext() {
- @Override
- public void onSuccess(Objective objective) {
- log.info("LLDP filter for {} {}.", port, (install) ? INSTALLED : REMOVED);
- }
-
- @Override
- public void onError(Objective objective, ObjectiveError error) {
- log.error("LLDP filter for {} failed {} because {}", port, (install) ? INSTALLATION : REMOVAL,
- error);
- }
- });
-
- flowObjectiveService.filter(port.deviceId(), lldp);
- }
-
- @Override
- public ForwardingObjective.Builder createTransparentBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId meterId,
- UniTagInformation tagInfo,
- boolean upstream) {
-
+ private void processUpstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
+ FlowOperation action,
+ MeterId upstreamMeterId,
+ MeterId upstreamOltMeterId,
+ UniTagInformation uti) {
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchVlanId(tagInfo.getPonSTag())
- .matchInPort(upstream ? subscriberPort.number() : uplinkPort.number())
- .matchInnerVlanId(tagInfo.getPonCTag())
- .build();
-
- TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
- if (meterId != null) {
- tBuilder.meter(meterId);
- }
-
- TrafficTreatment treatment = tBuilder
- .setOutput(upstream ? uplinkPort.number() : subscriberPort.number())
- .writeMetadata(createMetadata(upstream ? tagInfo.getPonSTag() : tagInfo.getPonCTag(),
- tagInfo.getTechnologyProfileId(),
- upstream ? uplinkPort.number() : subscriberPort.number()), 0)
- .build();
-
- return createForwardingObjectiveBuilder(selector, treatment, MIN_PRIORITY,
- DefaultAnnotations.builder().build());
- }
-
- @Override
- public ForwardingObjective.Builder createUpBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation uniTagInformation) {
-
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchInPort(subscriberPort.number())
- .matchVlanId(uniTagInformation.getUniTagMatch())
+ .matchInPort(port.number())
+ .matchVlanId(uti.getUniTagMatch())
.build();
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
//if the subscriberVlan (cTag) is different than ANY it needs to set.
- if (uniTagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
+ if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
treatmentBuilder.pushVlan()
- .setVlanId(uniTagInformation.getPonCTag());
+ .setVlanId(uti.getPonCTag());
}
- if (uniTagInformation.getUsPonCTagPriority() != NO_PCP) {
- treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonCTagPriority());
+ if (uti.getUsPonCTagPriority() != -1) {
+ treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
}
treatmentBuilder.pushVlan()
- .setVlanId(uniTagInformation.getPonSTag());
+ .setVlanId(uti.getPonSTag());
- if (uniTagInformation.getUsPonSTagPriority() != NO_PCP) {
- treatmentBuilder.setVlanPcp((byte) uniTagInformation.getUsPonSTagPriority());
+ if (uti.getUsPonSTagPriority() != -1) {
+ treatmentBuilder.setVlanPcp((byte) uti.getUsPonSTagPriority());
}
- treatmentBuilder.setOutput(uplinkPort.number())
- .writeMetadata(createMetadata(uniTagInformation.getPonCTag(),
- uniTagInformation.getTechnologyProfileId(), uplinkPort.number()), 0L);
+ treatmentBuilder.setOutput(nniPort.number())
+ .writeMetadata(createMetadata(uti.getPonCTag(),
+ uti.getTechnologyProfileId(), nniPort.number()), 0L);
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
@@ -855,55 +1253,84 @@
annotationBuilder.set(UPSTREAM_OLT, upstreamOltMeterId.toString());
}
- return createForwardingObjectiveBuilder(selector, treatmentBuilder.build(), MIN_PRIORITY,
+ DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selector,
+ treatmentBuilder.build(), MIN_PRIORITY,
annotationBuilder.build());
+
+ ObjectiveContext context = new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("{} Upstream Data plane filter for {}.",
+ completeFlowOpToString(action), sk);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.error("Upstream Data plane filter for {} failed {} because {}.",
+ sk, action, error);
+ updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
+ }
+ };
+
+ ForwardingObjective flow = null;
+ if (action == FlowOperation.ADD) {
+ flow = flowBuilder.add(context);
+ } else if (action == FlowOperation.REMOVE) {
+ flow = flowBuilder.remove(context);
+ } else {
+ log.error("Flow action not supported: {}", action);
+ }
+
+ if (flow != null) {
+ flowObjectiveService.forward(deviceId, flow);
+ }
}
- @Override
- public ForwardingObjective.Builder createDownBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId downstreamMeterId,
- MeterId downstreamOltMeterId,
- UniTagInformation tagInformation,
- Optional<MacAddress> macAddress) {
-
+ private void processDownstreamDataFilteringObjects(DeviceId deviceId, Port port, Port nniPort,
+ FlowOperation action,
+ MeterId downstreamMeterId,
+ MeterId downstreamOltMeterId,
+ UniTagInformation uti,
+ MacAddress macAddress) {
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(port), uti);
//subscriberVlan can be any valid Vlan here including ANY to make sure the packet is tagged
TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder()
- .matchVlanId(tagInformation.getPonSTag())
- .matchInPort(uplinkPort.number())
- .matchInnerVlanId(tagInformation.getPonCTag());
+ .matchVlanId(uti.getPonSTag())
+ .matchInPort(nniPort.number())
+ .matchInnerVlanId(uti.getPonCTag());
-
- if (tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
- selectorBuilder.matchMetadata(tagInformation.getPonCTag().toShort());
+ if (uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
+ selectorBuilder.matchMetadata(uti.getPonCTag().toShort());
}
- if (tagInformation.getDsPonSTagPriority() != NO_PCP) {
- selectorBuilder.matchVlanPcp((byte) tagInformation.getDsPonSTagPriority());
+ if (uti.getDsPonCTagPriority() != -1) {
+ selectorBuilder.matchVlanPcp((byte) uti.getDsPonCTagPriority());
}
- macAddress.ifPresent(selectorBuilder::matchEthDst);
+ if (macAddress != null) {
+ selectorBuilder.matchEthDst(macAddress);
+ }
TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder()
.popVlan()
- .setOutput(subscriberPort.number());
+ .setOutput(port.number());
- treatmentBuilder.writeMetadata(createMetadata(tagInformation.getPonCTag(),
- tagInformation.getTechnologyProfileId(),
- subscriberPort.number()), 0);
+ treatmentBuilder.writeMetadata(createMetadata(uti.getPonCTag(),
+ uti.getTechnologyProfileId(),
+ port.number()), 0);
// Upstream pbit is used to remark inner vlan pbit.
// Upstream is used to avoid trusting the BNG to send the packet with correct pbit.
// this is done because ds mode 0 is used because ds mode 3 or 6 that allow for
// all pbit acceptance are not widely supported by vendors even though present in
// the OMCI spec.
- if (tagInformation.getUsPonCTagPriority() != NO_PCP) {
- treatmentBuilder.setVlanPcp((byte) tagInformation.getUsPonCTagPriority());
+ if (uti.getUsPonCTagPriority() != -1) {
+ treatmentBuilder.setVlanPcp((byte) uti.getUsPonCTagPriority());
}
- if (!VlanId.NONE.equals(tagInformation.getUniTagMatch()) &&
- tagInformation.getPonCTag().toShort() != VlanId.ANY_VALUE) {
- treatmentBuilder.setVlanId(tagInformation.getUniTagMatch());
+ if (!VlanId.NONE.equals(uti.getUniTagMatch()) &&
+ uti.getPonCTag().toShort() != VlanId.ANY_VALUE) {
+ treatmentBuilder.setVlanId(uti.getUniTagMatch());
}
DefaultAnnotations.Builder annotationBuilder = DefaultAnnotations.builder();
@@ -918,13 +1345,39 @@
annotationBuilder.set(DOWNSTREAM_OLT, downstreamOltMeterId.toString());
}
- return createForwardingObjectiveBuilder(selectorBuilder.build(), treatmentBuilder.build(), MIN_PRIORITY,
- annotationBuilder.build());
- }
+ DefaultForwardingObjective.Builder flowBuilder = createForwardingObjectiveBuilder(selectorBuilder.build(),
+ treatmentBuilder.build(), MIN_PRIORITY, annotationBuilder.build());
- @Override
- public void clearDeviceState(DeviceId deviceId) {
- pendingEapolForDevice.remove(deviceId);
+ ObjectiveContext context = new ObjectiveContext() {
+ @Override
+ public void onSuccess(Objective objective) {
+ log.info("{} Downstream Data plane filter for {}.",
+ completeFlowOpToString(action), sk);
+ }
+
+ @Override
+ public void onError(Objective objective, ObjectiveError error) {
+ log.info("Downstream Data plane filter for {} failed {} because {}.",
+ sk, action, error);
+ updateConnectPointStatus(sk, null, OltFlowsStatus.ERROR, null);
+ }
+ };
+
+ ForwardingObjective flow = null;
+ if (action == FlowOperation.ADD) {
+ flow = flowBuilder.add(context);
+ } else if (action == FlowOperation.REMOVE) {
+ flow = flowBuilder.remove(context);
+ } else {
+ log.error("Flow action not supported: {}", action);
+ }
+
+ if (flow != null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Forwarding rule {}", flow);
+ }
+ flowObjectiveService.forward(deviceId, flow);
+ }
}
private DefaultForwardingObjective.Builder createForwardingObjectiveBuilder(TrafficSelector selector,
@@ -941,70 +1394,6 @@
.withTreatment(treatment);
}
- /**
- * Returns the write metadata value including tech profile reference and innerVlan.
- * For param cVlan, null can be sent
- *
- * @param cVlan c (customer) tag of one subscriber
- * @param techProfileId tech profile id of one subscriber
- * @param upstreamOltMeterId upstream meter id for OLT device.
- * @return the write metadata value including tech profile reference and innerVlan
- */
- private Long createTechProfValueForWm(VlanId cVlan, int techProfileId, MeterId upstreamOltMeterId) {
- Long writeMetadata;
-
- if (cVlan == null || VlanId.NONE.equals(cVlan)) {
- writeMetadata = (long) techProfileId << 32;
- } else {
- writeMetadata = ((long) (cVlan.id()) << 48 | (long) techProfileId << 32);
- }
- if (upstreamOltMeterId == null) {
- return writeMetadata;
- } else {
- return writeMetadata | upstreamOltMeterId.id();
- }
- }
-
- private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
- if (bpService == null) {
- log.warn(SADIS_NOT_RUNNING);
- return null;
- }
- if (bandwidthProfile == null) {
- return null;
- }
- return bpService.get(bandwidthProfile);
- }
-
- /**
- * It will be used to support AT&T use case (for EAPOL flows).
- * If multiple services are found in uniServiceList, returns default tech profile id
- * If one service is found, returns the found one
- *
- * @param port uni port
- * @return the default technology profile id
- */
- private int getDefaultTechProfileId(AccessDevicePort port) {
- if (subsService == null) {
- log.warn(SADIS_NOT_RUNNING);
- return defaultTechProfileId;
- }
- if (port != null) {
- SubscriberAndDeviceInformation info = subsService.get(port.name());
- if (info != null && info.uniTagList().size() == 1) {
- return info.uniTagList().get(0).getTechnologyProfileId();
- }
- }
- return defaultTechProfileId;
- }
-
- /**
- * Write metadata instruction value (metadata) is 8 bytes.
- * <p>
- * MS 2 bytes: C Tag
- * Next 2 bytes: Technology Profile Id
- * Next 4 bytes: Port number (uni or nni)
- */
private Long createMetadata(VlanId innerVlan, int techProfileId, PortNumber egressPort) {
if (techProfileId == NONE_TP_ID) {
techProfileId = DEFAULT_TP_ID_DEFAULT;
@@ -1013,5 +1402,318 @@
return ((long) (innerVlan.id()) << 48 | (long) techProfileId << 32) | egressPort.toLong();
}
+ private boolean isMacLearningEnabled(SubscriberAndDeviceInformation si) {
+ AtomicBoolean requiresMacLearning = new AtomicBoolean();
+ requiresMacLearning.set(false);
+ si.uniTagList().forEach(uniTagInfo -> {
+ if (uniTagInfo.getEnableMacLearning()) {
+ requiresMacLearning.set(true);
+ }
+ });
+
+ return requiresMacLearning.get();
+ }
+
+ /**
+ * Checks whether the subscriber has the MacAddress configured or discovered.
+ *
+ * @param deviceId DeviceId for this subscriber
+ * @param port Port for this subscriber
+ * @param si SubscriberAndDeviceInformation
+ * @return boolean
+ */
+ protected boolean isMacAddressAvailable(DeviceId deviceId, Port port, SubscriberAndDeviceInformation si) {
+ AtomicBoolean isConfigured = new AtomicBoolean();
+ isConfigured.set(true);
+
+ si.uniTagList().forEach(uniTagInfo -> {
+ boolean isMacLearningEnabled = uniTagInfo.getEnableMacLearning();
+ boolean configureMac = isMacAddressValid(uniTagInfo);
+ boolean discoveredMac = false;
+ Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
+ .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
+ if (optHost.isPresent() && optHost.get().mac() != null) {
+ discoveredMac = true;
+ }
+ if (isMacLearningEnabled && !configureMac && !discoveredMac) {
+ log.debug("Awaiting for macAddress on {} for service {}",
+ portWithName(port), uniTagInfo.getServiceName());
+ isConfigured.set(false);
+ }
+ });
+
+ return isConfigured.get();
+ }
+
+ protected MacAddress getMacAddress(DeviceId deviceId, Port port, UniTagInformation uniTagInfo) {
+ boolean configuredMac = isMacAddressValid(uniTagInfo);
+ if (configuredMac) {
+ return MacAddress.valueOf(uniTagInfo.getConfiguredMacAddress());
+ } else if (uniTagInfo.getEnableMacLearning()) {
+ Optional<Host> optHost = hostService.getConnectedHosts(new ConnectPoint(deviceId, port.number()))
+ .stream().filter(host -> host.vlan().equals(uniTagInfo.getPonCTag())).findFirst();
+ if (optHost.isPresent() && optHost.get().mac() != null) {
+ return optHost.get().mac();
+ }
+ }
+ return null;
+ }
+
+ private boolean isMacAddressValid(UniTagInformation tagInformation) {
+ return tagInformation.getConfiguredMacAddress() != null &&
+ !tagInformation.getConfiguredMacAddress().trim().equals("") &&
+ !MacAddress.NONE.equals(MacAddress.valueOf(tagInformation.getConfiguredMacAddress()));
+ }
+
+ protected void updateConnectPointStatus(ServiceKey key, OltFlowsStatus eapolStatus,
+ OltFlowsStatus subscriberFlowsStatus, OltFlowsStatus dhcpStatus) {
+ try {
+ cpStatusWriteLock.lock();
+ OltPortStatus status = cpStatus.get(key);
+
+ if (status == null) {
+ status = new OltPortStatus(
+ eapolStatus != null ? eapolStatus : OltFlowsStatus.NONE,
+ subscriberFlowsStatus != null ? subscriberFlowsStatus : OltFlowsStatus.NONE,
+ dhcpStatus != null ? dhcpStatus : OltFlowsStatus.NONE
+ );
+ } else {
+ if (eapolStatus != null) {
+ status.defaultEapolStatus = eapolStatus;
+ }
+ if (subscriberFlowsStatus != null) {
+ status.subscriberFlowsStatus = subscriberFlowsStatus;
+ }
+ if (dhcpStatus != null) {
+ status.dhcpStatus = dhcpStatus;
+ }
+ }
+
+ cpStatus.put(key, status);
+ } finally {
+ cpStatusWriteLock.unlock();
+ }
+ }
+
+ protected class InternalFlowListener implements FlowRuleListener {
+ @Override
+ public void event(FlowRuleEvent event) {
+ if (appId.id() != (event.subject().appId())) {
+ return;
+ }
+
+ if (!oltDeviceService.isLocalLeader(event.subject().deviceId())) {
+ if (log.isTraceEnabled()) {
+ log.trace("ignoring flow event {} " +
+ "as not leader for {}", event, event.subject().deviceId());
+ }
+ return;
+ }
+
+ switch (event.type()) {
+ case RULE_ADDED:
+ case RULE_REMOVED:
+ Port port = getCpFromFlowRule(event.subject());
+ if (port == null) {
+ log.error("Can't find port in flow {}", event.subject());
+ return;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("flow event {} on cp {}: {}", event.type(),
+ portWithName(port), event.subject());
+ }
+ updateCpStatus(event.type(), port, event.subject());
+ return;
+ case RULE_ADD_REQUESTED:
+ case RULE_REMOVE_REQUESTED:
+ // NOTE that PENDING_ADD/REMOVE is set when we create the flowObjective
+ return;
+ default:
+ return;
+ }
+ }
+
+ protected void updateCpStatus(FlowRuleEvent.Type type, Port port, FlowRule flowRule) {
+ OltFlowsStatus status = flowRuleStatusToOltFlowStatus(type);
+ if (isDefaultEapolFlow(flowRule)) {
+ ServiceKey sk = new ServiceKey(new AccessDevicePort(port),
+ defaultEapolUniTag);
+ if (log.isTraceEnabled()) {
+ log.trace("update defaultEapolStatus {} on {}", status, sk);
+ }
+ updateConnectPointStatus(sk, status, null, null);
+ } else if (isDhcpFlow(flowRule)) {
+ ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
+ if (sk == null) {
+ return;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("update dhcpStatus {} on {}", status, sk);
+ }
+ updateConnectPointStatus(sk, null, null, status);
+ } else if (isDataFlow(flowRule)) {
+
+ if (oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()),
+ getCpFromFlowRule(flowRule).number())) {
+ // the NNI has data-plane for every subscriber, doesn't make sense to track them
+ return;
+ }
+
+ ServiceKey sk = getSubscriberKeyFromFlowRule(flowRule);
+ if (sk == null) {
+ return;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("update dataplaneStatus {} on {}", status, sk);
+ }
+ updateConnectPointStatus(sk, null, status, null);
+ }
+ }
+
+ private boolean isDefaultEapolFlow(FlowRule flowRule) {
+ EthTypeCriterion c = (EthTypeCriterion) flowRule.selector().getCriterion(Criterion.Type.ETH_TYPE);
+ if (c == null) {
+ return false;
+ }
+ if (c.ethType().equals(EthType.EtherType.EAPOL.ethType())) {
+ AtomicBoolean isDefault = new AtomicBoolean(false);
+ flowRule.treatment().allInstructions().forEach(instruction -> {
+ if (instruction.type() == L2MODIFICATION) {
+ L2ModificationInstruction modificationInstruction = (L2ModificationInstruction) instruction;
+ if (modificationInstruction.subtype() == L2ModificationInstruction.L2SubType.VLAN_ID) {
+ L2ModificationInstruction.ModVlanIdInstruction vlanInstruction =
+ (L2ModificationInstruction.ModVlanIdInstruction) modificationInstruction;
+ if (vlanInstruction.vlanId().id().equals(EAPOL_DEFAULT_VLAN)) {
+ isDefault.set(true);
+ return;
+ }
+ }
+ }
+ });
+ return isDefault.get();
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if the flow is a DHCP flow.
+ * Matches both upstream and downstream flows.
+ *
+ * @param flowRule The FlowRule to evaluate
+ * @return boolean
+ */
+ private boolean isDhcpFlow(FlowRule flowRule) {
+ IPProtocolCriterion ipCriterion = (IPProtocolCriterion) flowRule.selector()
+ .getCriterion(Criterion.Type.IP_PROTO);
+ if (ipCriterion == null) {
+ return false;
+ }
+
+ UdpPortCriterion src = (UdpPortCriterion) flowRule.selector().getCriterion(Criterion.Type.UDP_SRC);
+
+ if (src == null) {
+ return false;
+ }
+ return ipCriterion.protocol() == IPv4.PROTOCOL_UDP &&
+ (src.udpPort().toInt() == 68 || src.udpPort().toInt() == 67);
+ }
+
+ private boolean isDataFlow(FlowRule flowRule) {
+ // we consider subscriber flows the one that matches on VLAN_VID
+ // method is valid only because it's the last check after EAPOL and DHCP.
+ // this matches mcast flows as well, if we want to avoid that we can
+ // filter out the elements that have groups in the treatment or
+ // mcastIp in the selector
+ // IPV4_DST:224.0.0.22/32
+ // treatment=[immediate=[GROUP:0x1]]
+
+ return flowRule.selector().getCriterion(Criterion.Type.VLAN_VID) != null;
+ }
+
+ private Port getCpFromFlowRule(FlowRule flowRule) {
+ DeviceId deviceId = flowRule.deviceId();
+ PortCriterion inPort = (PortCriterion) flowRule.selector().getCriterion(Criterion.Type.IN_PORT);
+ if (inPort != null) {
+ PortNumber port = inPort.port();
+ return deviceService.getPort(deviceId, port);
+ }
+ return null;
+ }
+
+ private ServiceKey getSubscriberKeyFromFlowRule(FlowRule flowRule) {
+ Port flowPort = getCpFromFlowRule(flowRule);
+ SubscriberAndDeviceInformation si = subsService.get(getPortName(flowPort));
+
+ Boolean isNni = oltDeviceService.isNniPort(deviceService.getDevice(flowRule.deviceId()), flowPort.number());
+ if (si == null && !isNni) {
+ log.error("Subscriber information not found in sadis for port {}", portWithName(flowPort));
+ return null;
+ }
+
+ if (isNni) {
+ return new ServiceKey(new AccessDevicePort(flowPort), nniUniTag);
+ }
+
+ Optional<UniTagInformation> found = Optional.empty();
+ VlanId flowVlan = null;
+ if (isDhcpFlow(flowRule)) {
+ // we need to make a special case for DHCP as in the ATT workflow DHCP flows don't match on tags
+ L2ModificationInstruction.ModVlanIdInstruction instruction =
+ (L2ModificationInstruction.ModVlanIdInstruction) flowRule.treatment().immediate().get(1);
+ flowVlan = instruction.vlanId();
+ } else {
+ // for now we assume that if it's not DHCP it's dataplane (or at least tagged)
+ VlanIdCriterion vlanIdCriterion =
+ (VlanIdCriterion) flowRule.selector().getCriterion(Criterion.Type.VLAN_VID);
+ if (vlanIdCriterion == null) {
+ log.warn("cannot match the flow to a subscriber service as it does not carry vlans");
+ return null;
+ }
+ flowVlan = vlanIdCriterion.vlanId();
+ }
+
+ VlanId finalFlowVlan = flowVlan;
+ found = si.uniTagList().stream().filter(uti ->
+ uti.getPonCTag().equals(finalFlowVlan) ||
+ uti.getPonSTag().equals(finalFlowVlan) ||
+ uti.getUniTagMatch().equals(finalFlowVlan)
+ ).findFirst();
+
+
+ if (found.isEmpty()) {
+ log.warn("Cannot map flow rule {} to Service in {}", flowRule, si);
+ }
+
+ return found.isPresent() ? new ServiceKey(new AccessDevicePort(flowPort), found.get()) : null;
+
+ }
+
+ private OltFlowsStatus flowRuleStatusToOltFlowStatus(FlowRuleEvent.Type type) {
+ switch (type) {
+ case RULE_ADD_REQUESTED:
+ return OltFlowsStatus.PENDING_ADD;
+ case RULE_ADDED:
+ return OltFlowsStatus.ADDED;
+ case RULE_REMOVE_REQUESTED:
+ return OltFlowsStatus.PENDING_REMOVE;
+ case RULE_REMOVED:
+ return OltFlowsStatus.REMOVED;
+ default:
+ return OltFlowsStatus.NONE;
+ }
+ }
+ }
+
+ protected void bindSadisService(SadisService service) {
+ this.subsService = service.getSubscriberInfoService();
+ this.bpService = service.getBandwidthProfileService();
+ log.info("Sadis service is loaded");
+ }
+
+ protected void unbindSadisService(SadisService service) {
+ this.subsService = null;
+ this.bpService = null;
+ log.info("Sadis service is unloaded");
+ }
}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceInterface.java b/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceInterface.java
new file mode 100644
index 0000000..e55c789
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltFlowServiceInterface.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.Map;
+
+/**
+ * Interface for flow installation/removal methods for different types of traffic.
+ */
+public interface OltFlowServiceInterface {
+ /**
+ * Installs or removes default flows for the port to trap to controller.
+ * @param sub the information about the port
+ * @param defaultBpId the default bandwidth profile
+ * @param oltBandwidthProfile the olt bandwidth profile.
+ * @return true if successful
+ */
+ boolean handleBasicPortFlows(
+ DiscoveredSubscriber sub, String defaultBpId, String oltBandwidthProfile);
+
+ /**
+ * Installs or removes subscriber specific flows.
+ * @param sub the information about the subscriber
+ * @param defaultBpId the default bandwidth profile
+ * @param multicastServiceName the multicast service name.
+ * @return true if successful
+ */
+ boolean handleSubscriberFlows(DiscoveredSubscriber sub, String defaultBpId, String multicastServiceName);
+
+ /**
+ * Installs or removes flows on the NNI port.
+ * @param device the OLT
+ * @param port the NNI port
+ * @param action the operatio, ADD or REMOVE.
+ */
+ void handleNniFlows(Device device, Port port, OltFlowService.FlowOperation action);
+
+ /**
+ * Checks if the default eapol flow is already installed.
+ * @param port the port
+ * @return true if installed, false otherwise.
+ */
+ boolean hasDefaultEapol(Port port);
+
+ /**
+ * Checks if the dhcp flows are installed.
+ * @param port the port
+ * @param uti the UniTagInformation to check for
+ * @return true if installed, false otherwise.
+ */
+ boolean hasDhcpFlows(Port port, UniTagInformation uti);
+
+ /**
+ * Checks if the subscriber flows are installed.
+ * @param port the port
+ * @param uti the UniTagInformation to check for
+ * @return true if installed, false otherwise.
+ */
+ boolean hasSubscriberFlows(Port port, UniTagInformation uti);
+
+ /**
+ * Removes all device flows.
+ * @param deviceId the olt.
+ */
+ void purgeDeviceFlows(DeviceId deviceId);
+
+ /**
+ * Return the status of installation on the connect points.
+ * @return the status map
+ */
+ Map<ServiceKey, OltPortStatus> getConnectPointStatus();
+
+ /**
+ * Returns all the programmed subscribers.
+ * @return the subscribers
+ */
+ Map<ServiceKey, UniTagInformation> getProgrammedSubscribers();
+
+ /**
+ * Returns the list of requested subscribers to be installed with status.
+ * @return the list
+ */
+ Map<ServiceKey, Boolean> getRequestedSubscribers();
+
+ /**
+ * Returns if a subscriber on a port is provisioned or not.
+ * @param cp the port
+ * @return true if any service on that port is provisioned, false otherwise
+ */
+ boolean isSubscriberServiceProvisioned(AccessDevicePort cp);
+
+ /**
+ * Returns if a subscriber on a port is provisioned or not.
+ * @param sk the SubscriberKey
+ * @return true if provisioned, false otherwise
+ */
+ boolean isSubscriberServiceProvisioned(ServiceKey sk);
+
+ /**
+ * Updates the subscriber provisioning status.
+ * @param sk the SubscriberKey
+ * @param status the next status
+ */
+ void updateProvisionedSubscriberStatus(ServiceKey sk, Boolean status);
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltMeterService.java b/impl/src/main/java/org/opencord/olt/impl/OltMeterService.java
index 4b88344..be04449 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OltMeterService.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OltMeterService.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,47 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.opencord.olt.impl;
-import static com.google.common.base.Strings.isNullOrEmpty;
-import static java.util.stream.Collectors.collectingAndThen;
-import static java.util.stream.Collectors.groupingBy;
-import static java.util.stream.Collectors.mapping;
-import static java.util.stream.Collectors.toSet;
-import static org.onlab.util.Tools.get;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.opencord.olt.impl.OsgiPropertyConstants.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Properties;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
+import com.google.common.collect.ImmutableMap;
import org.onlab.util.KryoNamespace;
import org.onlab.util.Tools;
import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flowobjective.ObjectiveError;
import org.onosproject.net.meter.Band;
import org.onosproject.net.meter.DefaultBand;
import org.onosproject.net.meter.DefaultMeterRequest;
@@ -66,12 +35,14 @@
import org.onosproject.net.meter.MeterListener;
import org.onosproject.net.meter.MeterRequest;
import org.onosproject.net.meter.MeterService;
+import org.onosproject.net.meter.MeterState;
import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMultimap;
import org.onosproject.store.service.Serializer;
import org.onosproject.store.service.StorageService;
-import org.opencord.olt.internalapi.AccessDeviceMeterService;
import org.opencord.sadis.BandwidthProfileInformation;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
@@ -79,219 +50,386 @@
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
import org.slf4j.Logger;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
-/**
- * Provisions Meters on access devices.
- */
+import static com.google.common.base.Strings.isNullOrEmpty;
+import static org.onlab.util.Tools.get;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.opencord.olt.impl.OsgiPropertyConstants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
@Component(immediate = true, property = {
DELETE_METERS + ":Boolean=" + DELETE_METERS_DEFAULT,
ZERO_REFERENCE_METER_COUNT + ":Integer=" + ZERO_REFERENCE_METER_COUNT_DEFAULT,
- })
-public class OltMeterService implements AccessDeviceMeterService {
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected MeterService meterService;
+})
+public class OltMeterService implements OltMeterServiceInterface {
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected ComponentConfigService componentConfigService;
+ protected ComponentConfigService cfgService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected StorageService storageService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected DeviceService deviceService;
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL,
+ bind = "bindSadisService",
+ unbind = "unbindSadisService",
+ policy = ReferencePolicy.DYNAMIC)
+ protected volatile SadisService sadisService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected ClusterService clusterService;
+ protected MeterService meterService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected MastershipService mastershipService;
+ protected OltDeviceServiceInterface oltDeviceService;
- @Reference(cardinality = ReferenceCardinality.MANDATORY)
- protected LeadershipService leadershipService;
+ private final Logger log = getLogger(getClass());
+ protected BaseInformationService<BandwidthProfileInformation> bpService;
+ private ApplicationId appId;
+ private static final String APP_NAME = "org.opencord.olt";
+ private final ReentrantReadWriteLock programmedMeterLock = new ReentrantReadWriteLock();
+ private final Lock programmedMeterWriteLock = programmedMeterLock.writeLock();
+ private final Lock programmedMeterReadLock = programmedMeterLock.readLock();
+
+ /**
+ * Programmed Meters status map.
+ * Keeps track of which meter is programmed on which device for which BandwidthProfile.
+ * The String key is the BandwidthProfile
+ */
+ protected Map<DeviceId, Map<String, MeterData>> programmedMeters;
+
+ private final MeterListener meterListener = new InternalMeterListener();
+ protected ExecutorService pendingRemovalMetersExecutor =
+ Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
+ "pending-removal-meters-%d", log));
+
+ /**
+ * Map that contains a list of meters that needs to be removed.
+ * We wait to get 3 METER_REFERENCE_COUNT_ZERO events before removing the meter
+ * so that we're sure no flow is referencing it.
+ */
+ protected Map<DeviceId, Map<MeterKey, AtomicInteger>> pendingRemoveMeters;
+
+ /**
+ * Number of consecutive meter events with empty reference count
+ * after which a meter gets removed from the device.
+ */
+ protected int zeroReferenceMeterCount = 3;
/**
* Delete meters when reference count drops to zero.
*/
protected boolean deleteMeters = DELETE_METERS_DEFAULT;
- /**
- * Number of Zero References received before deleting the meter.
- */
- protected int zeroReferenceMeterCount = ZERO_REFERENCE_METER_COUNT_DEFAULT;
-
- private ApplicationId appId;
- private static final String APP_NAME = "org.opencord.olt";
-
- private final MeterListener meterListener = new InternalMeterListener();
-
- private final Logger log = getLogger(getClass());
-
- protected ExecutorService eventExecutor;
-
- protected Map<DeviceId, Set<BandwidthProfileInformation>> pendingMeters;
- protected Map<DeviceId, Map<MeterKey, AtomicInteger>> pendingRemoveMeters;
- protected ConsistentMultimap<String, MeterKey> bpInfoToMeter;
-
@Activate
public void activate(ComponentContext context) {
- eventExecutor = Executors.newFixedThreadPool(5, groupedThreads("onos/olt",
- "events-%d", log));
appId = coreService.registerApplication(APP_NAME);
modified(context);
-
KryoNamespace serializer = KryoNamespace.newBuilder()
.register(KryoNamespaces.API)
+ .register(List.class)
+ .register(MeterData.class)
+ .register(MeterState.class)
.register(MeterKey.class)
- .register(BandwidthProfileInformation.class)
.build();
- bpInfoToMeter = storageService.<String, MeterKey>consistentMultimapBuilder()
- .withName("volt-bp-info-to-meter")
- .withSerializer(Serializer.using(serializer))
- .withApplicationId(appId)
- .build();
-
- meterService.addListener(meterListener);
- componentConfigService.registerProperties(getClass());
- pendingMeters = storageService.<DeviceId, Set<BandwidthProfileInformation>>consistentMapBuilder()
- .withName("volt-pending-meters")
+ programmedMeters = storageService.<DeviceId, Map<String, MeterData>>consistentMapBuilder()
+ .withName("volt-programmed-meters")
.withSerializer(Serializer.using(serializer))
.withApplicationId(appId)
.build().asJavaMap();
+
pendingRemoveMeters = storageService.<DeviceId, Map<MeterKey, AtomicInteger>>consistentMapBuilder()
.withName("volt-pending-remove-meters")
.withSerializer(Serializer.using(serializer))
.withApplicationId(appId)
.build().asJavaMap();
- log.info("Olt Meter service started");
- }
- @Deactivate
- public void deactivate() {
- meterService.removeListener(meterListener);
- }
+ cfgService.registerProperties(getClass());
+ meterService.addListener(meterListener);
+
+ log.info("Started");
+ }
@Modified
public void modified(ComponentContext context) {
Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
- Boolean d = Tools.isPropertyEnabled(properties, "deleteMeters");
+ Boolean d = Tools.isPropertyEnabled(properties, DELETE_METERS);
if (d != null) {
deleteMeters = d;
}
- String zeroReferenceMeterCountNew = get(properties, ZERO_REFERENCE_METER_COUNT);
- zeroReferenceMeterCount = isNullOrEmpty(zeroReferenceMeterCountNew) ? ZERO_REFERENCE_METER_COUNT_DEFAULT :
- Integer.parseInt(zeroReferenceMeterCountNew.trim());
+ String zeroCount = get(properties, ZERO_REFERENCE_METER_COUNT);
+ int oldSubscriberProcessingThreads = zeroReferenceMeterCount;
+ zeroReferenceMeterCount = isNullOrEmpty(zeroCount) ?
+ oldSubscriberProcessingThreads : Integer.parseInt(zeroCount.trim());
+ log.info("Modified. Values = deleteMeters: {}, zeroReferenceMeterCount: {}",
+ deleteMeters, zeroReferenceMeterCount);
+ }
+
+ @Deactivate
+ public void deactivate(ComponentContext context) {
+ cfgService.unregisterProperties(getClass(), false);
+ meterService.removeListener(meterListener);
+ log.info("Stopped");
}
@Override
- public ImmutableMap<String, Collection<MeterKey>> getBpMeterMappings() {
- return bpInfoToMeter.stream()
- .collect(collectingAndThen(
- groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toSet())),
- ImmutableMap::copyOf));
+ public Map<DeviceId, Map<String, MeterData>> getProgrammedMeters() {
+ try {
+ programmedMeterReadLock.lock();
+ return ImmutableMap.copyOf(programmedMeters);
+ } finally {
+ programmedMeterReadLock.unlock();
+ }
}
- boolean addMeterIdToBpMapping(DeviceId deviceId, MeterId meterId, String bandwidthProfile) {
- log.debug("adding bp {} to meter {} mapping for device {}",
- bandwidthProfile, meterId, deviceId);
- return bpInfoToMeter.put(bandwidthProfile, MeterKey.key(deviceId, meterId));
+ /**
+ * Will create a meter if needed and return true once available.
+ *
+ * @param deviceId DeviceId
+ * @param bandwidthProfile Bandwidth Profile Id
+ * @return true
+ */
+ @Override
+ public synchronized boolean createMeter(DeviceId deviceId, String bandwidthProfile) {
+
+ // NOTE it is possible that hasMeterByBandwidthProfile returns false has the meter is in PENDING_ADD
+ // then a different thread changes the meter to ADDED
+ // and thus hasPendingMeterByBandwidthProfile return false as well and we install the meter a second time
+ // this causes an inconsistency between the existing meter and meterId stored in the map
+
+ if (!hasMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
+ // NOTE this is at trace level as it's constantly called by the queue processor
+ if (log.isTraceEnabled()) {
+ log.trace("Missing meter for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
+ }
+
+ if (!hasPendingMeterByBandwidthProfile(deviceId, bandwidthProfile)) {
+ createMeterForBp(deviceId, bandwidthProfile);
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("Meter is not yet available for {} on device {}",
+ bandwidthProfile, deviceId);
+ }
+ return false;
+ }
+ log.debug("Meter found for BandwidthProfile {} on device {}", bandwidthProfile, deviceId);
+ return true;
}
@Override
- public MeterId getMeterIdFromBpMapping(DeviceId deviceId, String bandwidthProfile) {
- if (bandwidthProfile == null) {
- log.warn("Bandwidth Profile requested is null");
- return null;
+ public boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si) {
+ // Each UniTagInformation has up to 4 meters,
+ // check and/or create all of them
+ AtomicBoolean waitingOnMeter = new AtomicBoolean();
+ waitingOnMeter.set(false);
+ Map<String, List<String>> pendingMeters = new HashMap<>();
+ si.uniTagList().forEach(uniTagInfo -> {
+ String serviceName = uniTagInfo.getServiceName();
+ pendingMeters.put(serviceName, new LinkedList<>());
+ String usBp = uniTagInfo.getUpstreamBandwidthProfile();
+ String dsBp = uniTagInfo.getDownstreamBandwidthProfile();
+ String oltUBp = uniTagInfo.getDownstreamOltBandwidthProfile();
+ String oltDsBp = uniTagInfo.getUpstreamOltBandwidthProfile();
+ if (!createMeter(deviceId, usBp)) {
+ pendingMeters.get(serviceName).add(usBp);
+ waitingOnMeter.set(true);
+ }
+ if (!createMeter(deviceId, dsBp)) {
+ pendingMeters.get(serviceName).add(usBp);
+ waitingOnMeter.set(true);
+ }
+ if (!createMeter(deviceId, oltUBp)) {
+ pendingMeters.get(serviceName).add(usBp);
+ waitingOnMeter.set(true);
+ }
+ if (!createMeter(deviceId, oltDsBp)) {
+ pendingMeters.get(serviceName).add(usBp);
+ waitingOnMeter.set(true);
+ }
+ });
+ if (waitingOnMeter.get()) {
+ if (log.isTraceEnabled()) {
+ log.trace("Meters {} on device {} are not " +
+ "installed yet (requested by subscriber {})",
+ pendingMeters, deviceId, si.id());
+ }
+ return false;
}
- if (bpInfoToMeter.get(bandwidthProfile) == null) {
- log.warn("Bandwidth Profile '{}' is not present in the map",
- bandwidthProfile);
- return null;
- }
- if (bpInfoToMeter.get(bandwidthProfile).value().isEmpty()) {
- log.warn("Bandwidth Profile '{}' is not currently mapped to a meter",
- bandwidthProfile);
- return null;
- }
+ return true;
+ }
- Optional<? extends MeterKey> meterKeyForDevice = bpInfoToMeter.get(bandwidthProfile).value()
- .stream()
- .filter(meterKey -> meterKey.deviceId().equals(deviceId))
- .findFirst();
- if (meterKeyForDevice.isPresent()) {
- log.debug("Found meter {} for bandwidth profile {} on {}",
- meterKeyForDevice.get().meterId(), bandwidthProfile, deviceId);
- return meterKeyForDevice.get().meterId();
- } else {
- log.warn("Bandwidth Profile '{}' is not currently mapped to a meter on {} , {}",
- bandwidthProfile, deviceId, bpInfoToMeter.get(bandwidthProfile).value());
- return null;
+ /**
+ * Returns true if a meter is present in the programmed meters map, only if status is ADDED.
+ *
+ * @param deviceId the DeviceId on which to look for the meter
+ * @param bandwidthProfile the Bandwidth profile associated with this meter
+ * @return true if the meter is found
+ */
+ public boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
+ try {
+ programmedMeterReadLock.lock();
+ Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
+ if (metersOnDevice == null || metersOnDevice.isEmpty()) {
+ return false;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("added metersOnDevice {}: {}", deviceId, metersOnDevice);
+ }
+ return metersOnDevice.get(bandwidthProfile) != null &&
+ metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED);
+ } finally {
+ programmedMeterReadLock.unlock();
+ }
+ }
+
+ public boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
+ try {
+ programmedMeterReadLock.lock();
+ Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
+ if (metersOnDevice == null || metersOnDevice.isEmpty()) {
+ return false;
+ }
+ if (log.isTraceEnabled()) {
+ log.trace("pending metersOnDevice {}: {}", deviceId, metersOnDevice);
+ }
+ // NOTE that we check in order if the meter was ADDED and if it wasn't we check for PENDING_ADD
+ // it is possible that a different thread move the meter state from PENDING_ADD
+ // to ADDED between these two checks
+ // to avoid creating the meter twice we return true event if the meter is already added
+ return metersOnDevice.get(bandwidthProfile) != null && (
+ metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.ADDED) ||
+ metersOnDevice.get(bandwidthProfile).getMeterStatus().equals(MeterState.PENDING_ADD)
+ );
+
+ } finally {
+ programmedMeterReadLock.unlock();
+ }
+ }
+
+ public MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfile) {
+ try {
+ programmedMeterReadLock.lock();
+ Map<String, MeterData> metersOnDevice = programmedMeters.get(deviceId);
+ if (metersOnDevice == null || metersOnDevice.isEmpty()) {
+ return null;
+ }
+ MeterData meterData = metersOnDevice.get(bandwidthProfile);
+ if (meterData == null || meterData.getMeterStatus() != MeterState.ADDED) {
+ return null;
+ }
+ if (log.isTraceEnabled()) {
+ log.debug("Found meter {} on device {} for bandwidth profile {}",
+ meterData.getMeterId(), deviceId, bandwidthProfile);
+ }
+ return meterData.getMeterId();
+ } finally {
+ programmedMeterReadLock.unlock();
}
}
@Override
- public ImmutableSet<MeterKey> getProgMeters() {
- return bpInfoToMeter.stream()
- .map(Map.Entry::getValue)
- .collect(ImmutableSet.toImmutableSet());
+ public void purgeDeviceMeters(DeviceId deviceId) {
+ log.debug("Purging meters on device {}", deviceId);
+ meterService.purgeMeters(deviceId);
+
+ // after we purge the meters we also need to clear the map
+ try {
+ programmedMeterWriteLock.lock();
+ programmedMeters.remove(deviceId);
+ } finally {
+ programmedMeterWriteLock.unlock();
+ }
+
+ // and clear the event count
+ // NOTE do we need a lock?
+ pendingRemoveMeters.remove(deviceId);
}
- @Override
- public MeterId createMeter(DeviceId deviceId, BandwidthProfileInformation bpInfo,
- CompletableFuture<Object> meterFuture) {
- log.debug("Creating meter on {} for {}", deviceId, bpInfo);
+ /**
+ * Creates of a meter for a given Bandwidth Profile on a given device.
+ *
+ * @param deviceId the DeviceId
+ * @param bandwidthProfile the BandwidthProfile ID
+ */
+ public void createMeterForBp(DeviceId deviceId, String bandwidthProfile) {
+ // adding meter in pending state to the programmedMeter map
+ try {
+ programmedMeterWriteLock.lock();
+ programmedMeters.compute(deviceId, (d, deviceMeters) -> {
+
+ if (deviceMeters == null) {
+ deviceMeters = new HashMap<>();
+ }
+ // NOTE that this method is only called after verifying a
+ // meter for this BP does not already exist
+ MeterData meterData = new MeterData(
+ null,
+ MeterState.PENDING_ADD,
+ bandwidthProfile
+ );
+ deviceMeters.put(bandwidthProfile, meterData);
+
+ return deviceMeters;
+ });
+ } finally {
+ programmedMeterWriteLock.unlock();
+ }
+
+ BandwidthProfileInformation bpInfo = getBandwidthProfileInformation(bandwidthProfile);
if (bpInfo == null) {
- log.warn("Requested bandwidth profile on {} information is NULL", deviceId);
- meterFuture.complete(ObjectiveError.BADPARAMS);
- return null;
+ log.error("BandwidthProfile {} information not found in sadis", bandwidthProfile);
+ return;
}
- MeterId meterId = getMeterIdFromBpMapping(deviceId, bpInfo.id());
- if (meterId != null) {
- log.debug("Meter {} was previously created for bp {}", meterId, bpInfo.id());
- meterFuture.complete(null);
- return meterId;
+ log.info("Creating meter for BandwidthProfile {} on device {}", bpInfo.id(), deviceId);
+
+ if (log.isTraceEnabled()) {
+ log.trace("BandwidthProfile: {}", bpInfo);
}
List<Band> meterBands = createMeterBands(bpInfo);
- final AtomicReference<MeterId> meterIdRef = new AtomicReference<>();
+ CompletableFuture<Object> meterFuture = new CompletableFuture<>();
+
MeterRequest meterRequest = DefaultMeterRequest.builder()
.withBands(meterBands)
.withUnit(Meter.Unit.KB_PER_SEC)
.withContext(new MeterContext() {
@Override
public void onSuccess(MeterRequest op) {
- log.debug("Meter {} for {} is installed on the device {}",
- meterIdRef.get(), bpInfo.id(), deviceId);
- boolean added = addMeterIdToBpMapping(deviceId, meterIdRef.get(), bpInfo.id());
- if (added) {
- meterFuture.complete(null);
- } else {
- log.error("Failed to add Meter {} for {} on {} to the meter-bandwidth mapping",
- meterIdRef.get(), bpInfo.id(), deviceId);
- meterFuture.complete(ObjectiveError.UNKNOWN);
- }
+ log.info("Meter for BandwidthProfile {} is installed on the device {}",
+ bandwidthProfile, deviceId);
+ meterFuture.complete(null);
}
@Override
public void onError(MeterRequest op, MeterFailReason reason) {
- log.error("Failed installing meter {} on {} for {}",
- meterIdRef.get(), deviceId, bpInfo.id());
- bpInfoToMeter.remove(bpInfo.id(),
- MeterKey.key(deviceId, meterIdRef.get()));
+ log.error("Failed installing meter on {} for {}",
+ deviceId, bandwidthProfile);
meterFuture.complete(reason);
}
})
@@ -300,63 +438,34 @@
.burst()
.add();
+ // creating the meter
Meter meter = meterService.submit(meterRequest);
- meterIdRef.set(meter.id());
- log.info("Meter {} created and sent for installation on {} for {}",
- meter.id(), deviceId, bpInfo);
- return meter.id();
- }
- @Override
- public void removeFromPendingMeters(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
- if (deviceId == null) {
- return;
- }
- pendingMeters.computeIfPresent(deviceId, (id, bwps) -> {
- bwps.remove(bwpInfo);
- return bwps;
- });
- }
-
- @Override
- public synchronized boolean checkAndAddPendingMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
- if (bwpInfo == null) {
- log.debug("Bandwidth profile is null for device: {}", deviceId);
- return false;
- }
- if (pendingMeters.containsKey(deviceId)
- && pendingMeters.get(deviceId).contains(bwpInfo)) {
- log.debug("Meter is already pending on {} with bp {}",
- deviceId, bwpInfo);
- return false;
- }
- log.debug("Adding bandwidth profile {} to pending on {}",
- bwpInfo, deviceId);
- pendingMeters.compute(deviceId, (id, bwps) -> {
- if (bwps == null) {
- bwps = new HashSet<>();
+ // wait for the meter to be completed
+ meterFuture.thenAccept(error -> {
+ if (error != null) {
+ log.error("Cannot create meter, TODO address me");
}
- bwps.add(bwpInfo);
- return bwps;
+
+ // then update the map with the MeterId
+ try {
+ programmedMeterWriteLock.lock();
+ programmedMeters.compute(deviceId, (d, entry) -> {
+ if (entry != null) {
+ entry.compute(bandwidthProfile, (bp, meterData) -> {
+ if (meterData != null) {
+ meterData.setMeterCellId(meter.meterCellId());
+ meterData.setMeterStatus(MeterState.ADDED);
+ }
+ return meterData;
+ });
+ }
+ return entry;
+ });
+ } finally {
+ programmedMeterWriteLock.unlock();
+ }
});
-
- return true;
- }
-
- @Override
- public void clearMeters(DeviceId deviceId) {
- log.debug("Removing all meters for device {}", deviceId);
- clearDeviceState(deviceId);
- meterService.purgeMeters(deviceId);
- }
-
- @Override
- public void clearDeviceState(DeviceId deviceId) {
- log.info("Clearing local device state for {}", deviceId);
- pendingRemoveMeters.remove(deviceId);
- removeMetersFromBpMapping(deviceId);
- //Following call handles cornercase of OLT delete during meter provisioning
- pendingMeters.remove(deviceId);
}
private List<Band> createMeterBands(BandwidthProfileInformation bpInfo) {
@@ -408,119 +517,137 @@
.build();
}
- private void removeMeterFromBpMapping(MeterKey meterKey) {
- List<Map.Entry<String, MeterKey>> meters = bpInfoToMeter.stream()
- .filter(e -> e.getValue().equals(meterKey))
- .collect(Collectors.toList());
-
- meters.forEach(e -> bpInfoToMeter.remove(e.getKey(), e.getValue()));
- }
-
- private void removeMetersFromBpMapping(DeviceId deviceId) {
- List<Map.Entry<String, MeterKey>> meters = bpInfoToMeter.stream()
- .filter(e -> e.getValue().deviceId().equals(deviceId))
- .collect(Collectors.toList());
-
- meters.forEach(e -> bpInfoToMeter.remove(e.getKey(), e.getValue()));
- }
-
- /**
- * Checks for mastership or falls back to leadership on deviceId.
- * If the device is available use mastership,
- * otherwise fallback on leadership.
- * Leadership on the device topic is needed because the master can be NONE
- * in case the device went away, we still need to handle events
- * consistently
- */
- private boolean isLocalLeader(DeviceId deviceId) {
- if (deviceService.isAvailable(deviceId)) {
- return mastershipService.isLocalMaster(deviceId);
- } else {
- // Fallback with Leadership service - device id is used as topic
- NodeId leader = leadershipService.runForLeadership(
- deviceId.toString()).leaderNodeId();
- // Verify if this node is the leader
- return clusterService.getLocalNode().id().equals(leader);
+ private BandwidthProfileInformation getBandwidthProfileInformation(String bandwidthProfile) {
+ if (!checkSadisRunning()) {
+ return null;
}
+ if (bandwidthProfile == null) {
+ return null;
+ }
+ return bpService.get(bandwidthProfile);
+ }
+
+ private boolean checkSadisRunning() {
+ if (bpService == null) {
+ log.warn("Sadis is not running");
+ return false;
+ }
+ return true;
}
private class InternalMeterListener implements MeterListener {
-
@Override
public void event(MeterEvent meterEvent) {
- eventExecutor.execute(() -> {
+ pendingRemovalMetersExecutor.execute(() -> {
+
Meter meter = meterEvent.subject();
- if (meter == null) {
- log.error("Meter in event {} is null", meterEvent);
+ if (!appId.equals(meter.appId())) {
return;
}
- if (isLocalLeader(meter.deviceId())) {
- MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
- if (deleteMeters &&
- MeterEvent.Type.METER_REFERENCE_COUNT_ZERO.equals(meterEvent.type())) {
- log.info("Zero Count Meter Event is received. Meter is {} on {}",
- meter.id(), meter.deviceId());
- incrementMeterCount(meter.deviceId(), key);
- if (appId.equals(meter.appId()) && pendingRemoveMeters.get(meter.deviceId())
- .get(key).get() == zeroReferenceMeterCount) {
- log.info("Deleting unreferenced, no longer programmed Meter {} on {}",
- meter.id(), meter.deviceId());
+ if (log.isTraceEnabled()) {
+ log.trace("Received meter event {}", meterEvent);
+ }
+ MeterKey key = MeterKey.key(meter.deviceId(), meter.id());
+ if (meterEvent.type().equals(MeterEvent.Type.METER_REFERENCE_COUNT_ZERO)) {
+ if (!oltDeviceService.isLocalLeader(meter.deviceId())) {
+ if (log.isTraceEnabled()) {
+ log.trace("ignoring meter event {} " +
+ "as not leader for {}", meterEvent, meter.deviceId());
+ }
+ return;
+ }
+ log.info("Zero Count Reference event is received for meter {} on {}, " +
+ "incrementing counter",
+ meter.id(), meter.deviceId());
+ incrementMeterCount(meter.deviceId(), key);
+ if (pendingRemoveMeters.get(meter.deviceId())
+ .get(key).get() == zeroReferenceMeterCount) {
+ // only delete the meters if the app is configured to do so
+ if (deleteMeters) {
+ log.info("Meter {} on device {} is unused, removing it", meter.id(), meter.deviceId());
deleteMeter(meter.deviceId(), meter.id());
}
}
- if (MeterEvent.Type.METER_REMOVED.equals(meterEvent.type())) {
- log.info("Meter Removed Event is received for {} on {}",
- meter.id(), meter.deviceId());
- pendingRemoveMeters.computeIfPresent(meter.deviceId(),
- (id, meters) -> {
- if (meters.get(key) == null) {
- log.info("Meters is not pending " +
- "{} on {}", key, id);
- return meters;
- }
- meters.remove(key);
- return meters;
- });
- removeMeterFromBpMapping(key);
- }
- } else {
- log.trace("Ignoring meter event, not leader of {}, {}", meter.deviceId(), meterEvent);
+ }
+
+ if (meterEvent.type().equals(MeterEvent.Type.METER_REMOVED)) {
+ removeMeterCount(meter, key);
}
});
}
+ private void removeMeterCount(Meter meter, MeterKey key) {
+ pendingRemoveMeters.computeIfPresent(meter.deviceId(),
+ (id, meters) -> {
+ if (meters.get(key) == null) {
+ log.info("Meters is not pending " +
+ "{} on {}", key, id);
+ return meters;
+ }
+ meters.remove(key);
+ return meters;
+ });
+ }
+
private void incrementMeterCount(DeviceId deviceId, MeterKey key) {
if (key == null) {
return;
}
pendingRemoveMeters.compute(deviceId,
- (id, meters) -> {
- if (meters == null) {
- meters = new HashMap<>();
+ (id, meters) -> {
+ if (meters == null) {
+ meters = new HashMap<>();
- }
- if (meters.get(key) == null) {
- meters.put(key, new AtomicInteger(1));
- }
- meters.get(key).addAndGet(1);
- return meters;
- });
+ }
+ if (meters.get(key) == null) {
+ meters.put(key, new AtomicInteger(1));
+ }
+ meters.get(key).addAndGet(1);
+ return meters;
+ });
+ }
+ }
+
+ private void deleteMeter(DeviceId deviceId, MeterId meterId) {
+ Meter meter = meterService.getMeter(deviceId, meterId);
+ if (meter != null) {
+ MeterRequest meterRequest = DefaultMeterRequest.builder()
+ .withBands(meter.bands())
+ .withUnit(meter.unit())
+ .forDevice(deviceId)
+ .fromApp(appId)
+ .burst()
+ .remove();
+
+ meterService.withdraw(meterRequest, meterId);
}
- private void deleteMeter(DeviceId deviceId, MeterId meterId) {
- Meter meter = meterService.getMeter(deviceId, meterId);
- if (meter != null) {
- MeterRequest meterRequest = DefaultMeterRequest.builder()
- .withBands(meter.bands())
- .withUnit(meter.unit())
- .forDevice(deviceId)
- .fromApp(appId)
- .burst()
- .remove();
-
- meterService.withdraw(meterRequest, meterId);
- }
+ // remove the meter from local caching
+ try {
+ programmedMeterWriteLock.lock();
+ programmedMeters.computeIfPresent(deviceId, (d, deviceMeters) -> {
+ Iterator<Map.Entry<String, MeterData>> iter = deviceMeters.entrySet().iterator();
+ while (iter.hasNext()) {
+ Map.Entry<String, MeterData> entry = iter.next();
+ if (entry.getValue().getMeterId().equals(meterId)) {
+ deviceMeters.remove(entry.getKey());
+ }
+ }
+ return deviceMeters;
+ });
+ } finally {
+ programmedMeterWriteLock.unlock();
}
}
+
+ protected void bindSadisService(SadisService service) {
+ this.bpService = service.getBandwidthProfileService();
+ log.info("Sadis service is loaded");
+ }
+
+ protected void unbindSadisService(SadisService service) {
+ this.bpService = null;
+ log.info("Sadis service is unloaded");
+ }
}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltMeterServiceInterface.java b/impl/src/main/java/org/opencord/olt/impl/OltMeterServiceInterface.java
new file mode 100644
index 0000000..a10455b
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltMeterServiceInterface.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.MeterId;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+
+import java.util.Map;
+
+/**
+ * Interface for meter installation/removal methods
+ * for different types of bandwidth profiles.
+ */
+public interface OltMeterServiceInterface {
+ /**
+ * Checks for a meter, if not present it will create it and return false.
+ * @param deviceId DeviceId
+ * @param bandwidthProfile Bandwidth Profile Id
+ * @return boolean
+ */
+ boolean createMeter(DeviceId deviceId, String bandwidthProfile);
+
+ /**
+ * Checks for all the meters specified in the sadis uniTagList,
+ * if not present it will create them and return false.
+ * @param deviceId DeviceId
+ * @param si SubscriberAndDeviceInformation
+ * @return boolean
+ */
+ boolean createMeters(DeviceId deviceId, SubscriberAndDeviceInformation si);
+
+ /**
+ * Checks if a meter for the specified bandwidthProfile exists
+ * and is in ADDED state.
+ * @param deviceId DeviceId
+ * @param bandwidthProfileId bandwidth profile id
+ * @return true if present and in ADDED state
+ */
+ boolean hasMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfileId);
+
+ /**
+ * Checks if a meter for the specified bandwidthProfile exists
+ * and is in PENDING_ADD state.
+ * @param deviceId DeviceId
+ * @param bandwidthProfileId bandwidth profile id
+ * @return true if present and in PENDING_ADD state
+ */
+ boolean hasPendingMeterByBandwidthProfile(DeviceId deviceId, String bandwidthProfileId);
+
+ /**
+ * Creates a meter on a device for the given BandwidthProfile Id.
+ * @param deviceId the device id
+ * @param bandwidthProfileId the bandwidth profile Id
+ */
+ void createMeterForBp(DeviceId deviceId, String bandwidthProfileId);
+
+ /**
+ * Returns the meter Id for a given bandwidth profile Id.
+ * @param deviceId the device id
+ * @param bandwidthProfileId the bandwidth profile Id
+ * @return the meter Id
+ */
+ MeterId getMeterIdForBandwidthProfile(DeviceId deviceId, String bandwidthProfileId);
+
+ /**
+ * Purges all the meters on a device.
+ * @param deviceId the device
+ */
+ void purgeDeviceMeters(DeviceId deviceId);
+
+ /**
+ * Return all programmed meters for all OLTs controlled by this ONOS cluster.
+ * @return a map, with the device keys, and entry of map with bp Id and corresponding meter
+ */
+ Map<DeviceId, Map<String, MeterData>> getProgrammedMeters();
+
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltPortStatus.java b/impl/src/main/java/org/opencord/olt/impl/OltPortStatus.java
new file mode 100644
index 0000000..655a0ac
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltPortStatus.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import java.util.Objects;
+
+/**
+ * OltPortStatus is used to keep track of the flow status for a subscriber service.
+ */
+public class OltPortStatus {
+ // TODO consider adding a lastUpdated field, it may help with debugging
+ public OltFlowService.OltFlowsStatus defaultEapolStatus;
+ public OltFlowService.OltFlowsStatus subscriberFlowsStatus;
+ // NOTE we need to keep track of the DHCP status as that is installed before the other flows
+ // if macLearning is enabled (DHCP is needed to learn the MacAddress from the host)
+ public OltFlowService.OltFlowsStatus dhcpStatus;
+
+ public OltPortStatus(OltFlowService.OltFlowsStatus defaultEapolStatus,
+ OltFlowService.OltFlowsStatus subscriberFlowsStatus,
+ OltFlowService.OltFlowsStatus dhcpStatus) {
+ this.defaultEapolStatus = defaultEapolStatus;
+ this.subscriberFlowsStatus = subscriberFlowsStatus;
+ this.dhcpStatus = dhcpStatus;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ OltPortStatus that = (OltPortStatus) o;
+ return defaultEapolStatus == that.defaultEapolStatus
+ && subscriberFlowsStatus == that.subscriberFlowsStatus
+ && dhcpStatus == that.dhcpStatus;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(defaultEapolStatus, subscriberFlowsStatus, dhcpStatus);
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("OltPortStatus{");
+ sb.append("defaultEapolStatus=").append(defaultEapolStatus);
+ sb.append(", subscriberFlowsStatus=").append(subscriberFlowsStatus);
+ sb.append(", dhcpStatus=").append(dhcpStatus);
+ sb.append('}');
+ return sb.toString();
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OltUtils.java b/impl/src/main/java/org/opencord/olt/impl/OltUtils.java
new file mode 100644
index 0000000..6300ff9
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/OltUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.Port;
+
+import static org.opencord.olt.impl.OltFlowService.FlowOperation.ADD;
+
+/**
+ * Utility class for OLT app.
+ */
+final class OltUtils {
+
+ private OltUtils() {
+ }
+
+ /**
+ * Returns the port name if present in the annotations.
+ * @param port the port
+ * @return the annotated port name
+ */
+ static String getPortName(Port port) {
+ String name = port.annotations().value(AnnotationKeys.PORT_NAME);
+ return name == null ? "" : name;
+ }
+
+ /**
+ * Returns a port printed as a connect point and with the name appended.
+ * @param port the port
+ * @return the formatted string
+ */
+ static String portWithName(Port port) {
+ return port.element().id().toString() + '/' +
+ port.number() + '[' +
+ getPortName(port) + ']';
+ }
+
+ static String flowOpToString(OltFlowService.FlowOperation op) {
+ return op == ADD ? "Adding" : "Removing";
+ }
+
+ static String completeFlowOpToString(OltFlowService.FlowOperation op) {
+ return op == ADD ? "Added" : "Removed";
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java b/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
index 3657436..a73a42b 100644
--- a/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
+++ b/impl/src/main/java/org/opencord/olt/impl/OsgiPropertyConstants.java
@@ -58,15 +58,21 @@
public static final String ENABLE_PPPOE = "enablePppoe";
public static final boolean ENABLE_PPPOE_DEFAULT = false;
- public static final String EAPOL_DELETE_RETRY_MAX_ATTEMPS = "eapolDeleteRetryMaxAttempts";
- public static final int EAPOL_DELETE_RETRY_MAX_ATTEMPS_DEFAULT = 3;
-
- public static final String PROVISION_DELAY = "provisionDelay";
- public static final int PROVISION_DELAY_DEFAULT = 100;
+ public static final String WAIT_FOR_REMOVAL = "waitForRemoval";
+ public static final boolean WAIT_FOR_REMOVAL_DEFAULT = true;
public static final String REQUIRED_DRIVERS_PROPERTY_DELAY = "requiredDriversPropertyDelay";
public static final int REQUIRED_DRIVERS_PROPERTY_DELAY_DEFAULT = 5;
+ public static final String FLOW_PROCESSING_THREADS = "flowProcessingThreads";
+ public static final int FLOW_PROCESSING_THREADS_DEFAULT = 8;
+
+ public static final String SUBSCRIBER_PROCESSING_THREADS = "subscriberProcessingThreads";
+ public static final int SUBSCRIBER_PROCESSING_THREADS_DEFAULT = 8;
+
+ public static final String REQUEUE_DELAY = "requeueDelay";
+ public static final int REQUEUE_DELAY_DEFAULT = 500;
+
public static final String UPSTREAM_ONU = "upstreamOnu";
public static final String UPSTREAM_OLT = "upstreamOlt";
public static final String DOWNSTREAM_ONU = "downstreamOnu";
diff --git a/impl/src/main/java/org/opencord/olt/impl/ServiceKey.java b/impl/src/main/java/org/opencord/olt/impl/ServiceKey.java
new file mode 100644
index 0000000..c878062
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/ServiceKey.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.Objects;
+
+/**
+ * SubscriberKey is used to identify the combination of a subscriber and a service.
+ */
+public class ServiceKey {
+ private AccessDevicePort port;
+ private UniTagInformation service;
+
+ public ServiceKey(AccessDevicePort port, UniTagInformation service) {
+ this.port = port;
+ this.service = service;
+ }
+
+ public AccessDevicePort getPort() {
+ return port;
+ }
+
+ public void setPort(AccessDevicePort port) {
+ this.port = port;
+ }
+
+ public UniTagInformation getService() {
+ return service;
+ }
+
+ public void setService(UniTagInformation service) {
+ this.service = service;
+ }
+
+ @Override
+ public String toString() {
+ return this.port.toString() + " - " + this.service.getServiceName();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ServiceKey that = (ServiceKey) o;
+ boolean isPortEqual = Objects.equals(port, that.port);
+ boolean isServiceEqual = Objects.equals(service, that.service);
+
+ return isPortEqual && isServiceEqual;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(port, service);
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/ServiceKeySerializer.java b/impl/src/main/java/org/opencord/olt/impl/ServiceKeySerializer.java
new file mode 100644
index 0000000..4035bdd
--- /dev/null
+++ b/impl/src/main/java/org/opencord/olt/impl/ServiceKeySerializer.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+import com.esotericsoftware.kryo.Serializer;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.opencord.sadis.UniTagInformation;
+
+/**
+ * ServiceKeySerializer is a custom serializer to store a ServiceKey in an Atomix ditributed map.
+ */
+class ServiceKeySerializer extends Serializer<ServiceKey> {
+
+ ServiceKeySerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, ServiceKey object) {
+ kryo.writeClassAndObject(output, object.getPort().connectPoint().port());
+ output.writeString(object.getPort().name());
+ output.writeString(object.getPort().connectPoint().deviceId().toString());
+ kryo.writeClassAndObject(output, object.getService());
+ }
+
+ @Override
+ public ServiceKey read(Kryo kryo, Input input, Class<ServiceKey> type) {
+ PortNumber port = (PortNumber) kryo.readClassAndObject(input);
+ final String portName = input.readString();
+ final String devId = input.readString();
+
+ UniTagInformation uti = (UniTagInformation) kryo.readClassAndObject(input);
+ ConnectPoint cp = new ConnectPoint(DeviceId.deviceId(devId), port);
+ AccessDevicePort adp = new AccessDevicePort(cp, portName);
+
+ return new ServiceKey(adp, uti);
+ }
+}
diff --git a/impl/src/main/java/org/opencord/olt/impl/SubscriberFlowInfo.java b/impl/src/main/java/org/opencord/olt/impl/SubscriberFlowInfo.java
deleted file mode 100644
index 225e48f..0000000
--- a/impl/src/main/java/org/opencord/olt/impl/SubscriberFlowInfo.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.meter.MeterId;
-import org.opencord.olt.AccessDevicePort;
-import org.opencord.sadis.UniTagInformation;
-
-import java.util.Objects;
-
-/**
- * Contains the mapping of a given port to flow information, including bandwidth profile.
- */
-class SubscriberFlowInfo {
- private final DeviceId devId;
- private final AccessDevicePort nniPort;
- private final AccessDevicePort uniPort;
- private final UniTagInformation tagInfo;
- private MeterId downId;
- private MeterId upId;
- private MeterId downOltId;
- private MeterId upOltId;
- private final String downBpInfo;
- private final String upBpInfo;
- private final String downOltBpInfo;
- private final String upOltBpInfo;
-
- /**
- * Builds the mapper of information.
- * @param nniPort the nni port
- * @param uniPort the uni port
- * @param tagInfo the tag info
- * @param downId the downstream meter id
- * @param upId the upstream meter id
- * @param downOltId the downstream meter id of OLT device
- * @param upOltId the upstream meter id of OLT device
- * @param downBpInfo the downstream bandwidth profile
- * @param upBpInfo the upstream bandwidth profile
- * @param downOltBpInfo the downstream bandwidth profile of OLT device
- * @param upOltBpInfo the upstream bandwidth profile of OLT device
- */
- SubscriberFlowInfo(AccessDevicePort nniPort, AccessDevicePort uniPort,
- UniTagInformation tagInfo, MeterId downId, MeterId upId,
- MeterId downOltId, MeterId upOltId,
- String downBpInfo, String upBpInfo,
- String downOltBpInfo, String upOltBpInfo) {
- this.devId = uniPort.deviceId();
- this.nniPort = nniPort;
- this.uniPort = uniPort;
- this.tagInfo = tagInfo;
- this.downId = downId;
- this.upId = upId;
- this.downOltId = downOltId;
- this.upOltId = upOltId;
- this.downBpInfo = downBpInfo;
- this.upBpInfo = upBpInfo;
- this.downOltBpInfo = downOltBpInfo;
- this.upOltBpInfo = upOltBpInfo;
- }
-
- /**
- * Gets the device id of this subscriber and flow information.
- *
- * @return device id
- */
- DeviceId getDevId() {
- return devId;
- }
-
- /**
- * Gets the nni of this subscriber and flow information.
- *
- * @return nni port
- */
- AccessDevicePort getNniPort() {
- return nniPort;
- }
-
- /**
- * Gets the uni port of this subscriber and flow information.
- *
- * @return uni port
- */
- AccessDevicePort getUniPort() {
- return uniPort;
- }
-
- /**
- * Gets the tag of this subscriber and flow information.
- *
- * @return tag of the subscriber
- */
- UniTagInformation getTagInfo() {
- return tagInfo;
- }
-
- /**
- * Gets the downstream meter id of this subscriber and flow information.
- *
- * @return downstream meter id
- */
- MeterId getDownId() {
- return downId;
- }
-
- /**
- * Gets the upstream meter id of this subscriber and flow information.
- *
- * @return upstream meter id
- */
- MeterId getUpId() {
- return upId;
- }
-
- /**
- * Gets the downstream meter id of this subscriber and flow information of OLT device.
- *
- * @return downstream olt meter id
- */
- MeterId getDownOltId() {
- return downOltId;
- }
-
- /**
- * Gets the upstream meter id of this subscriber and flow information of OLT device.
- *
- * @return upstream olt meter id
- */
- MeterId getUpOltId() {
- return upOltId;
- }
-
- /**
- * Gets the downstream bandwidth profile of this subscriber and flow information.
- *
- * @return downstream bandwidth profile
- */
- String getDownBpInfo() {
- return downBpInfo;
- }
-
- /**
- * Gets the upstream bandwidth profile of this subscriber and flow information.
- *
- * @return upstream bandwidth profile.
- */
- String getUpBpInfo() {
- return upBpInfo;
- }
-
- /**
- * Gets the downstream bandwidth profile of this subscriber and flow information of OLT device.
- *
- * @return downstream OLT bandwidth profile
- */
- String getDownOltBpInfo() {
- return downOltBpInfo;
- }
-
- /**
- * Gets the upstream bandwidth profile of this subscriber and flow information of OLT device.
- *
- * @return upstream OLT bandwidth profile.
- */
- String getUpOltBpInfo() {
- return upOltBpInfo;
- }
-
- /**
- * Sets the upstream meter id.
- *
- * @param upMeterId the upstream meter id
- */
- void setUpMeterId(MeterId upMeterId) {
- this.upId = upMeterId;
- }
-
- /**
- * Sets the downstream meter id.
- *
- * @param downMeterId the downstream meter id
- */
- void setDownMeterId(MeterId downMeterId) {
- this.downId = downMeterId;
- }
-
- /**
- * Sets the upstream meter id of OLT.
- *
- * @param upOltMeterId the upstream meter id of OLT
- */
- void setUpOltMeterId(MeterId upOltMeterId) {
- this.upOltId = upOltMeterId;
- }
-
- /**
- * Sets the downstream meter id of OLT.
- *
- * @param downOltMeterId the downstream meter id of OLT
- */
- void setDownOltMeterId(MeterId downOltMeterId) {
- this.downOltId = downOltMeterId;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- SubscriberFlowInfo flowInfo = (SubscriberFlowInfo) o;
- return devId.equals(flowInfo.devId) &&
- nniPort.equals(flowInfo.nniPort) &&
- uniPort.equals(flowInfo.uniPort) &&
- tagInfo.equals(flowInfo.tagInfo) &&
- downBpInfo.equals(flowInfo.downBpInfo) &&
- upBpInfo.equals(flowInfo.upBpInfo) &&
- Objects.equals(downOltBpInfo, flowInfo.downOltBpInfo) &&
- Objects.equals(upOltBpInfo, flowInfo.upOltBpInfo);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(devId, nniPort, uniPort, tagInfo, downBpInfo, upBpInfo, downOltBpInfo, upOltBpInfo);
- }
-
- @Override
- public String toString() {
- return com.google.common.base.MoreObjects.toStringHelper(this)
- .add("devId", devId)
- .add("nniPort", nniPort)
- .add("uniPort", uniPort)
- .add("tagInfo", tagInfo)
- .add("downId", downId)
- .add("upId", upId)
- .add("downOltId", downOltId)
- .add("upOltId", upOltId)
- .add("downBpInfo", downBpInfo)
- .add("upBpInfo", upBpInfo)
- .add("downOltBpInfo", downOltBpInfo)
- .add("upOltBpInfo", upOltBpInfo)
- .toString();
- }
-}
diff --git a/impl/src/main/java/org/opencord/olt/impl/package-info.java b/impl/src/main/java/org/opencord/olt/impl/package-info.java
index b5ba320..ccdfda7 100644
--- a/impl/src/main/java/org/opencord/olt/impl/package-info.java
+++ b/impl/src/main/java/org/opencord/olt/impl/package-info.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,6 +15,6 @@
*/
/**
- * OLT application handling PMC OLT hardware.
+ * ONOS application archetype.
*/
-package org.opencord.olt.impl;
+package org.opencord.olt.impl;
\ No newline at end of file
diff --git a/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceFlowService.java b/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceFlowService.java
deleted file mode 100644
index 9e46782..0000000
--- a/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceFlowService.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.internalapi;
-
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.meter.MeterId;
-import org.opencord.olt.AccessDevicePort;
-import org.opencord.sadis.UniTagInformation;
-
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Olt service for flow operations.
- */
-public interface AccessDeviceFlowService {
-
- /**
- * Provisions or removes trap-to-controller DHCP packets.
- *
- * @param port the uni port for which this trap flow is designated
- * @param upstreamMeterId the upstream meter id that includes the upstream
- * bandwidth profile values such as PIR,CIR. If no meter id needs to be referenced,
- * null can be sent
- * @param upstreamOltMeterId the upstream meter id of OLT device that includes the upstream
- * bandwidth profile values such as PIR,CIR. If no meter id needs to be referenced,
- * null can be sent
- * @param tagInformation the uni tag (ctag, stag) information
- * @param install true to install the flow, false to remove the flow
- * @param upstream true if trapped packets are flowing upstream towards
- * server, false if packets are flowing downstream towards client
- * @param dhcpFuture gets result of dhcp objective when complete
- */
- void processDhcpFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream,
- Optional<CompletableFuture<ObjectiveError>> dhcpFuture);
-
- /**
- * Trap igmp packets to the controller.
- *
- * @param port Uni Port number
- * @param upstreamMeterId upstream meter id that represents the upstream bandwidth profile
- * @param upstreamOltMeterId upstream meter id of OLT device that represents the upstream bandwidth profile
- * @param tagInformation the uni tag information of the subscriber
- * @param install the indicator to install or to remove the flow
- * @param upstream determines the direction of the flow
- */
- void processIgmpFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream);
-
- /**
- * Trap eapol authentication packets to the controller.
- *
- * @param port the port for which this trap flow is designated
- * @param bpId bandwidth profile id to add the related meter to the flow
- * @param oltBpId bandwidth profile id of OLT device to add the related meter to the flow
- * @param filterFuture completable future for this filtering objective operation
- * @param vlanId the default or customer tag for a subscriber
- * @param install true to install the flow, false to remove the flow
- */
- void processEapolFilteringObjectives(AccessDevicePort port,
- String bpId,
- Optional<String> oltBpId,
- CompletableFuture<ObjectiveError> filterFuture,
- VlanId vlanId,
- boolean install);
-
- /**
- * Trap PPPoE discovery packets to the controller.
- *
- * @param port the uni port for which this trap flow is designated
- * @param upstreamMeterId the upstream meter id that includes the upstream
- * bandwidth profile values such as PIR,CIR. If no meter id needs to be referenced,
- * null can be sent
- * @param upstreamOltMeterId the upstream meter id of OLT device that includes the upstream
- * bandwidth profile values such as PIR,CIR. If no meter id needs to be referenced,
- * null can be sent
- * @param tagInformation the uni tag (ctag, stag) information
- * @param install true to install the flow, false to remove the flow
- * @param upstream true if trapped packets are flowing upstream towards
- * server, false if packets are flowing downstream towards client
- **/
- void processPPPoEDFilteringObjectives(AccessDevicePort port,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation tagInformation,
- boolean install,
- boolean upstream);
-
- /**
- * Trap lldp packets to the controller.
- *
- * @param port the port for which this trap flow is designated
- * @param install true to install the flow, false to remove the flow
- */
- void processLldpFilteringObjective(AccessDevicePort port, boolean install);
-
- /**
- * Installs trap filtering objectives for particular traffic types (LLDP, IGMP and DHCP) on an
- * NNI port.
- *
- * @param port port number
- * @param install true to install, false to remove
- */
- void processNniFilteringObjectives(AccessDevicePort port, boolean install);
-
- /**
- * Creates a ForwardingObjective builder with double-tag match criteria and output
- * action. The treatment will not contain pop or push actions.
- * If the last parameter is true, use the upstream meter id and vice versa.
- *
- * @param uplinkPort the nni port
- * @param subscriberPort the uni port
- * @param meterId the meter id that is assigned to upstream or downstream flows
- * @param tagInfo the uni tag information
- * @param upstream true to create upstream, false to create downstream builder
- * @return ForwardingObjective.Builder
- */
- ForwardingObjective.Builder createTransparentBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId meterId,
- UniTagInformation tagInfo,
- boolean upstream);
-
- /**
- * Creates a ForwardingObjective builder for the upstream flows.
- * The treatment will contain push action
- *
- * @param uplinkPort the nni port
- * @param subscriberPort the uni port
- * @param upstreamMeterId the meter id that is assigned to upstream flows
- * @param upstreamOltMeterId the meter id that is assigned to upstream flows for OLT device
- * @param uniTagInformation the uni tag information
- * @return ForwardingObjective.Builder
- */
- ForwardingObjective.Builder createUpBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId upstreamMeterId,
- MeterId upstreamOltMeterId,
- UniTagInformation uniTagInformation);
-
- /**
- * Creates a ForwardingObjective builder for the downstream flows.
- * The treatment will contain pop action
- *
- * @param uplinkPort the nni port
- * @param subscriberPort the uni port
- * @param downstreamMeterId the meter id that is assigned to downstream flows
- * @param downstreamOltMeterId the meter id that is assigned to downstream flows
- * @param tagInformation the uni tag information
- * @param macAddress the mac address
- * @return ForwardingObjective.Builder
- */
- ForwardingObjective.Builder createDownBuilder(AccessDevicePort uplinkPort,
- AccessDevicePort subscriberPort,
- MeterId downstreamMeterId,
- MeterId downstreamOltMeterId,
- UniTagInformation tagInformation,
- Optional<MacAddress> macAddress);
-
- /**
- * Clears pending mappings and state for device.
- * @param deviceId the device id
- */
- void clearDeviceState(DeviceId deviceId);
-}
diff --git a/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceMeterService.java b/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceMeterService.java
deleted file mode 100644
index ca04ed4..0000000
--- a/impl/src/main/java/org/opencord/olt/internalapi/AccessDeviceMeterService.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.internalapi;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.meter.MeterId;
-import org.onosproject.net.meter.MeterKey;
-import org.opencord.sadis.BandwidthProfileInformation;
-
-import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Olt service for meter operations.
- */
-public interface AccessDeviceMeterService {
-
- /**
- * Returns information about bandwidthProfile-meterKey (device / meter) mappings
- * that have been programmed in the data-plane.
- *
- * @return an immutable map of bandwidthProfile-meterKey (device / meter) mappings
- */
- ImmutableMap<String, Collection<MeterKey>> getBpMeterMappings();
-
- /**
- * Returns the meter id for a given bandwidth profile.
- *
- * @param deviceId the access device id
- * @param bandwidthProfile the bandwidth profile id
- * @return the meter id
- */
- MeterId getMeterIdFromBpMapping(DeviceId deviceId, String bandwidthProfile);
-
- /**
- * Returns information about device-meter relations that have been programmed in the
- * data-plane.
- *
- * @return an immutable set of device-meter mappings
- */
- ImmutableSet<MeterKey> getProgMeters();
-
- /**
- * Creates a meter and sends it to the device.
- *
- * @param deviceId the access device id
- * @param bpInfo the bandwidth profile information
- * @param meterFuture the meter future to indicate whether the meter creation is
- * successful or not.
- * @return meter id that is generated for the given parameters
- */
- MeterId createMeter(DeviceId deviceId, BandwidthProfileInformation bpInfo,
- CompletableFuture<Object> meterFuture);
-
- /**
- * Removes the DeviceBandwidthProfile from the pendingMeters.
- *
- * @param deviceId the device
- * @param bwpInfo the bandwidth profile info
- *
- */
- void removeFromPendingMeters(DeviceId deviceId, BandwidthProfileInformation bwpInfo);
-
- /**
- * Checks if DeviceBandwidthProfile is pending installation.
- * If so immediately returns false meaning that no further action is needed,
- * if not it adds the bandwidth profile do the pending list and returns true,
- * meaning that further action to install the meter is required.
- *
- * @param deviceId the device
- * @param bwpInfo the bandwidth profile info
- *
- * @return true if it was added to pending and a create meter action is needed,
- * false if it is already pending and no further action is needed.
- */
- boolean checkAndAddPendingMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo);
-
- /**
- * Clears out meters for the given device.
- *
- * @param deviceId device ID
- */
- void clearMeters(DeviceId deviceId);
-
- /**
- * Clears out local state for the given device.
- *
- * @param deviceId device ID
- */
- void clearDeviceState(DeviceId deviceId);
-}
diff --git a/impl/src/main/java/org/opencord/olt/internalapi/package-info.java b/impl/src/main/java/org/opencord/olt/internalapi/package-info.java
deleted file mode 100644
index 2b5d98d..0000000
--- a/impl/src/main/java/org/opencord/olt/internalapi/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Internal APIs for the OLT application.
- */
-package org.opencord.olt.internalapi;
diff --git a/impl/src/main/resources/olt-drivers.xml b/impl/src/main/resources/olt-drivers.xml
index fa7b585..761d962 100644
--- a/impl/src/main/resources/olt-drivers.xml
+++ b/impl/src/main/resources/olt-drivers.xml
@@ -15,20 +15,6 @@
~ limitations under the License.
-->
<drivers>
- <driver name="celestica" extends="default"
- manufacturer="PMC GPON Networks" hwVersion="PAS5211 v2" swVersion="vOLT version 1.5.3.*">
- <behaviour api="org.onosproject.net.behaviour.Pipeliner"
- impl="org.opencord.olt.driver.OltPipeline"/>
- <behaviour api="org.onosproject.net.behaviour.MeterQuery"
- impl="org.onosproject.driver.query.FullMetersAvailable"/>
- </driver>
- <driver name="pmc-olt" extends="default"
- manufacturer="PMC GPON Networks" hwVersion="PASffffffff v-1" swVersion="vOLT.*">
- <behaviour api="org.onosproject.net.behaviour.Pipeliner"
- impl="org.opencord.olt.driver.OltPipeline"/>
- <behaviour api="org.onosproject.net.behaviour.MeterQuery"
- impl="org.onosproject.driver.query.FullMetersAvailable"/>
- </driver>
<driver name="voltha" extends="default"
manufacturer="VOLTHA Project" hwVersion=".*" swVersion=".*">
<behaviour api="org.onosproject.net.behaviour.Pipeliner"
@@ -37,26 +23,5 @@
impl="org.onosproject.driver.query.FullMetersAvailable"/>
<property name="accumulatorEnabled">true</property>
</driver>
- <driver name="fj-olt" extends="default"
- manufacturer="Fujitsu" hwVersion="svkOLT" swVersion="v1.0">
- <behaviour api="org.onosproject.net.behaviour.Pipeliner"
- impl="org.opencord.olt.driver.OltPipeline"/>
- <behaviour api="org.onosproject.net.behaviour.MeterQuery"
- impl="org.onosproject.driver.query.FullMetersAvailable"/>
- </driver>
- <driver name="nokia-olt" extends="default"
- manufacturer="Nokia" hwVersion="SDOLT" swVersion="5.2.1">
- <behaviour api="org.onosproject.net.behaviour.Pipeliner"
- impl="org.opencord.olt.driver.NokiaOltPipeline"/>
- <behaviour api="org.onosproject.net.behaviour.MeterQuery"
- impl="org.onosproject.driver.query.FullMetersAvailable"/>
- </driver>
- <driver name="g.fast" extends="default"
- manufacturer="TEST1" hwVersion="TEST2" swVersion="TEST3">
- <behaviour api="org.onosproject.net.behaviour.Pipeliner"
- impl="org.opencord.olt.driver.OltPipeline"/>
- <behaviour api="org.onosproject.net.behaviour.MeterQuery"
- impl="org.onosproject.driver.query.FullMetersAvailable"/>
- </driver>
</drivers>
diff --git a/impl/src/test/java/org/opencord/olt/impl/ConsistentHasherTest.java b/impl/src/test/java/org/opencord/olt/impl/ConsistentHasherTest.java
deleted file mode 100644
index 1f682cb..0000000
--- a/impl/src/test/java/org/opencord/olt/impl/ConsistentHasherTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.net.DeviceId;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.equalTo;
-
-public class ConsistentHasherTest {
-
- private static final int WEIGHT = 10;
-
- private static final NodeId N1 = new NodeId("10.0.0.1");
- private static final NodeId N2 = new NodeId("10.0.0.2");
- private static final NodeId N3 = new NodeId("10.0.0.3");
-
- private ConsistentHasher hasher;
-
- @Before
- public void setUp() {
- List<NodeId> servers = new ArrayList<>();
- servers.add(N1);
- servers.add(N2);
-
- hasher = new ConsistentHasher(servers, WEIGHT);
- }
-
- @Test
- public void testHasher() {
- DeviceId deviceId = DeviceId.deviceId("foo");
- NodeId server = hasher.hash(deviceId.toString());
-
- assertThat(server, equalTo(N1));
-
- deviceId = DeviceId.deviceId("bsaf");
- server = hasher.hash(deviceId.toString());
-
- assertThat(server, equalTo(N2));
- }
-
- @Test
- public void testAddServer() {
- DeviceId deviceId = DeviceId.deviceId("foo");
- NodeId server = hasher.hash(deviceId.toString());
-
- assertThat(server, equalTo(N1));
-
- hasher.addServer(N3);
-
- server = hasher.hash(deviceId.toString());
-
- assertThat(server, equalTo(N3));
- }
-}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltDeviceListenerTest.java b/impl/src/test/java/org/opencord/olt/impl/OltDeviceListenerTest.java
new file mode 100644
index 0000000..f8229da
--- /dev/null
+++ b/impl/src/test/java/org/opencord/olt/impl/OltDeviceListenerTest.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.onlab.packet.ChassisId;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.AnnotationKeys;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultDevice;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.provider.ProviderId;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+public class OltDeviceListenerTest extends OltTestHelpers {
+ private Olt olt;
+ private Olt.OltDeviceListener oltDeviceListener;
+
+ private final DeviceId deviceId = DeviceId.deviceId("test-device");
+ private final Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
+ "testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
+
+ @Before
+ public void setUp() {
+ olt = new Olt();
+ olt.eventsQueues = new HashMap<>();
+ olt.mastershipService = Mockito.mock(MastershipService.class);
+ olt.oltDeviceService = Mockito.mock(OltDeviceService.class);
+ olt.oltFlowService = Mockito.mock(OltFlowService.class);
+ olt.oltMeterService = Mockito.mock(OltMeterService.class);
+ olt.deviceService = Mockito.mock(DeviceService.class);
+ olt.leadershipService = Mockito.mock(LeadershipService.class);
+ olt.clusterService = Mockito.mock(ClusterService.class);
+ olt.subsService = Mockito.mock(BaseInformationService.class);
+
+ Olt.OltDeviceListener baseClass = olt.deviceListener;
+ baseClass.eventExecutor = Mockito.mock(ExecutorService.class);
+ oltDeviceListener = Mockito.spy(baseClass);
+
+ // mock the executor so it immediately invokes the method
+ doAnswer(new Answer<Object>() {
+ public Object answer(InvocationOnMock invocation) throws Exception {
+ ((Runnable) invocation.getArguments()[0]).run();
+ return null;
+ }
+ }).when(baseClass.eventExecutor).execute(any(Runnable.class));
+
+ olt.eventsQueues.forEach((cp, q) -> q.clear());
+ }
+
+ @Test
+ public void testDeviceDisconnection() {
+ doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
+ doReturn(false).when(olt.deviceService).isAvailable(any());
+ doReturn(new LinkedList<Port>()).when(olt.deviceService).getPorts(any());
+ doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
+
+ DeviceEvent disconnect = new DeviceEvent(DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED, testDevice, null);
+ oltDeviceListener.event(disconnect);
+
+ verify(olt.oltFlowService, times(1)).purgeDeviceFlows(testDevice.id());
+ verify(olt.oltMeterService, times(1)).purgeDeviceMeters(testDevice.id());
+ }
+
+ @Test
+ public void testPortEventOwnership() {
+ // make sure that we ignore events for devices that are not local to this node
+
+ // make sure the device is recognized as an OLT and the port is not an NNI
+ doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
+ doReturn(false).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
+
+ // make sure we're not leaders of the device
+ doReturn(false).when(olt.oltDeviceService).isLocalLeader(any());
+
+ // this is a new port, should not create an entry in the queue
+ // we're not owners of the device
+ Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
+ DeviceEvent uniUpdateEnabledEvent =
+ new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
+ oltDeviceListener.event(uniUpdateEnabledEvent);
+
+ // the queue won't even be created
+ assert olt.eventsQueues.isEmpty();
+ }
+
+ @Test
+ public void testNniEvent() throws InterruptedException {
+ // make sure the device is recognized as an OLT and the port is recognized as an NNI,
+ // and we're local leaders
+ doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
+ doReturn(true).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
+ doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
+
+ Port enabledNniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1").build());
+ DeviceEvent nniEnabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_ADDED, testDevice, enabledNniPort);
+ oltDeviceListener.event(nniEnabledEvent);
+
+ // NNI events are straight forward, we can provision the flows directly
+ assert olt.eventsQueues.isEmpty();
+ verify(olt.oltFlowService, times(1))
+ .handleNniFlows(testDevice, enabledNniPort, OltFlowService.FlowOperation.ADD);
+
+ Port disabledNniPort = new OltPort(testDevice, false, PortNumber.portNumber(1048576),
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1").build());
+ DeviceEvent nniDisabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, disabledNniPort);
+ oltDeviceListener.event(nniDisabledEvent);
+
+ assert olt.eventsQueues.isEmpty();
+ verify(olt.oltFlowService, times(1))
+ .handleNniFlows(testDevice, disabledNniPort, OltFlowService.FlowOperation.REMOVE);
+
+ // when we disable the device we receive a PORT_REMOVED event with status ENABLED
+ // make sure we're removing the flows correctly
+ DeviceEvent nniRemoveEvent = new DeviceEvent(DeviceEvent.Type.PORT_REMOVED, testDevice, enabledNniPort);
+ oltDeviceListener.event(nniRemoveEvent);
+
+ assert olt.eventsQueues.isEmpty();
+ verify(olt.oltFlowService, times(1))
+ .handleNniFlows(testDevice, enabledNniPort, OltFlowService.FlowOperation.REMOVE);
+ }
+
+ @Test
+ public void testUniEvents() {
+ DiscoveredSubscriber sub;
+ // there are few cases we need to test in the UNI port case:
+ // - [X] UNI port added in disabled state
+ // - [X] UNI port added in disabled state (with default EAPOL installed)
+ // - UNI port added in enabled state
+ // - [X] UNI port updated to enabled state
+ // - UNI port updated to disabled state
+ // - UNI port removed (assumes it's disabled state)
+
+ // make sure the device is recognized as an OLT, the port is not an NNI,
+ // and we're local masters
+ doReturn(true).when(olt.oltDeviceService).isOlt(testDevice);
+ doReturn(false).when(olt.oltDeviceService).isNniPort(eq(testDevice), any());
+ doReturn(true).when(olt.oltDeviceService).isLocalLeader(any());
+
+ PortNumber uniPortNumber = PortNumber.portNumber(16);
+ Port uniAddedDisabled = new OltPort(testDevice, false, uniPortNumber,
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
+ DeviceEvent uniAddedDisabledEvent = new DeviceEvent(DeviceEvent.Type.PORT_ADDED, testDevice, uniAddedDisabled);
+ ConnectPoint cp = new ConnectPoint(testDevice.id(), uniPortNumber);
+
+ // if the port does not have default EAPOL we should not generate an event
+ oltDeviceListener.event(uniAddedDisabledEvent);
+
+ // no event == no queue is created
+ assert olt.eventsQueues.isEmpty();
+
+ // if the port has default EAPOL then create an entry in the queue to remove it
+ doReturn(true).when(olt.oltFlowService)
+ .hasDefaultEapol(uniAddedDisabled);
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+ doReturn(si).when(olt.subsService).get("uni-1");
+
+ oltDeviceListener.event(uniAddedDisabledEvent);
+ LinkedBlockingQueue<DiscoveredSubscriber> q = olt.eventsQueues.get(cp);
+ assert !q.isEmpty();
+ sub = q.poll();
+ assert !sub.hasSubscriber; // this is not a provision subscriber call
+ assert sub.device.equals(testDevice);
+ assert sub.port.equals(uniAddedDisabled);
+ assert sub.status.equals(DiscoveredSubscriber.Status.REMOVED); // we need to remove flows for this port (if any)
+ assert q.isEmpty(); // the queue is now empty
+
+ Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1").build());
+ DeviceEvent uniUpdateEnabledEvent =
+ new DeviceEvent(DeviceEvent.Type.PORT_UPDATED, testDevice, uniUpdateEnabled);
+ oltDeviceListener.event(uniUpdateEnabledEvent);
+
+ assert !q.isEmpty();
+ sub = q.poll();
+ assert !sub.hasSubscriber; // this is not a provision subscriber call
+ assert sub.device.equals(testDevice);
+ assert sub.port.equals(uniUpdateEnabled);
+ assert sub.status.equals(DiscoveredSubscriber.Status.ADDED); // we need to remove flows for this port (if any)
+ assert q.isEmpty(); // the queue is now empty
+ }
+}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltDeviceServiceTest.java b/impl/src/test/java/org/opencord/olt/impl/OltDeviceServiceTest.java
new file mode 100644
index 0000000..5623896
--- /dev/null
+++ b/impl/src/test/java/org/opencord/olt/impl/OltDeviceServiceTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.ControllerNode;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.Leader;
+import org.onosproject.cluster.Leadership;
+import org.onosproject.cluster.LeadershipService;
+import org.onosproject.cluster.NodeId;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.device.DeviceService;
+import org.opencord.sadis.SadisService;
+
+import java.util.LinkedList;
+
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+
+public class OltDeviceServiceTest {
+ OltDeviceService component;
+ private OltDeviceService oltDeviceService;
+
+ @Before
+ public void setUp() {
+ component = new OltDeviceService();
+ component.mastershipService = Mockito.mock(MastershipService.class);
+ component.deviceService = Mockito.mock(DeviceService.class);
+ component.leadershipService = Mockito.mock(LeadershipService.class);
+ component.clusterService = Mockito.mock(ClusterService.class);
+ component.sadisService = Mockito.mock(SadisService.class);
+ component.activate();
+
+ oltDeviceService = Mockito.spy(component);
+
+
+ }
+
+ @Test
+ public void testIsLocalLeader() {
+
+ NodeId nodeId = NodeId.nodeId("node1");
+ ControllerNode localNode = new DefaultControllerNode(nodeId, "host1");
+ DeviceId deviceId1 = DeviceId.deviceId("availableNotLocal");
+ DeviceId deviceId2 = DeviceId.deviceId("notAvailableButLocal");
+ Leadership leadership = new Leadership(deviceId2.toString(), new Leader(nodeId, 0, 0), new LinkedList<>());
+
+ doReturn(true).when(oltDeviceService.deviceService).isAvailable(eq(deviceId1));
+ doReturn(false).when(oltDeviceService.mastershipService).isLocalMaster(eq(deviceId1));
+ Assert.assertFalse(oltDeviceService.isLocalLeader(deviceId1));
+
+ doReturn(false).when(oltDeviceService.deviceService).isAvailable(eq(deviceId1));
+ doReturn(localNode).when(oltDeviceService.clusterService).getLocalNode();
+ doReturn(leadership).when(oltDeviceService.leadershipService).runForLeadership(eq(deviceId2.toString()));
+ Assert.assertTrue(oltDeviceService.isLocalLeader(deviceId2));
+
+ }
+}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltFlowServiceTest.java b/impl/src/test/java/org/opencord/olt/impl/OltFlowServiceTest.java
new file mode 100644
index 0000000..c9bad5f
--- /dev/null
+++ b/impl/src/test/java/org/opencord/olt/impl/OltFlowServiceTest.java
@@ -0,0 +1,669 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.onlab.packet.ChassisId;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IPv4;
+import org.onlab.packet.IPv6;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.TpPort;
+import org.onlab.packet.VlanId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+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.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowRule;
+import org.onosproject.net.flow.FlowRuleEvent;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.criteria.Criteria;
+import org.onosproject.net.flowobjective.DefaultFilteringObjective;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.TestStorageService;
+import org.opencord.sadis.BaseInformationService;
+import org.opencord.sadis.SadisService;
+import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.UniTagInformation;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.NONE;
+import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.ADDED;
+import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.PENDING_ADD;
+import static org.opencord.olt.impl.OltFlowService.OltFlowsStatus.REMOVED;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
+
+public class OltFlowServiceTest extends OltTestHelpers {
+
+ private OltFlowService oltFlowService;
+ OltFlowService.InternalFlowListener internalFlowListener;
+ private final ApplicationId testAppId = new DefaultApplicationId(1, "org.opencord.olt.test");
+ private final short eapolDefaultVlan = 4091;
+
+ private final DeviceId deviceId = DeviceId.deviceId("test-device");
+ private final Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
+ "testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
+ Port nniPort = new OltPort(testDevice, true, PortNumber.portNumber(1048576),
+ DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
+ Port nniPortDisabled = new OltPort(testDevice, false, PortNumber.portNumber(1048576),
+ DefaultAnnotations.builder().set(PORT_NAME, "nni-1").build());
+ Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
+ DefaultAnnotations.builder().set(PORT_NAME, "uni-1").build());
+
+ @Before
+ public void setUp() {
+ oltFlowService = new OltFlowService();
+ oltFlowService.cfgService = new ComponentConfigAdapter();
+ oltFlowService.sadisService = Mockito.mock(SadisService.class);
+ oltFlowService.coreService = Mockito.spy(new CoreServiceAdapter());
+ oltFlowService.oltMeterService = Mockito.mock(OltMeterService.class);
+ oltFlowService.flowObjectiveService = Mockito.mock(FlowObjectiveService.class);
+ oltFlowService.hostService = Mockito.mock(HostService.class);
+ oltFlowService.flowRuleService = Mockito.mock(FlowRuleService.class);
+ oltFlowService.storageService = new TestStorageService();
+ oltFlowService.oltDeviceService = Mockito.mock(OltDeviceService.class);
+ oltFlowService.appId = testAppId;
+
+ doReturn(Mockito.mock(BaseInformationService.class))
+ .when(oltFlowService.sadisService).getSubscriberInfoService();
+ doReturn(testAppId).when(oltFlowService.coreService).registerApplication("org.opencord.olt");
+ oltFlowService.activate(null);
+ oltFlowService.bindSadisService(oltFlowService.sadisService);
+
+ internalFlowListener = spy(oltFlowService.internalFlowListener);
+ }
+
+ @After
+ public void tearDown() {
+ oltFlowService.deactivate(null);
+ }
+
+ @Test
+ public void testUpdateConnectPointStatus() {
+
+ DeviceId deviceId = DeviceId.deviceId("test-device");
+ ProviderId pid = new ProviderId("of", "foo");
+ Device device =
+ new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
+ Port port1 = new DefaultPort(device, PortNumber.portNumber(1), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "port-1").build());
+ Port port2 = new DefaultPort(device, PortNumber.portNumber(2), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "port-2").build());
+ Port port3 = new DefaultPort(device, PortNumber.portNumber(3), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "port-3").build());
+
+ ServiceKey sk1 = new ServiceKey(new AccessDevicePort(port1), new UniTagInformation());
+ ServiceKey sk2 = new ServiceKey(new AccessDevicePort(port2), new UniTagInformation());
+ ServiceKey sk3 = new ServiceKey(new AccessDevicePort(port3), new UniTagInformation());
+
+ // cpStatus map for the test
+ oltFlowService.cpStatus = oltFlowService.storageService.
+ <ServiceKey, OltPortStatus>consistentMapBuilder().build().asJavaMap();
+ OltPortStatus cp1Status = new OltPortStatus(PENDING_ADD, NONE, NONE);
+ oltFlowService.cpStatus.put(sk1, cp1Status);
+
+ //check that we only update the provided value
+ oltFlowService.updateConnectPointStatus(sk1, ADDED, null, null);
+ OltPortStatus updated = oltFlowService.cpStatus.get(sk1);
+ Assert.assertEquals(ADDED, updated.defaultEapolStatus);
+ Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
+ Assert.assertEquals(NONE, updated.dhcpStatus);
+
+ // check that it creates an entry if it does not exist
+ oltFlowService.updateConnectPointStatus(sk2, PENDING_ADD, NONE, NONE);
+ Assert.assertNotNull(oltFlowService.cpStatus.get(sk2));
+
+ // check that if we create a new entry with null values they're converted to NONE
+ oltFlowService.updateConnectPointStatus(sk3, null, null, null);
+ updated = oltFlowService.cpStatus.get(sk3);
+ Assert.assertEquals(NONE, updated.defaultEapolStatus);
+ Assert.assertEquals(NONE, updated.subscriberFlowsStatus);
+ Assert.assertEquals(NONE, updated.dhcpStatus);
+ }
+
+ @Test
+ public void testHasDefaultEapol() {
+ DeviceId deviceId = DeviceId.deviceId("test-device");
+ ProviderId pid = new ProviderId("of", "foo");
+
+ Device device =
+ new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
+
+ Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
+ ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
+ oltFlowService.defaultEapolUniTag);
+
+ Port port17 = new DefaultPort(device, PortNumber.portNumber(17), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
+
+ OltPortStatus portStatusAdded = new OltPortStatus(
+ OltFlowService.OltFlowsStatus.ADDED,
+ NONE,
+ null
+ );
+
+ OltPortStatus portStatusRemoved = new OltPortStatus(
+ REMOVED,
+ NONE,
+ null
+ );
+
+ oltFlowService.cpStatus.put(skWithStatus, portStatusAdded);
+ Assert.assertTrue(oltFlowService.hasDefaultEapol(port));
+
+ oltFlowService.cpStatus.put(skWithStatus, portStatusRemoved);
+
+ Assert.assertFalse(oltFlowService.hasDefaultEapol(port17));
+ }
+
+ @Test
+ public void testHasSubscriberFlows() {
+ // TODO test with multiple services
+ DeviceId deviceId = DeviceId.deviceId("test-device");
+ ProviderId pid = new ProviderId("of", "foo");
+
+ Device device =
+ new DefaultDevice(pid, deviceId, Device.Type.OLT, "", "", "", "", null);
+
+ Port port = new DefaultPort(device, PortNumber.portNumber(16), true,
+ DefaultAnnotations.builder().set(PORT_NAME, "name-1").build());
+
+ UniTagInformation uti = new UniTagInformation.Builder().setServiceName("test").build();
+ ServiceKey skWithStatus = new ServiceKey(new AccessDevicePort(port),
+ uti);
+
+ OltPortStatus withDefaultEapol = new OltPortStatus(
+ ADDED,
+ NONE,
+ NONE
+ );
+
+ OltPortStatus withDhcp = new OltPortStatus(
+ REMOVED,
+ NONE,
+ ADDED
+ );
+
+ OltPortStatus withSubFlow = new OltPortStatus(
+ REMOVED,
+ ADDED,
+ ADDED
+ );
+
+ oltFlowService.cpStatus.put(skWithStatus, withDefaultEapol);
+ Assert.assertFalse(oltFlowService.hasSubscriberFlows(port, uti));
+
+ oltFlowService.cpStatus.put(skWithStatus, withDhcp);
+ Assert.assertTrue(oltFlowService.hasDhcpFlows(port, uti));
+
+ oltFlowService.cpStatus.put(skWithStatus, withSubFlow);
+ Assert.assertTrue(oltFlowService.hasSubscriberFlows(port, uti));
+ }
+
+ @Test
+ public void testHandleBasicPortFlowsNoEapol() throws Exception {
+ oltFlowService.enableEapol = false;
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+
+ final DiscoveredSubscriber addedSub =
+ new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
+ false, si);
+ oltFlowService.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
+ // if eapol is not enabled there's nothing we need to do,
+ // so make sure we don't even call sadis
+ verify(oltFlowService.subsService, never()).get(any());
+ }
+
+ @Test
+ public void testHandleBasicPortFlowsWithEapolNoMeter() throws Exception {
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+ final DiscoveredSubscriber addedSub =
+ new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
+ false, si);
+ // whether the meter is pending or not is up to the createMeter method to handle
+ // we just don't proceed with the subscriber till it's ready
+ doReturn(false).when(oltFlowService.oltMeterService)
+ .createMeter(addedSub.device.id(), DEFAULT_BP_ID_DEFAULT);
+ boolean res = oltFlowService.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
+
+ Assert.assertFalse(res);
+
+ // we do not create flows
+ verify(oltFlowService.flowObjectiveService, never())
+ .filter(eq(addedSub.device.id()), any());
+ }
+
+ @Test
+ public void testHandleBasicPortFlowsWithEapolAddedMeter() throws Exception {
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+ final DiscoveredSubscriber addedSub =
+ new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
+ false, si);
+ // this is the happy case, we have the meter so we check that the default EAPOL flow
+ // is installed
+ doReturn(true).when(oltFlowService.oltMeterService)
+ .createMeter(deviceId, DEFAULT_BP_ID_DEFAULT);
+ doReturn(true).when(oltFlowService.oltMeterService)
+ .hasMeterByBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
+ doReturn(MeterId.meterId(1)).when(oltFlowService.oltMeterService)
+ .getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(
+ DefaultTrafficTreatment.builder()
+ .meter(MeterId.meterId(1))
+ .writeMetadata(oltFlowService.createTechProfValueForWriteMetadata(
+ VlanId.vlanId(eapolDefaultVlan),
+ oltFlowService.defaultTechProfileId, MeterId.meterId(1)), 0)
+ .setOutput(PortNumber.CONTROLLER)
+ .pushVlan()
+ .setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
+ )
+ .add();
+
+
+ oltFlowService.handleBasicPortFlows(addedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
+
+ // we check for an existing meter (present)
+ // FIXME understand why the above test invokes this call and this one doesn't
+// verify(oltFlowService.oltMeterService, times(1))
+// .hasMeterByBandwidthProfile(eq(addedSub.device.id()), eq(DEFAULT_BP_ID_DEFAULT));
+
+ // the meter exist, no need to check for PENDING or to create it
+ verify(oltFlowService.oltMeterService, never())
+ .hasPendingMeterByBandwidthProfile(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
+ verify(oltFlowService.oltMeterService, never())
+ .createMeterForBp(eq(deviceId), eq(DEFAULT_BP_ID_DEFAULT));
+
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ }
+
+ @Test
+ public void testHandleBasicPortFlowsRemovedSub() throws Exception {
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+ final DiscoveredSubscriber removedSub =
+ new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.REMOVED,
+ false, si);
+ // we are testing that when a port goes down we remove the default EAPOL flow
+
+ doReturn(MeterId.meterId(1)).when(oltFlowService.oltMeterService)
+ .getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .deny()
+ .withKey(Criteria.matchInPort(uniUpdateEnabled.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.EAPOL.ethType()))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(
+ DefaultTrafficTreatment.builder()
+ .meter(MeterId.meterId(1))
+ .writeMetadata(oltFlowService.createTechProfValueForWriteMetadata(
+ VlanId.vlanId(eapolDefaultVlan),
+ oltFlowService.defaultTechProfileId, MeterId.meterId(1)), 0)
+ .setOutput(PortNumber.CONTROLLER)
+ .pushVlan()
+ .setVlanId(VlanId.vlanId(eapolDefaultVlan)).build()
+ )
+ .add();
+
+ oltFlowService.handleBasicPortFlows(removedSub, DEFAULT_BP_ID_DEFAULT, DEFAULT_BP_ID_DEFAULT);
+
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ }
+
+ @Test
+ public void testHandleNniFlowsOnlyLldp() {
+ oltFlowService.enableDhcpOnNni = false;
+ oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(nniPort.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.LLDP.ethType()))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
+ .add();
+
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), any());
+ }
+
+ @Test
+ public void testHandleNniFlowsDhcpV4() {
+ oltFlowService.enableDhcpOnNni = true;
+ oltFlowService.enableDhcpV4 = true;
+ oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(nniPort.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
+ .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
+ .addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
+ .add();
+
+ // invoked with the correct DHCP filtering objective
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ // invoked only twice, LLDP and DHCP
+ verify(oltFlowService.flowObjectiveService, times(2))
+ .filter(eq(deviceId), any());
+ }
+
+ @Test
+ public void testRemoveNniFlowsDhcpV4() {
+ oltFlowService.enableDhcpOnNni = true;
+ oltFlowService.enableDhcpV4 = true;
+ oltFlowService.handleNniFlows(testDevice, nniPortDisabled, OltFlowService.FlowOperation.REMOVE);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .deny()
+ .withKey(Criteria.matchInPort(nniPort.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
+ .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(67)))
+ .addCondition(Criteria.matchUdpDst(TpPort.tpPort(68)))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
+ .add();
+
+ // invoked with the correct DHCP filtering objective
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ // invoked only twice, LLDP and DHCP
+ verify(oltFlowService.flowObjectiveService, times(2))
+ .filter(eq(deviceId), any());
+ }
+
+ @Test
+ public void testHandleNniFlowsDhcpV6() {
+ oltFlowService.enableDhcpOnNni = true;
+ oltFlowService.enableDhcpV4 = false;
+ oltFlowService.enableDhcpV6 = true;
+ oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(nniPort.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV6.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv6.PROTOCOL_UDP))
+ .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(546)))
+ .addCondition(Criteria.matchUdpDst(TpPort.tpPort(547)))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .withMeta(DefaultTrafficTreatment.builder().setOutput(PortNumber.CONTROLLER).build())
+ .add();
+
+ // invoked with the correct DHCP filtering objective
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ // invoked only twice, LLDP and DHCP
+ verify(oltFlowService.flowObjectiveService, times(2))
+ .filter(eq(deviceId), any());
+ }
+
+ @Test
+ public void testHandleNniFlowsIgmp() {
+ oltFlowService.enableDhcpOnNni = false;
+ oltFlowService.enableIgmpOnNni = true;
+ oltFlowService.handleNniFlows(testDevice, nniPort, OltFlowService.FlowOperation.ADD);
+
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(nniPort.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_IGMP))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .add();
+
+ // invoked with the correct DHCP filtering objective
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(deviceId), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ // invoked only twice, LLDP and DHCP
+ verify(oltFlowService.flowObjectiveService, times(2))
+ .filter(eq(deviceId), any());
+ }
+
+ @Test
+ public void testMacAddressNotRequired() {
+ // create a single service that doesn't require mac address
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation hsia = new UniTagInformation.Builder()
+ .setEnableMacLearning(false)
+ .build();
+ uniTagInformationList.add(hsia);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+
+ boolean isMacAvailable = oltFlowService.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
+ // we return true as we don't care wether it's available or not
+ Assert.assertTrue(isMacAvailable);
+ }
+
+ @Test
+ public void testIsMacAddressAvailableViaMacLearning() {
+
+ // create a single service that requires macLearning to be enabled
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ VlanId hsiaCtag = VlanId.vlanId((short) 11);
+ UniTagInformation hsia = new UniTagInformation.Builder()
+ .setPonCTag(hsiaCtag)
+ .setEnableMacLearning(true).build();
+ uniTagInformationList.add(hsia);
+
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+
+ // with no hosts discovered, return false
+ boolean isMacAvailable = oltFlowService.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
+ Assert.assertFalse(isMacAvailable);
+
+ // with a discovered host, return true
+ Host fakeHost = new DefaultHost(ProviderId.NONE, HostId.hostId(MacAddress.NONE), MacAddress.ZERO,
+ hsiaCtag, HostLocation.NONE, new HashSet<>(), DefaultAnnotations.builder().build());
+ Set<Host> hosts = new HashSet<>(Arrays.asList(fakeHost));
+ doReturn(hosts).when(oltFlowService.hostService).getConnectedHosts((ConnectPoint) any());
+
+ isMacAvailable = oltFlowService.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
+ Assert.assertTrue(isMacAvailable);
+ }
+
+ @Test
+ public void testIsMacAddressAvailableViaConfiguration() {
+ // create a single service that has a macAddress configured
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation hsia = new UniTagInformation.Builder()
+ .setConfiguredMacAddress("2e:0a:00:01:00:00")
+ .build();
+ uniTagInformationList.add(hsia);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+
+ boolean isMacAvailable = oltFlowService.isMacAddressAvailable(testDevice.id(), uniUpdateEnabled, si);
+ Assert.assertTrue(isMacAvailable);
+ }
+
+ @Test
+ public void testHandleSubscriberDhcpFlowsAdd() {
+
+ String usBp = "usBp";
+ String usOltBp = "usOltBp";
+ oltFlowService.enableDhcpV4 = true;
+
+ // create two services, one requires DHCP the other doesn't
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ VlanId hsiaCtag = VlanId.vlanId((short) 11);
+ UniTagInformation hsia = new UniTagInformation.Builder()
+ .setPonCTag(hsiaCtag)
+ .setTechnologyProfileId(64)
+ .setUniTagMatch(VlanId.vlanId(VlanId.NO_VID))
+ .setUpstreamBandwidthProfile(usBp)
+ .setUpstreamOltBandwidthProfile(usOltBp)
+ .setIsDhcpRequired(true).build();
+ UniTagInformation mc = new UniTagInformation.Builder()
+ .setIsDhcpRequired(false).build();
+ uniTagInformationList.add(hsia);
+ uniTagInformationList.add(mc);
+
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+
+ final DiscoveredSubscriber addedSub =
+ new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
+ false, si);
+
+ // return meter IDs
+ doReturn(MeterId.meterId(2)).when(oltFlowService.oltMeterService)
+ .getMeterIdForBandwidthProfile(addedSub.device.id(), usBp);
+ doReturn(MeterId.meterId(3)).when(oltFlowService.oltMeterService)
+ .getMeterIdForBandwidthProfile(addedSub.device.id(), usOltBp);
+
+ // TODO improve the matches on the filter
+ FilteringObjective expectedFilter = DefaultFilteringObjective.builder()
+ .permit()
+ .withKey(Criteria.matchInPort(addedSub.port.number()))
+ .addCondition(Criteria.matchEthType(EthType.EtherType.IPV4.ethType()))
+ .addCondition(Criteria.matchIPProtocol(IPv4.PROTOCOL_UDP))
+ .addCondition(Criteria.matchUdpSrc(TpPort.tpPort(68)))
+ .addCondition(Criteria.matchUdpDst(TpPort.tpPort(67)))
+ .fromApp(testAppId)
+ .withPriority(10000)
+ .add();
+
+ oltFlowService.handleSubscriberDhcpFlows(addedSub.device.id(), addedSub.port,
+ OltFlowService.FlowOperation.ADD, si);
+ verify(oltFlowService.flowObjectiveService, times(1))
+ .filter(eq(addedSub.device.id()), argThat(new FilteringObjectiveMatcher(expectedFilter)));
+ }
+
+ @Test
+ public void testInternalFlowListenerNotMaster() {
+ doReturn(false).when(oltFlowService.oltDeviceService).isLocalLeader(any());
+
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(DeviceId.deviceId("foo"))
+ .fromApp(testAppId)
+ .makePermanent()
+ .withPriority(1000)
+ .build();
+ FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
+ flowRule);
+
+ internalFlowListener.event(event);
+
+ // if we're not master of the device, we should not update
+ verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
+ }
+
+ @Test
+ public void testInternalFlowListenerDifferentApp() {
+ ApplicationId someAppId = new DefaultApplicationId(1, "org.opencord.olt.not-test");
+ FlowRule flowRule = DefaultFlowRule.builder()
+ .forDevice(DeviceId.deviceId("foo"))
+ .fromApp(someAppId)
+ .makePermanent()
+ .withPriority(1000)
+ .build();
+ FlowRuleEvent event = new FlowRuleEvent(FlowRuleEvent.Type.RULE_ADDED,
+ flowRule);
+
+ internalFlowListener.event(event);
+
+ // if we're not master of the device, we should not update
+ verify(internalFlowListener, never()).updateCpStatus(any(), any(), any());
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltFlowTest.java b/impl/src/test/java/org/opencord/olt/impl/OltFlowTest.java
deleted file mode 100644
index 752e44d..0000000
--- a/impl/src/test/java/org/opencord/olt/impl/OltFlowTest.java
+++ /dev/null
@@ -1,574 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-
-import com.google.common.collect.Maps;
-import org.apache.commons.lang3.tuple.Pair;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.EthType;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.cluster.RoleInfo;
-import org.onosproject.mastership.MastershipInfo;
-import org.onosproject.mastership.MastershipListener;
-import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.DefaultPort;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.MastershipRole;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.EthTypeCriterion;
-import org.onosproject.net.flow.criteria.PortCriterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flowobjective.FilteringObjQueueKey;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.ForwardingObjQueueKey;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjQueueKey;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.meter.MeterId;
-import org.onosproject.net.meter.MeterKey;
-import org.opencord.olt.AccessDevicePort;
-import org.opencord.sadis.BandwidthProfileInformation;
-import org.opencord.sadis.UniTagInformation;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ListMultimap;
-
-public class OltFlowTest extends TestBase {
- private OltFlowService oltFlowService;
- AccessDevicePort uniPort1 = new AccessDevicePort(new DefaultPort(olt, PortNumber.portNumber(1), true,
- DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "BBSM00010001-1").build()),
- AccessDevicePort.Type.UNI);
- AccessDevicePort uniPort2 = new AccessDevicePort(new DefaultPort(olt, PortNumber.portNumber(2), true,
- DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "BBSM00010002-1").build()),
- AccessDevicePort.Type.UNI);
- AccessDevicePort nniPort = new AccessDevicePort(new DefaultPort(olt, PortNumber.portNumber(65535), true,
- DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "nni-1048576").build()),
- AccessDevicePort.Type.NNI);
-
- MacAddress macAddress = MacAddress.valueOf("00:00:00:00:0a:0b");
-
- UniTagInformation.Builder tagInfoBuilder = new UniTagInformation.Builder();
- UniTagInformation uniTagInfo = tagInfoBuilder.setUniTagMatch(VlanId.vlanId((short) 35))
- .setPonCTag(VlanId.vlanId((short) 33))
- .setPonSTag(VlanId.vlanId((short) 7))
- .setDsPonCTagPriority(0)
- .setUsPonSTagPriority(0)
- .setTechnologyProfileId(64)
- .setDownstreamBandwidthProfile(dsBpId)
- .setUpstreamBandwidthProfile(usBpId)
- .setIsDhcpRequired(true)
- .setIsIgmpRequired(true)
- .build();
-
- UniTagInformation.Builder tagInfoBuilderNoPcp = new UniTagInformation.Builder();
- UniTagInformation uniTagInfoNoPcp = tagInfoBuilderNoPcp.setUniTagMatch(VlanId.vlanId((short) 35))
- .setPonCTag(VlanId.vlanId((short) 34))
- .setPonSTag(VlanId.vlanId((short) 7))
- .setDsPonCTagPriority(-1)
- .setUsPonSTagPriority(-1)
- .setUsPonCTagPriority(-1)
- .setDsPonSTagPriority(-1)
- .setTechnologyProfileId(64)
- .setDownstreamBandwidthProfile(dsBpId)
- .setUpstreamBandwidthProfile(usBpId)
- .setIsDhcpRequired(true)
- .setIsIgmpRequired(true)
- .build();
-
- UniTagInformation.Builder tagInfoBuilder2 = new UniTagInformation.Builder();
- UniTagInformation uniTagInfoNoDhcpNoIgmp = tagInfoBuilder2
- .setUniTagMatch(VlanId.vlanId((short) 35))
- .setPonCTag(VlanId.vlanId((short) 33))
- .setPonSTag(VlanId.vlanId((short) 7))
- .setDsPonCTagPriority(0)
- .setUsPonSTagPriority(0)
- .setTechnologyProfileId(64)
- .setDownstreamBandwidthProfile(dsBpId)
- .setUpstreamBandwidthProfile(usBpId)
- .build();
-
- @Before
- public void setUp() {
- oltFlowService = new OltFlowService();
- oltFlowService.oltMeterService = new MockOltMeterService();
- oltFlowService.flowObjectiveService = new MockOltFlowObjectiveService();
- oltFlowService.mastershipService = new MockMastershipService();
- oltFlowService.sadisService = new MockSadisService();
- oltFlowService.bpService = oltFlowService.sadisService.getBandwidthProfileService();
- oltFlowService.appId = appId;
- oltFlowService.pendingEapolForDevice = Maps.newConcurrentMap();
- }
-
- @Test
- public void testDhcpFiltering() {
- System.out.println(uniPort1);
- oltFlowService.flowObjectiveService.clearQueue();
- // ensure upstream dhcp traps can be added and removed
- oltFlowService.processDhcpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 1;
- oltFlowService.processDhcpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- false, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 2;
-
- // Ensure upstream flow has no pcp unless properly specified.
- oltFlowService.processDhcpFilteringObjectives(uniPort2,
- usMeterId, null, uniTagInfoNoPcp,
- true, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 3;
-
- // ensure upstream flows are not added if uniTagInfo is missing dhcp requirement
- oltFlowService.processDhcpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfoNoDhcpNoIgmp,
- true, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 3;
-
- // ensure downstream traps don't succeed without global config for nni ports
- oltFlowService.processDhcpFilteringObjectives(nniPort,
- null, null, null,
- true, false, Optional.empty());
- oltFlowService.processDhcpFilteringObjectives(nniPort,
- null, null, null,
- false, false, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 3;
- // do global config for nni ports and now it should succeed
- oltFlowService.enableDhcpOnNni = true;
- oltFlowService.processDhcpFilteringObjectives(nniPort,
- null, null, null,
- true, false, Optional.empty());
- oltFlowService.processDhcpFilteringObjectives(nniPort,
- null, null, null,
- false, false, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 5;
-
- // turn on DHCPv6 and we should get 2 flows
- oltFlowService.enableDhcpV6 = true;
- oltFlowService.processDhcpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 7;
-
- // turn off DHCPv4 and it's only v6
- oltFlowService.enableDhcpV4 = false;
- oltFlowService.processDhcpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true, Optional.empty());
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 8;
-
- // cleanup
- oltFlowService.flowObjectiveService.clearQueue();
- oltFlowService.enableDhcpV4 = true;
- oltFlowService.enableDhcpV6 = false;
- }
-
- @Test
- public void testPppoedFiltering() {
- oltFlowService.flowObjectiveService.clearQueue();
-
- // ensure pppoed traps are not added if global config is off.
- oltFlowService.enablePppoe = false;
- oltFlowService.processPPPoEDFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 0;
-
- // ensure upstream pppoed traps can be added and removed
- oltFlowService.enablePppoe = true;
- oltFlowService.processPPPoEDFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 1;
- oltFlowService.processPPPoEDFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- false, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 2;
-
- // ensure downstream pppoed traps can be added and removed
- oltFlowService.processPPPoEDFilteringObjectives(nniPort,
- null, null, null,
- true, false);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 3;
- oltFlowService.processPPPoEDFilteringObjectives(nniPort,
- null, null, null,
- false, false);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 4;
-
- // cleanup
- oltFlowService.flowObjectiveService.clearQueue();
- }
-
- @Test
- public void testIgmpFiltering() {
- oltFlowService.flowObjectiveService.clearQueue();
-
- // ensure igmp flows can be added and removed
- oltFlowService.processIgmpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfo,
- true, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 1;
- oltFlowService.processIgmpFilteringObjectives(uniPort1, usMeterId,
- null, uniTagInfo,
- false, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 2;
-
- // ensure igmp flow is not added if uniTag has no igmp requirement
- oltFlowService.processIgmpFilteringObjectives(uniPort1,
- usMeterId, null, uniTagInfoNoDhcpNoIgmp,
- true, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 2;
-
- //ensure igmp flow on NNI fails without global setting
- oltFlowService.processIgmpFilteringObjectives(nniPort,
- null, null, null,
- true, false);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 2;
-
- // igmp trap on NNI should succeed with global config
- oltFlowService.enableIgmpOnNni = true;
- oltFlowService.processIgmpFilteringObjectives(nniPort,
- null, null, null,
- true, false);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives().size() == 3;
- // cleanup
- oltFlowService.flowObjectiveService.clearQueue();
-
- }
-
- @Test
- public void testEapolFiltering() {
- addBandwidthProfile(uniTagInfo.getUpstreamBandwidthProfile());
- oltFlowService.enableEapol = true;
-
- //will install
- oltFlowService.processEapolFilteringObjectives(uniPort1,
- uniTagInfo.getUpstreamBandwidthProfile(), Optional.empty(), new CompletableFuture<>(),
- uniTagInfo.getUniTagMatch(), true);
-
- //bp profile doesn't exist
- oltFlowService.processEapolFilteringObjectives(uniPort1,
- uniTagInfo.getDownstreamBandwidthProfile(), Optional.empty(), new CompletableFuture<>(),
- uniTagInfo.getUniTagMatch(), true);
- }
-
- @Test
- public void testLldpFiltering() {
- oltFlowService.processLldpFilteringObjective(nniPort, true);
- oltFlowService.processLldpFilteringObjective(nniPort, false);
- }
-
- @Test
- public void testNniFiltering() {
- oltFlowService.flowObjectiveService.clearQueue();
- oltFlowService.enableDhcpOnNni = true;
- oltFlowService.enableIgmpOnNni = true;
- oltFlowService.processNniFilteringObjectives(nniPort, true);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives()
- .size() == 3;
- oltFlowService.processNniFilteringObjectives(nniPort, false);
- assert oltFlowService.flowObjectiveService.getPendingFlowObjectives()
- .size() == 6;
- oltFlowService.flowObjectiveService.clearQueue();
- }
-
- @Test
- public void testUpBuilder() {
- ForwardingObjective objective =
- oltFlowService.createUpBuilder(nniPort, uniPort1, usMeterId, usMeterId, uniTagInfo).add();
- checkObjective(objective, true);
- }
-
- @Test
- public void testDownBuilder() {
- ForwardingObjective objective =
- oltFlowService.createDownBuilder(nniPort, uniPort1, dsMeterId, dsMeterId, uniTagInfo,
- Optional.of(macAddress)).remove();
- checkObjective(objective, false);
- }
-
- private void checkObjective(ForwardingObjective fwd, boolean upstream) {
- TrafficTreatment treatment = fwd.treatment();
-
- //check instructions
- Set<Instructions.MeterInstruction> meters = treatment.meters();
- assert !meters.isEmpty();
-
- Instructions.MetadataInstruction writeMetadata = treatment.writeMetadata();
- assert writeMetadata != null;
-
- List<Instruction> immediateInstructions = treatment.immediate();
- Optional<Instruction> vlanInstruction = immediateInstructions.stream()
- .filter(i -> i.type() == Instruction.Type.L2MODIFICATION)
- .filter(i -> ((L2ModificationInstruction) i).subtype() ==
- L2ModificationInstruction.L2SubType.VLAN_PUSH ||
- ((L2ModificationInstruction) i).subtype() ==
- L2ModificationInstruction.L2SubType.VLAN_POP)
- .findAny();
-
- assert vlanInstruction.isPresent();
-
- //check match criteria
- TrafficSelector selector = fwd.selector();
- assert selector.getCriterion(Criterion.Type.IN_PORT) != null;
- assert selector.getCriterion(Criterion.Type.VLAN_VID) != null;
-
- if (!upstream) {
- assert selector.getCriterion(Criterion.Type.METADATA) != null;
- assert selector.getCriterion(Criterion.Type.ETH_DST) != null;
- }
- }
-
- private class MockOltMeterService implements org.opencord.olt.internalapi.AccessDeviceMeterService {
- @Override
- public ImmutableMap<String, Collection<MeterKey>> getBpMeterMappings() {
- return null;
- }
-
- @Override
- public MeterId getMeterIdFromBpMapping(DeviceId deviceId, String bandwidthProfile) {
- return null;
- }
-
-
- @Override
- public ImmutableSet<MeterKey> getProgMeters() {
- return null;
- }
-
- @Override
- public MeterId createMeter(DeviceId deviceId, BandwidthProfileInformation bpInfo,
- CompletableFuture<Object> meterFuture) {
- return usMeterId;
- }
-
- @Override
- public void removeFromPendingMeters(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
-
- }
-
- @Override
- public boolean checkAndAddPendingMeter(DeviceId deviceId, BandwidthProfileInformation bwpInfo) {
- return false;
- }
-
-
- @Override
- public void clearMeters(DeviceId deviceId) {
- }
-
- @Override
- public void clearDeviceState(DeviceId deviceId) {
-
- }
- }
-
- private class MockOltFlowObjectiveService implements org.onosproject.net.flowobjective.FlowObjectiveService {
- List<String> flowObjectives = new ArrayList<>();
-
- @Override
- public void filter(DeviceId deviceId, FilteringObjective filteringObjective) {
- flowObjectives.add(filteringObjective.toString());
- EthTypeCriterion ethType = (EthTypeCriterion)
- filterForCriterion(filteringObjective.conditions(), Criterion.Type.ETH_TYPE);
-
- Instructions.MeterInstruction meter = filteringObjective.meta().metered();
- Instruction writeMetadata = filteringObjective.meta().writeMetadata();
- VlanIdCriterion vlanIdCriterion = (VlanIdCriterion)
- filterForCriterion(filteringObjective.conditions(), Criterion.Type.VLAN_VID);
- PortCriterion portCriterion = (PortCriterion) filteringObjective.key();
-
- filteringObjective.meta().allInstructions().forEach(instruction -> {
- if (instruction.type().equals(Instruction.Type.L2MODIFICATION)) {
- L2ModificationInstruction l2Instruction = (L2ModificationInstruction) instruction;
- if (l2Instruction.subtype().equals(L2ModificationInstruction.L2SubType.VLAN_PCP)) {
- //this, given the uniTagInfo we provide, should not be present
- assert false;
- }
- }
- });
-
-
- if (ethType.ethType().equals(EthType.EtherType.LLDP.ethType()) ||
- portCriterion.port().equals(nniPort.number())) {
- assert meter == null;
- assert writeMetadata == null;
- assert vlanIdCriterion == null;
- } else {
- assert meter.meterId().equals(usMeterId) || meter.meterId().equals(dsMeterId);
- assert writeMetadata != null;
- assert vlanIdCriterion == null || vlanIdCriterion.vlanId() == uniTagInfo.getUniTagMatch()
- || vlanIdCriterion.vlanId() == uniTagInfoNoPcp.getUniTagMatch();
- }
-
- }
-
- @Override
- public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
-
- }
-
- @Override
- public void next(DeviceId deviceId, NextObjective nextObjective) {
-
- }
-
- @Override
- public int allocateNextId() {
- return 0;
- }
-
- @Override
- public void initPolicy(String s) {
-
- }
-
- @Override
- public void apply(DeviceId deviceId, Objective objective) {
-
- }
-
- @Override
- public Map<Pair<Integer, DeviceId>, List<String>> getNextMappingsChain() {
- return null;
- }
-
- @Override
- public List<String> getNextMappings() {
- return null;
- }
-
- @Override
- public List<String> getPendingFlowObjectives() {
- return ImmutableList.copyOf(flowObjectives);
- }
-
- @Override
- public ListMultimap<FilteringObjQueueKey, Objective> getFilteringObjQueue() {
- return null;
- }
-
- @Override
- public ListMultimap<ForwardingObjQueueKey, Objective> getForwardingObjQueue() {
- return null;
- }
-
- @Override
- public ListMultimap<NextObjQueueKey, Objective> getNextObjQueue() {
- return null;
- }
-
- @Override
- public Map<FilteringObjQueueKey, Objective> getFilteringObjQueueHead() {
- return null;
- }
-
- @Override
- public Map<ForwardingObjQueueKey, Objective> getForwardingObjQueueHead() {
- return null;
- }
-
- @Override
- public Map<NextObjQueueKey, Objective> getNextObjQueueHead() {
- return null;
- }
-
- @Override
- public void clearQueue() {
- flowObjectives.clear();
- }
-
- private Criterion filterForCriterion(Collection<Criterion> criteria, Criterion.Type type) {
- return criteria.stream()
- .filter(c -> c.type().equals(type))
- .limit(1)
- .findFirst().orElse(null);
- }
- }
-
- private class MockMastershipService implements org.onosproject.mastership.MastershipService {
- @Override
- public MastershipRole getLocalRole(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public boolean isLocalMaster(DeviceId deviceId) {
- return true;
- }
-
- @Override
- public CompletableFuture<MastershipRole> requestRoleFor(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public CompletableFuture<Void> relinquishMastership(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public NodeId getMasterFor(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public RoleInfo getNodesFor(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public MastershipInfo getMastershipFor(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public Set<DeviceId> getDevicesOf(NodeId nodeId) {
- return null;
- }
-
- @Override
- public void addListener(MastershipListener mastershipListener) {
-
- }
-
- @Override
- public void removeListener(MastershipListener mastershipListener) {
-
- }
- }
-}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltMeterServiceTest.java b/impl/src/test/java/org/opencord/olt/impl/OltMeterServiceTest.java
new file mode 100644
index 0000000..a7d653c
--- /dev/null
+++ b/impl/src/test/java/org/opencord/olt/impl/OltMeterServiceTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.MeterId;
+import org.onosproject.net.meter.MeterServiceAdapter;
+import org.onosproject.net.meter.MeterState;
+import org.onosproject.store.service.StorageServiceAdapter;
+import org.onosproject.store.service.TestStorageService;
+import org.opencord.sadis.SadisService;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
+
+public class OltMeterServiceTest extends OltTestHelpers {
+ OltMeterService oltMeterService;
+ OltMeterService component;
+
+ DeviceId deviceId = DeviceId.deviceId("foo");
+
+ @Before
+ public void setUp() {
+ component = new OltMeterService();
+ component.cfgService = new ComponentConfigAdapter();
+ component.coreService = new CoreServiceAdapter();
+ component.storageService = new StorageServiceAdapter();
+ component.sadisService = Mockito.mock(SadisService.class);
+ component.meterService = new MeterServiceAdapter();
+ component.storageService = new TestStorageService();
+ component.activate(null);
+ oltMeterService = Mockito.spy(component);
+ }
+
+ @After
+ public void tearDown() {
+ component.deactivate(null);
+ }
+
+ @Test
+ public void testHasMeter() {
+
+ MeterData meterPending = new MeterData(MeterId.meterId(1),
+ MeterState.PENDING_ADD, "pending");
+ MeterData meterAdded = new MeterData(MeterId.meterId(2),
+ MeterState.ADDED, DEFAULT_BP_ID_DEFAULT);
+
+ Map<String, MeterData> deviceMeters = new HashMap<>();
+ deviceMeters.put("pending", meterPending);
+ deviceMeters.put(DEFAULT_BP_ID_DEFAULT, meterAdded);
+ oltMeterService.programmedMeters.put(deviceId, deviceMeters);
+
+ assert oltMeterService.hasMeterByBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT);
+ assert !oltMeterService.hasMeterByBandwidthProfile(deviceId, "pending");
+ assert !oltMeterService.hasMeterByBandwidthProfile(deviceId, "someBandwidthProfile");
+
+ assert !oltMeterService.hasMeterByBandwidthProfile(DeviceId.deviceId("bar"), DEFAULT_BP_ID_DEFAULT);
+ }
+
+ @Test
+ public void testGetMeterId() {
+
+ MeterData meterAdded = new MeterData(MeterId.meterId(2),
+ MeterState.ADDED, DEFAULT_BP_ID_DEFAULT);
+
+ Map<String, MeterData> deviceMeters = new HashMap<>();
+ deviceMeters.put(DEFAULT_BP_ID_DEFAULT, meterAdded);
+ oltMeterService.programmedMeters.put(deviceId, deviceMeters);
+
+ Assert.assertNull(oltMeterService.getMeterIdForBandwidthProfile(deviceId, "pending"));
+ Assert.assertEquals(MeterId.meterId(2),
+ oltMeterService.getMeterIdForBandwidthProfile(deviceId, DEFAULT_BP_ID_DEFAULT));
+ }
+
+ @Test
+ public void testCreateMeter() {
+
+ DeviceId deviceId = DeviceId.deviceId("foo");
+ String bp = "Default";
+
+ // if we already have a meter do nothing and return true
+ doReturn(true).when(oltMeterService).hasMeterByBandwidthProfile(deviceId, bp);
+ Assert.assertTrue(oltMeterService.createMeter(deviceId, bp));
+ verify(oltMeterService, never()).createMeterForBp(any(), any());
+
+ // if we have a pending meter, do nothing and return false
+ doReturn(false).when(oltMeterService).hasMeterByBandwidthProfile(deviceId, bp);
+ doReturn(true).when(oltMeterService).hasPendingMeterByBandwidthProfile(deviceId, bp);
+ Assert.assertFalse(oltMeterService.createMeter(deviceId, bp));
+ verify(oltMeterService, never()).createMeterForBp(any(), any());
+
+ // if the meter is not present at all, create it and return false
+ doReturn(false).when(oltMeterService).hasMeterByBandwidthProfile(deviceId, bp);
+ doReturn(false).when(oltMeterService).hasPendingMeterByBandwidthProfile(deviceId, bp);
+ Assert.assertFalse(oltMeterService.createMeter(deviceId, bp));
+ verify(oltMeterService, times(1)).createMeterForBp(deviceId, bp);
+ }
+
+ @Test
+ public void testConcurrentMeterCreation() throws InterruptedException {
+
+ ExecutorService executor = Executors.newFixedThreadPool(4);
+
+ DeviceId deviceId = DeviceId.deviceId("foo");
+ String bp = "Default";
+
+ // try to create 4 meters at the same time, only one should be created
+ for (int i = 0; i < 4; i++) {
+
+ executor.execute(() -> {
+ oltMeterService.createMeter(deviceId, bp);
+ });
+ }
+
+ TimeUnit.MILLISECONDS.sleep(600);
+
+ verify(oltMeterService, times(4)).hasMeterByBandwidthProfile(deviceId, bp);
+ verify(oltMeterService, times(1)).createMeterForBp(deviceId, bp);
+ }
+}
\ No newline at end of file
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltMeterTest.java b/impl/src/test/java/org/opencord/olt/impl/OltMeterTest.java
deleted file mode 100644
index 000ea35..0000000
--- a/impl/src/test/java/org/opencord/olt/impl/OltMeterTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import com.google.common.collect.ImmutableMap;
-import org.junit.Before;
-import org.junit.Test;
-import org.onosproject.cfg.ComponentConfigAdapter;
-import org.onosproject.core.CoreServiceAdapter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.meter.DefaultMeter;
-import org.onosproject.net.meter.Meter;
-import org.onosproject.net.meter.MeterId;
-import org.onosproject.net.meter.MeterKey;
-import org.onosproject.net.meter.MeterListener;
-import org.onosproject.net.meter.MeterRequest;
-import org.onosproject.store.service.TestStorageService;
-import org.opencord.sadis.BandwidthProfileInformation;
-
-import java.util.Collection;
-import java.util.concurrent.CompletableFuture;
-
-public class OltMeterTest extends TestBase {
- private OltMeterService oltMeterService;
-
- private BandwidthProfileInformation bandwidthProfileInformation = new BandwidthProfileInformation();
-
- @Before
- public void setUp() {
- oltMeterService = new OltMeterService();
- oltMeterService.storageService = new TestStorageService();
- oltMeterService.meterService = new MockMeterService();
- oltMeterService.coreService = new CoreServiceAdapter();
- oltMeterService.componentConfigService = new ComponentConfigAdapter();
- oltMeterService.activate(null);
- oltMeterService.bpInfoToMeter = new MockConsistentMultimap<>();
- }
-
- @Test
- public void testAddAndGetMeterIdToBpMapping() {
- oltMeterService.addMeterIdToBpMapping(DEVICE_ID_1, usMeterId, usBpId);
- MeterId usMeterId = oltMeterService.getMeterIdFromBpMapping(DEVICE_ID_1, usBpId);
- assert usMeterId.equals(this.usMeterId);
-
- oltMeterService.addMeterIdToBpMapping(DEVICE_ID_1, dsMeterId, dsBpId);
- MeterId dsMeterId = oltMeterService.getMeterIdFromBpMapping(DEVICE_ID_1, dsBpId);
- assert dsMeterId.equals(this.dsMeterId);
-
- ImmutableMap<String, Collection<MeterKey>> meterMappings = oltMeterService.getBpMeterMappings();
- assert meterMappings.size() == 2;
- }
-
- @Test
- public void testCreateMeter() {
- //with provided bandwidth profile information
- bandwidthProfileInformation.setId(usBpId);
- bandwidthProfileInformation.setExceededInformationRate(10000);
- bandwidthProfileInformation.setExceededBurstSize(10000L);
- bandwidthProfileInformation.setCommittedBurstSize(10000L);
- bandwidthProfileInformation.setCommittedInformationRate(10000);
-
- oltMeterService.addMeterIdToBpMapping(DEVICE_ID_1, usMeterId, usBpId);
-
-
- MeterId meterId =
- oltMeterService.createMeter(DEVICE_ID_1, bandwidthProfileInformation, new CompletableFuture<>());
- assert meterId != null;
-
- //with null bandwidth profile information
- meterId = oltMeterService.createMeter(DEVICE_ID_1, null, new CompletableFuture<>());
- assert meterId == null;
- }
-
-
- private class MockMeterService implements org.onosproject.net.meter.MeterService {
- @Override
- public Meter submit(MeterRequest meterRequest) {
- return DefaultMeter.builder()
- .forDevice(DEVICE_ID_1)
- .fromApp(appId)
- .withId(usMeterId)
- .build();
- }
-
- @Override
- public void withdraw(MeterRequest meterRequest, MeterId meterId) {
-
- }
-
- @Override
- public Meter getMeter(DeviceId deviceId, MeterId meterId) {
- return null;
- }
-
- @Override
- public Collection<Meter> getAllMeters() {
- return null;
- }
-
- @Override
- public Collection<Meter> getMeters(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public MeterId allocateMeterId(DeviceId deviceId) {
- return null;
- }
-
- @Override
- public void freeMeterId(DeviceId deviceId, MeterId meterId) {
-
- }
-
- @Override
- public void addListener(MeterListener meterListener) {
-
- }
-
- @Override
- public void removeListener(MeterListener meterListener) {
-
- }
- }
-}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltTest.java b/impl/src/test/java/org/opencord/olt/impl/OltTest.java
index af63004..e8a76d0 100644
--- a/impl/src/test/java/org/opencord/olt/impl/OltTest.java
+++ b/impl/src/test/java/org/opencord/olt/impl/OltTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,155 +13,196 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package org.opencord.olt.impl;
-import static org.junit.Assert.assertEquals;
-
-import java.util.Set;
-
-import com.google.common.collect.Maps;
+import org.junit.After;
+import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
-
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
import org.onlab.packet.ChassisId;
+import org.onosproject.cfg.ComponentConfigAdapter;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.core.DefaultApplicationId;
import org.onosproject.net.AnnotationKeys;
-import org.onosproject.net.Annotations;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
-import org.onosproject.net.Element;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceServiceAdapter;
+import org.onosproject.net.device.DeviceService;
import org.onosproject.net.provider.ProviderId;
+import org.onosproject.store.service.TestStorageService;
+import org.opencord.sadis.SadisService;
import org.opencord.sadis.SubscriberAndDeviceInformation;
+import org.opencord.sadis.UniTagInformation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-public class OltTest extends TestBase {
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.opencord.olt.impl.OsgiPropertyConstants.DEFAULT_BP_ID_DEFAULT;
+
+/**
+ * Set of tests of the ONOS application component.
+ */
+
+@RunWith(MockitoJUnitRunner.class)
+public class OltTest extends OltTestHelpers {
+
+ private Olt component;
+ private final ApplicationId testAppId = new DefaultApplicationId(1, "org.opencord.olt.test");
private final Logger log = LoggerFactory.getLogger(getClass());
- private Olt olt;
-
- private static final String SCHEME_NAME = "olt";
- private static final DefaultAnnotations DEVICE_ANNOTATIONS = DefaultAnnotations.builder()
- .set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase()).build();
+ private DeviceId deviceId = DeviceId.deviceId("test-device");
+ private Device testDevice = new DefaultDevice(ProviderId.NONE, deviceId, Device.Type.OLT,
+ "testManufacturer", "1.0", "1.0", "SN", new ChassisId(1));
+ private Port uniUpdateEnabled = new OltPort(testDevice, true, PortNumber.portNumber(16),
+ DefaultAnnotations.builder().set(AnnotationKeys.PORT_NAME, "uni-1")
+ .build());
+ private ConnectPoint cp = new ConnectPoint(deviceId, uniUpdateEnabled.number());
+ private DiscoveredSubscriber sub;
@Before
public void setUp() {
- olt = new Olt();
- olt.deviceService = new MockDeviceService();
- olt.sadisService = new MockSadisService();
- olt.subsService = olt.sadisService.getSubscriberInfoService();
- olt.pendingSubscribersForDevice = Maps.newConcurrentMap();
+ component = new Olt();
+ component.requeueDelay = 0; // avoid delays in the queue add to make things easier in testing
+ component.cfgService = new ComponentConfigAdapter();
+ component.deviceService = Mockito.mock(DeviceService.class);
+ component.storageService = new TestStorageService();
+ component.coreService = Mockito.spy(new CoreServiceAdapter());
+ component.oltDeviceService = Mockito.mock(OltDeviceService.class);
+
+ doReturn(testAppId).when(component.coreService).registerApplication("org.opencord.olt");
+
+ component.discoveredSubscriberExecutor =
+ Executors.newSingleThreadScheduledExecutor(
+ groupedThreads("onos/olt",
+ "discovered-cp-%d", log));
+ component.flowsExecutor =
+ Executors.newFixedThreadPool(component.flowProcessingThreads,
+ groupedThreads("onos/olt-service",
+ "flows-installer-%d"));
+
+ component.subscriberExecutor = Executors.newFixedThreadPool(component.subscriberProcessingThreads,
+ groupedThreads("onos/olt-service",
+ "subscriber-installer-%d"));
+ SadisService sadisService = Mockito.mock(SadisService.class);
+ component.oltFlowService = Mockito.mock(OltFlowService.class);
+ component.sadisService = sadisService;
+
+ // reset the spy on oltFlowService
+ reset(component.oltFlowService);
+
+ component.bindSadisService(sadisService);
+ component.eventsQueues = component.storageService.
+ <ConnectPoint, LinkedBlockingQueue<DiscoveredSubscriber>>consistentMapBuilder().build().asJavaMap();
+ component.eventsQueues.put(cp, new LinkedBlockingQueue<>());
+
+ component.discoveredSubscriberExecutor.execute(() -> {
+ component.processDiscoveredSubscribers();
+ });
+ // create empty service for testing
+ List<UniTagInformation> uniTagInformationList = new LinkedList<>();
+ UniTagInformation empty = new UniTagInformation.Builder().build();
+ uniTagInformationList.add(empty);
+ SubscriberAndDeviceInformation si = new SubscriberAndDeviceInformation();
+ si.setUniTagList(uniTagInformationList);
+ sub = new DiscoveredSubscriber(testDevice,
+ uniUpdateEnabled, DiscoveredSubscriber.Status.ADDED,
+ false, si);
}
- /**
- * Tests that the getSubscriber method does throw a NullPointerException with a meaningful message.
- */
+ @After
+ public void tearDown() {
+ component.deactivate(null);
+ }
+
@Test
- public void testGetSubscriberError() {
- ConnectPoint cp = ConnectPoint.deviceConnectPoint(OLT_DEV_ID + "/" + 1);
- try {
- olt.getSubscriber(cp);
- } catch (NullPointerException e) {
- assertEquals(e.getMessage(), "Invalid connect point");
- }
+ public void testProcessDiscoveredSubscribersBasicPortSuccess() throws Exception {
+ doReturn(true).when(component.deviceService).isAvailable(any());
+ doReturn(sub.port).when(component.deviceService).getPort(any(), any());
+ doReturn(true).when(component.oltFlowService).handleBasicPortFlows(eq(sub), eq(DEFAULT_BP_ID_DEFAULT),
+ eq(DEFAULT_BP_ID_DEFAULT));
+ doReturn(true).when(component.oltDeviceService).isLocalLeader(cp.deviceId());
+
+ // adding the discovered subscriber to the queue
+ LinkedBlockingQueue<DiscoveredSubscriber> q = component.eventsQueues.get(cp);
+ q.add(sub);
+ component.eventsQueues.put(cp, q);
+
+ // check that we're calling the correct method
+ TimeUnit.MILLISECONDS.sleep(600);
+ verify(component.oltFlowService, atLeastOnce()).handleBasicPortFlows(eq(sub), eq(DEFAULT_BP_ID_DEFAULT),
+ eq(DEFAULT_BP_ID_DEFAULT));
+
+ // check if the method doesn't throw an exception we're removing the subscriber from the queue
+ LinkedBlockingQueue<DiscoveredSubscriber> updatedQueue = component.eventsQueues.get(cp);
+ assert updatedQueue.isEmpty();
}
- /**
- * Tests that the getSubscriber method returns Subscriber informations.
- */
@Test
- public void testGetSubscriber() {
- ConnectPoint cp = ConnectPoint.deviceConnectPoint(OLT_DEV_ID + "/" + 2);
+ public void testProcessDiscoveredSubscribersBasicPortException() throws Exception {
+ doReturn(true).when(component.deviceService).isAvailable(any());
+ doReturn(sub.port).when(component.deviceService).getPort(any(), any());
+ doReturn(false).when(component.oltFlowService).handleBasicPortFlows(any(), eq(DEFAULT_BP_ID_DEFAULT),
+ eq(DEFAULT_BP_ID_DEFAULT));
+ doReturn(true).when(component.oltDeviceService).isLocalLeader(cp.deviceId());
+ // replace the queue with a spy
+ LinkedBlockingQueue<DiscoveredSubscriber> q = component.eventsQueues.get(cp);
+ LinkedBlockingQueue<DiscoveredSubscriber> spiedQueue = spy(q);
+ // adding the discovered subscriber to the queue
+ spiedQueue.add(sub);
+ component.eventsQueues.put(cp, spiedQueue);
- SubscriberAndDeviceInformation s = olt.getSubscriber(cp);
+ TimeUnit.MILLISECONDS.sleep(600);
- assertEquals(s.circuitId(), CLIENT_CIRCUIT_ID);
- assertEquals(s.nasPortId(), CLIENT_NAS_PORT_ID);
+ // check that we're calling the correct method,
+ // since the subscriber is not removed from the queue we're calling the method multiple times
+ verify(component.oltFlowService, atLeastOnce()).handleBasicPortFlows(eq(sub), eq(DEFAULT_BP_ID_DEFAULT),
+ eq(DEFAULT_BP_ID_DEFAULT));
+
+ // check if the method throws an exception we are not removing the subscriber from the queue
+ verify(spiedQueue, never()).remove(sub);
}
- private class MockDevice extends DefaultDevice {
+ @Test
+ public void testAddSubscriberToQueue() {
- public MockDevice(ProviderId providerId, DeviceId id, Type type,
- String manufacturer, String hwVersion, String swVersion,
- String serialNumber, ChassisId chassisId, Annotations... annotations) {
- super(providerId, id, type, manufacturer, hwVersion, swVersion, serialNumber,
- chassisId, annotations);
- }
+ // replace the queue with a spy
+ LinkedBlockingQueue<DiscoveredSubscriber> q = component.eventsQueues.get(cp);
+ LinkedBlockingQueue<DiscoveredSubscriber> spiedQueue = spy(q);
+ component.eventsQueues.put(cp, spiedQueue);
+
+ // trying to add the same event twice should result in a single item in the queue
+ component.addSubscriberToQueue(sub);
+ component.addSubscriberToQueue(sub);
+
+ verify(spiedQueue, times(1)).add(sub);
+ Assert.assertEquals(1, spiedQueue.size());
+
+
+
}
- private class MockDeviceService extends DeviceServiceAdapter {
-
- private ProviderId providerId = new ProviderId("of", "foo");
- private final Device device1 = new MockDevice(providerId, DEVICE_ID_1, Device.Type.SWITCH,
- "foo.inc", "0", "0", OLT_DEV_ID, new ChassisId(),
- DEVICE_ANNOTATIONS);
-
- @Override
- public Device getDevice(DeviceId devId) {
- return device1;
-
- }
-
- @Override
- public Port getPort(ConnectPoint cp) {
- log.info("Looking up port {}", cp.port().toString());
- if (cp.port().toString().equals("1")) {
- return null;
- }
- return new MockPort();
- }
- }
-
- private class MockPort implements Port {
-
- @Override
- public boolean isEnabled() {
- return true;
- }
-
- @Override
- public long portSpeed() {
- return 1000;
- }
-
- @Override
- public Element element() {
- return null;
- }
-
- @Override
- public PortNumber number() {
- return null;
- }
-
- @Override
- public Annotations annotations() {
- return new MockAnnotations();
- }
-
- @Override
- public Type type() {
- return Port.Type.FIBER;
- }
-
- private class MockAnnotations implements Annotations {
-
- @Override
- public String value(String val) {
- return "BRCM12345678";
- }
-
- @Override
- public Set<String> keys() {
- return null;
- }
- }
- }
-
-
-}
\ No newline at end of file
+}
diff --git a/impl/src/test/java/org/opencord/olt/impl/OltTestHelpers.java b/impl/src/test/java/org/opencord/olt/impl/OltTestHelpers.java
new file mode 100644
index 0000000..6965bd2
--- /dev/null
+++ b/impl/src/test/java/org/opencord/olt/impl/OltTestHelpers.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2021-present Open Networking Foundation
+ *
+ * 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.opencord.olt.impl;
+
+import com.google.common.collect.Maps;
+import org.mockito.ArgumentMatcher;
+import org.onosproject.net.Annotations;
+import org.onosproject.net.Element;
+import org.onosproject.net.Port;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.flowobjective.FilteringObjective;
+import org.opencord.sadis.BandwidthProfileInformation;
+
+import java.util.Map;
+
+@SuppressWarnings("checkstyle:HideUtilityClassConstructor")
+public class OltTestHelpers {
+
+ protected static final String CLIENT_NAS_PORT_ID = "PON 1/1";
+ protected static final String CLIENT_CIRCUIT_ID = "CIR-PON 1/1";
+ protected static final String OLT_DEV_ID = "of:00000000000000aa";
+ Map<String, BandwidthProfileInformation> bpInformation = Maps.newConcurrentMap();
+
+ protected class FilteringObjectiveMatcher extends ArgumentMatcher<FilteringObjective> {
+
+ private FilteringObjective left;
+
+ public FilteringObjectiveMatcher(FilteringObjective left) {
+ this.left = left;
+ }
+
+ @Override
+ public boolean matches(Object right) {
+ // NOTE this matcher can be improved
+ FilteringObjective r = (FilteringObjective) right;
+ boolean matches = left.type().equals(r.type()) &&
+ left.key().equals(r.key()) &&
+ left.conditions().equals(r.conditions()) &&
+ left.appId().equals(r.appId()) &&
+ left.priority() == r.priority();
+
+ if (left.meta() != null) {
+ if (left.meta().equals(r.meta())) {
+ return matches;
+ } else {
+ return false;
+ }
+ }
+ return matches;
+ }
+ }
+
+ public class OltPort implements Port {
+
+ public boolean enabled;
+ public PortNumber portNumber;
+ public Annotations annotations;
+ public Element element;
+
+ public OltPort(Element element, boolean enabled, PortNumber portNumber, Annotations annotations) {
+ this.enabled = enabled;
+ this.portNumber = portNumber;
+ this.annotations = annotations;
+ this.element = element;
+ }
+
+ @Override
+ public Element element() {
+ return element;
+ }
+
+ @Override
+ public PortNumber number() {
+ return portNumber;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ @Override
+ public Type type() {
+ return null;
+ }
+
+ @Override
+ public long portSpeed() {
+ return 0;
+ }
+
+ @Override
+ public Annotations annotations() {
+ return annotations;
+ }
+ }
+}
diff --git a/impl/src/test/java/org/opencord/olt/impl/TestBase.java b/impl/src/test/java/org/opencord/olt/impl/TestBase.java
deleted file mode 100644
index 87db5d1..0000000
--- a/impl/src/test/java/org/opencord/olt/impl/TestBase.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.opencord.olt.impl;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multiset;
-import org.onlab.packet.ChassisId;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.MacAddress;
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.net.DefaultDevice;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.meter.MeterId;
-import org.onosproject.store.service.AsyncConsistentMultimap;
-import org.onosproject.store.service.ConsistentMultimap;
-import org.onosproject.store.service.ConsistentMultimapBuilder;
-import org.onosproject.store.service.MultimapEventListener;
-import org.onosproject.store.service.TestConsistentMultimap;
-import org.onosproject.store.service.Versioned;
-import org.opencord.sadis.BandwidthProfileInformation;
-import org.opencord.sadis.BaseInformationService;
-import org.opencord.sadis.SadisService;
-import org.opencord.sadis.SubscriberAndDeviceInformation;
-
-import java.util.AbstractMap;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-public class TestBase {
-
- protected static final String CLIENT_NAS_PORT_ID = "PON 1/1";
- protected static final String CLIENT_CIRCUIT_ID = "CIR-PON 1/1";
- protected static final String OLT_DEV_ID = "of:00000000000000aa";
- protected static final DeviceId DEVICE_ID_1 = DeviceId.deviceId(OLT_DEV_ID);
- protected MeterId usMeterId = MeterId.meterId(1);
- protected MeterId dsMeterId = MeterId.meterId(2);
- protected MeterId usOltMeterId = MeterId.meterId(3);
- protected MeterId dsOltMeterId = MeterId.meterId(4);
- protected String usBpId = "HSIA-US";
- protected String dsBpId = "HSIA-DS";
- protected DefaultApplicationId appId = new DefaultApplicationId(1, "OltServices");
-
- protected static Device olt = new DefaultDevice(null, DeviceId.deviceId(OLT_DEV_ID), Device.Type.SWITCH,
- "VOLTHA Project", "open_pon", "open_pon", "BBSIM_OLT_1", new ChassisId("a0a0a0a0a01"));
-
- Map<String, BandwidthProfileInformation> bpInformation = Maps.newConcurrentMap();
-
- protected void addBandwidthProfile(String id) {
- BandwidthProfileInformation bpInfo = new BandwidthProfileInformation();
- bpInfo.setGuaranteedInformationRate(0);
- bpInfo.setCommittedInformationRate(10000);
- bpInfo.setCommittedBurstSize(1000L);
- bpInfo.setExceededBurstSize(2000L);
- bpInfo.setExceededInformationRate(20000);
- bpInformation.put(id, bpInfo);
- }
-
- protected class MockSadisService implements SadisService {
-
- @Override
- public BaseInformationService<SubscriberAndDeviceInformation> getSubscriberInfoService() {
- return new MockSubService();
- }
-
- @Override
- public BaseInformationService<BandwidthProfileInformation> getBandwidthProfileService() {
- return new MockBpService();
- }
- }
-
- private class MockBpService implements BaseInformationService<BandwidthProfileInformation> {
- @Override
- public void clearLocalData() {
-
- }
-
- @Override
- public void invalidateAll() {
-
- }
-
- @Override
- public void invalidateId(String id) {
-
- }
-
- @Override
- public BandwidthProfileInformation get(String id) {
- return bpInformation.get(id);
- }
-
- @Override
- public BandwidthProfileInformation getfromCache(String id) {
- return null;
- }
- }
-
- private class MockSubService implements BaseInformationService<SubscriberAndDeviceInformation> {
- MockSubscriberAndDeviceInformation sub =
- new MockSubscriberAndDeviceInformation(CLIENT_NAS_PORT_ID,
- CLIENT_NAS_PORT_ID, CLIENT_CIRCUIT_ID, null, null);
-
- @Override
- public SubscriberAndDeviceInformation get(String id) {
- return sub;
- }
-
- @Override
- public void clearLocalData() {
-
- }
-
- @Override
- public void invalidateAll() {
- }
-
- @Override
- public void invalidateId(String id) {
- }
-
- @Override
- public SubscriberAndDeviceInformation getfromCache(String id) {
- return null;
- }
- }
-
- private class MockSubscriberAndDeviceInformation extends SubscriberAndDeviceInformation {
-
- MockSubscriberAndDeviceInformation(String id, String nasPortId,
- String circuitId, MacAddress hardId,
- Ip4Address ipAddress) {
- this.setHardwareIdentifier(hardId);
- this.setId(id);
- this.setIPAddress(ipAddress);
- this.setNasPortId(nasPortId);
- this.setCircuitId(circuitId);
- }
- }
-
- class MockConsistentMultimap<K, V> implements ConsistentMultimap<K, V> {
- private HashMultimap<K, Versioned<V>> innermap;
- private AtomicLong counter = new AtomicLong();
-
- public MockConsistentMultimap() {
- this.innermap = HashMultimap.create();
- }
-
- private Versioned<V> version(V v) {
- return new Versioned<>(v, counter.incrementAndGet(), System.currentTimeMillis());
- }
-
- private Versioned<Collection<? extends V>> versionCollection(Collection<? extends V> collection) {
- return new Versioned<>(collection, counter.incrementAndGet(), System.currentTimeMillis());
- }
-
- @Override
- public int size() {
- return innermap.size();
- }
-
- @Override
- public boolean isEmpty() {
- return innermap.isEmpty();
- }
-
- @Override
- public boolean containsKey(K key) {
- return innermap.containsKey(key);
- }
-
- @Override
- public boolean containsValue(V value) {
- return innermap.containsValue(value);
- }
-
- @Override
- public boolean containsEntry(K key, V value) {
- return innermap.containsEntry(key, value);
- }
-
- @Override
- public boolean put(K key, V value) {
- return innermap.put(key, version(value));
- }
-
- @Override
- public Versioned<Collection<? extends V>> putAndGet(K key, V value) {
- innermap.put(key, version(value));
- return (Versioned<Collection<? extends V>>) innermap.get(key);
- }
-
- @Override
- public boolean remove(K key, V value) {
- return innermap.remove(key, value);
- }
-
- @Override
- public Versioned<Collection<? extends V>> removeAndGet(K key, V value) {
- innermap.remove(key, value);
- return (Versioned<Collection<? extends V>>) innermap.get(key);
- }
-
- @Override
- public boolean removeAll(K key, Collection<? extends V> values) {
- return false;
- }
-
- @Override
- public Versioned<Collection<? extends V>> removeAll(K key) {
- return null;
- }
-
- @Override
- public boolean removeAll(Map<K, Collection<? extends V>> mapping) {
- return false;
- }
-
- @Override
- public boolean putAll(K key, Collection<? extends V> values) {
- return false;
- }
-
- @Override
- public boolean putAll(Map<K, Collection<? extends V>> mapping) {
- return false;
- }
-
- @Override
- public Versioned<Collection<? extends V>> replaceValues(K key, Collection<V> values) {
- return null;
- }
-
- @Override
- public void clear() {
- innermap.clear();
- }
-
- @Override
- public Versioned<Collection<? extends V>> get(K key) {
- Collection<? extends V> values = innermap.get(key).stream()
- .map(v -> v.value())
- .collect(Collectors.toList());
- return versionCollection(values);
- }
-
- @Override
- public Set<K> keySet() {
- return innermap.keySet();
- }
-
- @Override
- public Multiset<K> keys() {
- return innermap.keys();
- }
-
- @Override
- public Multiset<V> values() {
- return null;
- }
-
- @Override
- public Collection<Map.Entry<K, V>> entries() {
- return null;
- }
-
- @Override
- public Iterator<Map.Entry<K, V>> iterator() {
- return new ConsistentMultimapIterator(innermap.entries().iterator());
- }
-
- @Override
- public Map<K, Collection<V>> asMap() {
- return null;
- }
-
- @Override
- public void addListener(MultimapEventListener<K, V> listener, Executor executor) {
- }
-
- @Override
- public void removeListener(MultimapEventListener<K, V> listener) {
- }
-
- @Override
- public String name() {
- return "mock multimap";
- }
-
- @Override
- public Type primitiveType() {
- return null;
- }
-
- private class ConsistentMultimapIterator implements Iterator<Map.Entry<K, V>> {
-
- private final Iterator<Map.Entry<K, Versioned<V>>> it;
-
- public ConsistentMultimapIterator(Iterator<Map.Entry<K, Versioned<V>>> it) {
- this.it = it;
- }
-
- @Override
- public boolean hasNext() {
- return it.hasNext();
- }
-
- @Override
- public Map.Entry<K, V> next() {
- Map.Entry<K, Versioned<V>> e = it.next();
- return new AbstractMap.SimpleEntry<>(e.getKey(), e.getValue().value());
- }
- }
-
- }
-
- public static TestConsistentMultimap.Builder builder() {
- return new TestConsistentMultimap.Builder();
- }
-
- public static class Builder<K, V> extends ConsistentMultimapBuilder<K, V> {
-
- @Override
- public AsyncConsistentMultimap<K, V> buildMultimap() {
- return null;
- }
-
- @Override
- public ConsistentMultimap<K, V> build() {
- return new TestConsistentMultimap<K, V>();
- }
- }
-}
diff --git a/pom.xml b/pom.xml
index a8fa27a..dd82c5d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2015-present Open Networking Foundation
+ ~ Copyright 2021 Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,34 +14,30 @@
~ 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"
+<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-dependencies</artifactId>
- <version>2.5.2</version>
- </parent>
-
- <groupId>org.opencord</groupId>
- <artifactId>olt</artifactId>
- <version>4.6.0-SNAPSHOT</version>
- <packaging>pom</packaging>
-
- <properties>
- <sadis.api.version>5.4.0</sadis.api.version>
- <olt.api.version>4.6.0-SNAPSHOT</olt.api.version>
- </properties>
-
<modules>
<module>api</module>
<module>impl</module>
<module>web</module>
<module>app</module>
</modules>
-
+ <parent>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-dependencies</artifactId>
+ <version>2.5.2</version>
+ </parent>
+ <groupId>org.opencord</groupId>
+ <artifactId>olt</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <description>OLT Application</description>
+ <url>http://onosproject.org</url>
+ <properties>
+ <sadis.api.version>5.5.0-SNAPSHOT</sadis.api.version>
+ <olt.api.version>5.0.0-SNAPSHOT</olt.api.version>
+ </properties>
<dependencies>
<dependency>
<groupId>org.onosproject</groupId>
@@ -56,9 +52,7 @@
<version>${onos.version}</version>
<scope>provided</scope>
</dependency>
-
</dependencies>
-
<build>
<plugins>
<plugin>
@@ -90,7 +84,6 @@
</plugin>
</plugins>
</build>
-
<repositories>
<repository>
<id>central</id>
diff --git a/spotbugs-security-exclude.xml b/spotbugs-security-exclude.xml
index 4aa0021..d16aa3c 100644
--- a/spotbugs-security-exclude.xml
+++ b/spotbugs-security-exclude.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright 2020-present Open Networking Foundation
+ ~ Copyright 2021-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -14,4 +14,7 @@
~ limitations under the License.
-->
<FindBugsFilter>
+ <Match>
+ <Bug pattern="CRLF_INJECTION_LOGS"/>
+ </Match>
</FindBugsFilter>
\ No newline at end of file
diff --git a/spotbugs-security-include.xml b/spotbugs-security-include.xml
index b3b408f..5341623 100644
--- a/spotbugs-security-include.xml
+++ b/spotbugs-security-include.xml
@@ -1,5 +1,5 @@
<!--
- ~ Copyright 2020-present Open Networking Foundation
+ ~ Copyright 2021-present Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
diff --git a/web/pom.xml b/web/pom.xml
index 7c64f96..6b95b8d 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- ~ Copyright 2016-present Open Networking Foundation
+ ~ Copyright 2021 Open Networking Foundation
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
@@ -18,12 +18,10 @@
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">
<parent>
- <groupId>org.opencord</groupId>
<artifactId>olt</artifactId>
- <version>4.6.0-SNAPSHOT</version>
- <relativePath>../pom.xml</relativePath>
+ <groupId>org.opencord</groupId>
+ <version>5.0.0-SNAPSHOT</version>
</parent>
-
<modelVersion>4.0.0</modelVersion>
<artifactId>olt-web</artifactId>
<packaging>bundle</packaging>
@@ -38,70 +36,19 @@
</api.description>
<api.package>org.opencord.olt.rest</api.package>
</properties>
-
<dependencies>
<dependency>
- <groupId>org.opencord</groupId>
- <artifactId>olt-api</artifactId>
- <version>${olt.api.version}</version>
- <scope>compile</scope>
- </dependency>
-
- <dependency>
- <groupId>org.opencord</groupId>
- <artifactId>olt-impl</artifactId>
- <version>${project.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.opencord</groupId>
- <artifactId>sadis-api</artifactId>
- <version>${sadis.api.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-cli</artifactId>
- <version>${onos.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-api</artifactId>
- <version>${onos.version}</version>
- <classifier>tests</classifier>
- <scope>test</scope>
- </dependency>
-
- <dependency>
- <groupId>org.onosproject</groupId>
- <artifactId>onos-core-serializers</artifactId>
- <version>${onos.version}</version>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.apache.karaf.shell</groupId>
- <artifactId>org.apache.karaf.shell.console</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
- <groupId>org.apache.karaf.shell</groupId>
- <artifactId>org.apache.karaf.shell.core</artifactId>
- <scope>provided</scope>
- </dependency>
-
- <dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.opencord</groupId>
+ <artifactId>olt-api</artifactId>
+ <version>5.0.0-SNAPSHOT</version>
+ <scope>compile</scope>
+ </dependency>
</dependencies>
-
<build>
<plugins>
<plugin>
@@ -123,10 +70,9 @@
*,org.glassfish.jersey.servlet
</Import-Package>
<Web-ContextPath>${web.context}</Web-ContextPath>
- <Karaf-Commands>org.opencord.olt.cli</Karaf-Commands>
</instructions>
</configuration>
</plugin>
</plugins>
</build>
-</project>
+</project>
\ No newline at end of file
diff --git a/web/src/main/java/org/opencord/olt/rest/OltWebResource.java b/web/src/main/java/org/opencord/olt/rest/OltWebResource.java
index 7b19b18..c512304 100644
--- a/web/src/main/java/org/opencord/olt/rest/OltWebResource.java
+++ b/web/src/main/java/org/opencord/olt/rest/OltWebResource.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-present Open Networking Foundation
+ * Copyright 2021-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,12 +21,11 @@
import org.onosproject.net.PortNumber;
import org.onosproject.rest.AbstractWebResource;
import org.opencord.olt.AccessDeviceService;
-import org.opencord.olt.AccessSubscriberId;
import org.slf4j.Logger;
-import java.util.Optional;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -34,6 +33,8 @@
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import java.util.Optional;
+
import static javax.ws.rs.core.Response.Status.INTERNAL_SERVER_ERROR;
import static org.slf4j.LoggerFactory.getLogger;
@@ -43,10 +44,15 @@
@Path("oltapp")
public class OltWebResource extends AbstractWebResource {
-
private final Logger log = getLogger(getClass());
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("status")
+ public Response status() {
+ return Response.ok().build();
+ }
/**
* Provision a subscriber.
*
@@ -64,6 +70,7 @@
DeviceId deviceId = DeviceId.deviceId(device);
PortNumber portNumber = PortNumber.portNumber(port);
ConnectPoint connectPoint = new ConnectPoint(deviceId, portNumber);
+
try {
service.provisionSubscriber(connectPoint);
} catch (Exception e) {
@@ -113,7 +120,39 @@
Optional<VlanId> emptyVlan = Optional.empty();
Optional<Integer> emptyTpId = Optional.empty();
- if (service.provisionSubscriber(new AccessSubscriberId(portName), emptyVlan, emptyVlan, emptyTpId)) {
+ // Check if we can find the connect point to which this subscriber is connected
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity("ConnectPoint not found for " + portName).build();
+ }
+ if (service.provisionSubscriber(cp)) {
+ return ok("").build();
+ }
+ return Response.noContent().build();
+ }
+
+ /**
+ * Removes services for a subscriber.
+ *
+ * @param portName Name of the port on which the subscriber is connected
+ * @return 200 OK or 204 NO CONTENT
+ */
+ @DELETE
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("services/{portName}")
+ public Response deleteServices(
+ @PathParam("portName") String portName) {
+ AccessDeviceService service = get(AccessDeviceService.class);
+
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity("ConnectPoint not found for " + portName).build();
+ }
+ if (service.removeSubscriber(cp)) {
return ok("").build();
}
return Response.noContent().build();
@@ -141,30 +180,16 @@
VlanId cTag = VlanId.vlanId(cTagVal);
VlanId sTag = VlanId.vlanId(sTagVal);
Integer tpId = Integer.valueOf(tpIdVal);
-
- if (service.provisionSubscriber(new AccessSubscriberId(portName), Optional.of(sTag),
- Optional.of(cTag), Optional.of(tpId))) {
- return ok("").build();
+ // TODO this is not optimal, because we call device service 2 times here and
+ // 2 times in the provisionSubscriber call, optimize byu having 2 more methods
+ // in the OltService that allow provisioning with portName.
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity("ConnectPoint not found for " + portName).build();
}
- return Response.noContent().build();
- }
-
- /**
- * Removes services for a subscriber.
- *
- * @param portName Name of the port on which the subscriber is connected
- * @return 200 OK or 204 NO CONTENT
- */
- @DELETE
- @Produces(MediaType.APPLICATION_JSON)
- @Path("services/{portName}")
- public Response deleteServices(
- @PathParam("portName") String portName) {
- AccessDeviceService service = get(AccessDeviceService.class);
-
- Optional<VlanId> emptyVlan = Optional.empty();
- Optional<Integer> emptyTpId = Optional.empty();
- if (service.removeSubscriber(new AccessSubscriberId(portName), emptyVlan, emptyVlan, emptyTpId)) {
+ if (service.provisionSubscriber(cp, cTag, sTag, tpId)) {
return ok("").build();
}
return Response.noContent().build();
@@ -192,12 +217,18 @@
VlanId cTag = VlanId.vlanId(cTagVal);
VlanId sTag = VlanId.vlanId(sTagVal);
Integer tpId = Integer.valueOf(tpIdVal);
-
- if (service.removeSubscriber(new AccessSubscriberId(portName), Optional.of(sTag),
- Optional.of(cTag), Optional.of(tpId))) {
+ // TODO this is not optimal, because we call device service 2 times here and
+ // 2 times in the provisionSubscriber call, optimize byu having 2 more methods
+ // in the OltService that allow provisioning with portName.
+ ConnectPoint cp = service.findSubscriberConnectPoint(portName);
+ if (cp == null) {
+ log.warn("ConnectPoint not found for {}", portName);
+ return Response.status(INTERNAL_SERVER_ERROR)
+ .entity("ConnectPoint not found for " + portName).build();
+ }
+ if (service.removeSubscriber(cp, cTag, sTag, tpId)) {
return ok("").build();
}
return Response.noContent().build();
}
-
}