Merge "[VOL-2351] Sending the same indications as a real OLT on disable"
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index 6e5e34d..0d658e9 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -153,12 +153,12 @@
 }
 
 // NewVethChan returns a new channel for receiving packets over the NNI interface
-func (n *NniPort) NewVethChan() (chan *types.PacketMsg, error) {
-	ch, err := listenOnVeth(n.nniVeth)
+func (n *NniPort) NewVethChan() (chan *types.PacketMsg, *pcap.Handle, error) {
+	ch, handle, err := listenOnVeth(n.nniVeth)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
-	return ch, err
+	return ch, handle, err
 }
 
 // setVethUp is responsible to activate a virtual interface
@@ -211,16 +211,17 @@
 	return handle, nil
 }
 
-var listenOnVeth = func(vethName string) (chan *types.PacketMsg, error) {
+var listenOnVeth = func(vethName string) (chan *types.PacketMsg, *pcap.Handle, error) {
 
 	handle, err := getVethHandler(vethName)
 	if err != nil {
-		return nil, err
+		return nil, nil, err
 	}
 
 	channel := make(chan *types.PacketMsg, 1024)
 
 	go func() {
+		nniLogger.Info("Start listening on NNI for packets")
 		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
 		for packet := range packetSource.Packets() {
 
@@ -237,7 +238,8 @@
 			}
 			channel <- &pkt
 		}
+		nniLogger.Info("Stop listening on NNI for packets")
 	}()
 
-	return channel, nil
+	return channel, handle, nil
 }
