Merge "Adding release target for bbsimctl and bbr"
diff --git a/README.md b/README.md
index b6dee44..4566a7d 100644
--- a/README.md
+++ b/README.md
@@ -144,13 +144,4 @@
 
 More advanced documentation lives in the [here](./docs/README.md)
 
-## Know Issues
-
-In some runs, EAPOL fails with:
-```
-time="2019-09-20T21:24:31Z" level=error msg="Can't send EapStart Message: ONU {intfid:1, onuid:2} - Not DONE (GemportID is not set)" IntfId=1 OnuId=2 OnuSn=BBSM00000102 module=EAPOL
-time="2019-09-20T21:24:31Z" level=error msg="ONU failed to authenticate!" IntfId=1 OnuId=2 OnuSn=BBSM00000102 module=ONU
-```
-Investigate why this happens (we believe the source to be in the OMCI library)
-
 > This project structure is based on [golang-standards/project-layout](https://github.com/golang-standards/project-layout).
diff --git a/docs/assets/bbr_runs.png b/docs/assets/bbr_runs.png
new file mode 100644
index 0000000..f436d44
--- /dev/null
+++ b/docs/assets/bbr_runs.png
Binary files differ
diff --git a/docs/bbr.md b/docs/bbr.md
index fafdf77..359d106 100644
--- a/docs/bbr.md
+++ b/docs/bbr.md
@@ -1,5 +1,17 @@
 # BBR
 
+BBR (a.k.a BBSim Reflector) is a tool designed to scale test BBSim.
+It is responsible to emulate ONOS and VOLTHA in order to quickly reply
+to any message that BBSim sends.
+
+Here is a graph of the measuraments of BBSim performance captured over
+10 runs with different PON Layout
+
+![BBSim Performances](./assets/bbr_runs.png "BBSim Performances") 
+
+
+## Run BBR
+
 To run `bbr` you need to have a `bbsim` instance running.
 
 You can start `bbsim` locally with:
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 27f4e78..bfaa5fd 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -25,6 +25,7 @@
 	"github.com/looplab/fsm"
 	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
 	bbsim "github.com/opencord/bbsim/internal/bbsim/types"
+	omcisim "github.com/opencord/omci-sim"
 	"github.com/opencord/voltha-protos/go/openolt"
 	"github.com/opencord/voltha-protos/go/tech_profile"
 	log "github.com/sirupsen/logrus"
@@ -212,7 +213,7 @@
 		}
 		o.channel <- msg
 	}