diff --git a/internal/bbsim/devices/nni_test.go b/internal/bbsim/devices/nni_test.go
index 348d40f..9ee945e 100644
--- a/internal/bbsim/devices/nni_test.go
+++ b/internal/bbsim/devices/nni_test.go
@@ -19,6 +19,7 @@
 
 import (
 	"errors"
+	"github.com/google/gopacket/pcap"
 	"testing"
 
 	"github.com/opencord/bbsim/internal/bbsim/types"
@@ -58,9 +59,9 @@
 	listenOnVethCalled := false
 	_listenOnVeth := listenOnVeth
 	defer func() { listenOnVeth = _listenOnVeth }()
-	listenOnVeth = func(vethName string) (chan *types.PacketMsg, error) {
+	listenOnVeth = func(vethName string) (chan *types.PacketMsg, *pcap.Handle, error) {
 		listenOnVethCalled = true
-		return make(chan *types.PacketMsg, 1), nil
+		return make(chan *types.PacketMsg, 1), nil, nil
 	}
 	spy := &ExecutorSpy{
 		failRun: false,
@@ -71,7 +72,7 @@
 	nni := NniPort{}
 
 	err := createNNIPair(spy, &olt, &nni)
-	olt.nniPktInChannel, _ = nni.NewVethChan()
+	olt.nniPktInChannel, olt.nniHandle, _ = nni.NewVethChan()
 
 	assert.Equal(t, spy.CommandCallCount, 3)
 	assert.Equal(t, startDHCPServerCalled, true)
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index cc4965c..1ccb5be 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -20,6 +20,7 @@
 	"context"
 	"errors"
 	"fmt"
+	"github.com/google/gopacket/pcap"
 	"net"
 	"sync"
 	"time"
@@ -54,6 +55,7 @@
 	InternalState   *fsm.FSM
 	channel         chan Message
 	nniPktInChannel chan *bbsim.PacketMsg // packets coming in from the NNI and going to VOLTHA
+	nniHandle       *pcap.Handle          // handle on the NNI interface, close it when shutting down the NNI channel
 
 	Delay int
 
@@ -175,18 +177,28 @@
 	o.nniPktInChannel = make(chan *bbsim.PacketMsg, 1024)
 	// FIXME we are assuming we have only one NNI
 	if o.Nnis[0] != nil {
-		ch, err := o.Nnis[0].NewVethChan()
+		// NOTE we want to make sure the state is down when we initialize the OLT,
+		// the NNI may be in a bad state after a disable/reboot as we are not disabling it for
+		// in-band management
+		o.Nnis[0].OperState.SetState("down")
+		ch, handle, err := o.Nnis[0].NewVethChan()
 		if err == nil {
+			oltLogger.WithFields(log.Fields{
+				"Type":      o.Nnis[0].Type,
+				"IntfId":    o.Nnis[0].ID,
+				"OperState": o.Nnis[0].OperState.Current(),
+			}).Info("NNI Channel created")
 			o.nniPktInChannel = ch
+			o.nniHandle = handle
 		} else {
-			log.Errorf("Error getting NNI channel: %v", err)
+			oltLogger.Errorf("Error getting NNI channel: %v", err)
 		}
 	}
 
 	for i := range olt.Pons {
 		for _, onu := range olt.Pons[i].Onus {
 			if err := onu.InternalState.Event("initialize"); err != nil {
-				log.Errorf("Error initializing ONU: %v", err)
+				oltLogger.Errorf("Error initializing ONU: %v", err)
 				return err
 			}
 		}
@@ -220,8 +232,16 @@
 	// terminate the OLT's processOltMessages go routine
 	close(o.channel)
 	// terminate the OLT's processNniPacketIns go routine
+	o.nniHandle.Close()
 	close(o.nniPktInChannel)
 
+	for i := range olt.Pons {
+		for _, onu := range olt.Pons[i].Onus {
+			// NOTE while the olt is off, restore the ONU to the initial state
+			onu.InternalState.SetState("created")
+		}
+	}
+
 	time.Sleep(time.Duration(rebootDelay) * time.Second)
 
 	if err := o.InternalState.Event("initialize"); err != nil {
@@ -408,7 +428,23 @@
 
 func (o *OltDevice) sendNniIndication(msg NniIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	nni, _ := o.getNniById(msg.NniPortID)
-	nni.OperState.Event("enable")
+	if msg.OperState == UP {
+		if err := nni.OperState.Event("enable"); err != nil {
+			log.WithFields(log.Fields{
+				"Type":      nni.Type,
+				"IntfId":    nni.ID,
+				"OperState": nni.OperState.Current(),
+			}).Errorf("Can't move NNI Port to enabled state: %v", err)
+		}
+	} else if msg.OperState == DOWN {
+		if err := nni.OperState.Event("disable"); err != nil {
+			log.WithFields(log.Fields{
+				"Type":      nni.Type,
+				"IntfId":    nni.ID,
+				"OperState": nni.OperState.Current(),
+			}).Errorf("Can't move NNI Port to disable state: %v", err)
+		}
+	}
 	// NOTE Operstate may need to be an integer
 	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
 		Type:      nni.Type,
@@ -430,7 +466,23 @@
 
 func (o *OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	pon, _ := o.GetPonById(msg.PonPortID)
-	pon.OperState.Event("enable")
+	if msg.OperState == UP {
+		if err := pon.OperState.Event("enable"); err != nil {
+			log.WithFields(log.Fields{
+				"Type":      pon.Type,
+				"IntfId":    pon.ID,
+				"OperState": pon.OperState.Current(),
+			}).Errorf("Can't move PON Port to enable state: %v", err)
+		}
+	} else if msg.OperState == DOWN {
+		if err := pon.OperState.Event("disable"); err != nil {
+			log.WithFields(log.Fields{
+				"Type":      pon.Type,
+				"IntfId":    pon.ID,
+				"OperState": pon.OperState.Current(),
+			}).Errorf("Can't move PON Port to disable state: %v", err)
+		}
+	}
 	discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
 		IntfId:    pon.ID,
 		OperState: pon.OperState.Current(),
@@ -516,7 +568,7 @@
 func (o *OltDevice) processNniPacketIns(ctx context.Context, stream openolt.Openolt_EnableIndicationServer, wg *sync.WaitGroup) {
 	oltLogger.WithFields(log.Fields{
 		"nniChannel": o.nniPktInChannel,
-	}).Debug("Started NNI Channel")
+	}).Debug("Started Processing Packets arriving from the NNI")
 	nniId := o.Nnis[0].ID // FIXME we are assuming we have only one NNI
 
 	ch := o.nniPktInChannel
@@ -679,18 +731,7 @@
 		"oltId": o.ID,
 	}).Info("Disabling OLT")
 
-	for i, pon := range o.Pons {
-		// disable all onus
-		for _, onu := range o.Pons[i].Onus {
-			// NOTE order of these is important.
-			if err := onu.OperState.Event("disable"); err != nil {
-				log.Errorf("Error disabling ONU oper state: %v", err)
-			}
-			if err := onu.InternalState.Event("disable"); err != nil {
-				log.Errorf("Error disabling ONU: %v", err)
-			}
-		}
-
+	for _, pon := range o.Pons {
 		// disable PONs
 		msg := Message{
 			Type: PonIndication,
@@ -703,17 +744,8 @@
 		o.channel <- msg
 	}
 
-	// disable NNI
-	for _, nni := range o.Nnis {
-		msg := Message{
-			Type: NniIndication,
-			Data: NniIndicationMessage{
-				OperState: DOWN,
-				NniPortID: nni.ID,
-			},
-		}
-		o.channel <- msg
-	}
+	// Note that we are not disabling the NNI as the real OLT does not.
+	// The reason for that is in-band management
 
 	// disable OLT
 	oltMsg := Message{