-
+	go o.processOmciMessages()
 	// send PON Port indications
 	for _, pon := range o.Pons {
 		msg := Message{
@@ -226,7 +227,6 @@
 
 		for _, onu := range pon.Onus {
 			go onu.ProcessOnuMessages(stream, nil)
-			go onu.processOmciMessages(stream)
 			// FIXME move the message generation in the state transition
 			// from here only invoke the state transition
 			msg := Message{
@@ -244,6 +244,22 @@
 	return nil
 }
 
+func (o OltDevice) processOmciMessages() {
+	ch := omcisim.GetChannel()
+
+	oltLogger.Debug("Started OMCI Indication Channel")
+
+	for message := range ch {
+		onuId := message.Data.OnuId
+		intfId := message.Data.IntfId
+		onu, err := o.FindOnuById(intfId, onuId)
+		if err != nil {
+			oltLogger.Errorf("Failed to find onu: %v", err)
+		}
+		go onu.processOmciMessage(message)
+	}
+}
+
 // Helpers method
 
 func (o OltDevice) GetPonById(id uint32) (*PonPort, error) {
@@ -433,6 +449,22 @@
 	return &Onu{}, errors.New(fmt.Sprintf("cannot-find-onu-by-serial-number-%s", serialNumber))
 }
 
+// returns an ONU with a given interface/Onu Id
+func (o OltDevice) FindOnuById(intfId uint32, onuId uint32) (*Onu, error) {
+	// TODO this function can be a performance bottlenec when we have many ONUs,
+	// memoizing it will remove the bottleneck
+	for _, pon := range o.Pons {
+		if pon.ID == intfId {
+			for _, onu := range pon.Onus {
+				if onu.ID == onuId {
+					return onu, nil
+				}
+			}
+		}
+	}
+	return &Onu{}, errors.New(fmt.Sprintf("cannot-find-onu-by-id-%v-%v", intfId, onuId))
+}
+
 // returns an ONU with a given Mac Address
 func (o OltDevice) FindOnuByMacAddress(mac net.HardwareAddr) (*Onu, error) {
 	// TODO this function can be a perfoormance bottlenec when we have many ONUs,
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index f4ef84b..2aba5d2 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -294,32 +294,23 @@
 	}
 }
 
-func (o Onu) processOmciMessages(stream openolt.Openolt_EnableIndicationServer) {
-	ch := omcisim.GetChannel()
+func (o Onu) processOmciMessage(message omcisim.OmciChMessage) {
+	switch message.Type {
+	case omcisim.GemPortAdded:
+		log.WithFields(log.Fields{
+			"OnuId":  message.Data.OnuId,
+			"IntfId": message.Data.IntfId,
+		}).Infof("GemPort Added")
 
-	onuLogger.WithFields(log.Fields{
-		"onuID": o.ID,
-		"onuSN": o.Sn(),
-	}).Debug("Started OMCI Indication Channel")
-
-	for message := range ch {
-		switch message.Type {
-		case omcisim.GemPortAdded:
-			log.WithFields(log.Fields{
-				"OnuId":  message.Data.OnuId,
-				"IntfId": message.Data.IntfId,
-			}).Infof("GemPort Added")
-
-			// NOTE if we receive the GemPort but we don't have EAPOL flows
-			// go an intermediate state, otherwise start auth
-			if o.InternalState.Is("enabled") {
-				if err := o.InternalState.Event("add_gem_port"); err != nil {
-					log.Errorf("Can't go to gem_port_added: %v", err)
-				}
-			} else if o.InternalState.Is("eapol_flow_received") {
-				if err := o.InternalState.Event("start_auth"); err != nil {
-					log.Errorf("Can't go to auth_started: %v", err)
-				}
+		// NOTE if we receive the GemPort but we don't have EAPOL flows
+		// go an intermediate state, otherwise start auth
+		if o.InternalState.Is("enabled") {
+			if err := o.InternalState.Event("add_gem_port"); err != nil {
+				log.Errorf("Can't go to gem_port_added: %v", err)
+			}
+		} else if o.InternalState.Is("eapol_flow_received") {
+			if err := o.InternalState.Event("start_auth"); err != nil {
+				log.Errorf("Can't go to auth_started: %v", err)
 			}
 		}
 	}
diff --git a/tests/bbr.sh b/tests/bbr.sh
new file mode 100644
index 0000000..434cb56
--- /dev/null
+++ b/tests/bbr.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+# Copyright 2018-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.
+
+usage()
+{
+    echo " "
+    echo "Runs BBR against BBSim a number of times and output the results in a file name results.logs"
+    echo " "
+    echo "Usage: $0 [--onus|--pons|--runs]" >&2
+    echo " "
+    echo "   -o, --onus              Number of ONUs to emulate on each PON"
+    echo "   -p, --pons              Number of PONs to emulate"
+    echo "   -r, --runs              Number of runs to perform"
+    echo " "
+    echo "Example usages:"
+    echo "   ./$0 -i -p 2 -o 32 -r 10"
+    echo " "
+}
+
+run()
+{
+    echo "Running with: ${ONU} Onus ${PON} Pons for ${RUN} times" >> results.logs
+    for i in {0..10}
+    do
+        echo "RUN Number: $i"
+        docker rm -f bbsim
+        DOCKER_RUN_ARGS="-pon ${PON} -onu ${ONU}" make docker-run
+        sleep 5
+        ./bbr -pon $PON -onu $ONU 2>&1 | tee bbr.logs
+        docker logs bbsim 2>&1 | tee bbsim.logs
+        echo "RUN Number: $i" >> results.logs
+        cat bbr.logs | grep Duration | awk '{print $5}' >> results.logs
+    done
+}
+
+ONU=1
+PON=1
+RUN=10
+
+while [ "$1" != "" ]; do
+    case $1 in
+        -h | --help)
+                                usage
+                                exit 0
+                                ;;
+        -o | --onus )           ONU=$2
+                                ;;
+        -p | --pons )           PON=$2
+                                ;;
+        -r | --runs )           RUN=$2
+                                ;;
+        --) # End of all options
+                                shift
+                                break
+                                ;;
+    esac
+    shift
+done
+
+run
+