SEBA-927 implemenation of controlled PON and ONU activation
updated controlledActivation to enum

Change-Id: I63ba732338f3df79f01a30703d9e6118d95bf8aa
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 016f148..f68ee9e 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -47,17 +47,17 @@
 	sync.Mutex
 
 	// BBSIM Internals
-	ID              int
-	SerialNumber    string
-	NumNni          int
-	NumPon          int
-	NumOnuPerPon    int
-	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
+	ID                   int
+	SerialNumber         string
+	NumNni               int
+	NumPon               int
+	NumOnuPerPon         int
+	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
+	ControlledActivation mode
 
 	Pons []*PonPort
 	Nnis []*NniPort
@@ -78,7 +78,7 @@
 	return &olt
 }
 
-func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, auth bool, dhcp bool, delay int, isMock bool) *OltDevice {
+func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, auth bool, dhcp bool, delay int, ca string, isMock bool) *OltDevice {
 	oltLogger.WithFields(log.Fields{
 		"ID":           oltId,
 		"NumNni":       nni,
@@ -100,6 +100,13 @@
 		Delay:        delay,
 	}
 
+	if val, ok := ControlledActivationModes[ca]; ok {
+		olt.ControlledActivation = val
+	} else {
+		oltLogger.Warn("Unknown ControlledActivation Mode given, running in Default mode")
+		olt.ControlledActivation = Default
+	}
+
 	// OLT State machine
 	// NOTE do we need 2 state machines for the OLT? (InternalState and OperState)
 	olt.InternalState = fsm.NewFSM(
@@ -132,7 +139,7 @@
 	// create PON ports
 	availableCTag := cTagInit
 	for i := 0; i < pon; i++ {
-		p := CreatePonPort(olt, uint32(i))
+		p := CreatePonPort(&olt, uint32(i))
 
 		// create ONU devices
 		for j := 0; j < onuPerPon; j++ {
@@ -209,22 +216,33 @@
 	// TODO handle hard poweroff (i.e. no indications sent to Voltha) vs soft poweroff
 	time.Sleep(1 * time.Second) // we need to give the OLT the time to respond to all the pending gRPC request before stopping the server
 	if err := o.StopOltServer(); err != nil {
+		oltLogger.Errorf("Error in stopping OLT server")
 		return err
 	}
 
+	for _, pon := range olt.Pons {
+		msg := Message{
+			Type: PonIndication,
+			Data: PonIndicationMessage{
+				OperState: DOWN,
+				PonPortID: pon.ID,
+			},
+		}
+		o.channel <- msg
+
+		for _, onu := range pon.Onus {
+			if onu.InternalState.Current() != "initialized" {
+				onu.InternalState.Event("disable")
+			}
+		}
+	}
+
 	// terminate the OLT's processOltMessages go routine
 	close(o.channel)
 	// terminate the OLT's processNniPacketIns go routine
 	go 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 {
@@ -277,6 +295,7 @@
 // Enable implements the OpenOLT EnableIndicationServer functionality
 func (o *OltDevice) Enable(stream openolt.Openolt_EnableIndicationServer) error {
 	oltLogger.Debug("Enable OLT called")
+	rebootFlag := false
 
 	// If enabled has already been called then an enabled context has
 	// been created. If this is the case then we want to cancel all the
@@ -285,6 +304,7 @@
 	o.Lock()
 	if o.enableContext != nil && o.enableContextCancel != nil {
 		o.enableContextCancel()
+		rebootFlag = true
 	}
 	o.enableContext, o.enableContextCancel = context.WithCancel(context.TODO())
 	o.Unlock()
@@ -321,25 +341,35 @@
 
 	go o.processOmciMessages(o.enableContext, stream, &wg)
 
-	// send PON Port indications
-	for i, pon := range o.Pons {
-		msg := Message{
-			Type: PonIndication,
-			Data: PonIndicationMessage{
-				OperState: UP,
-				PonPortID: pon.ID,
-			},
-		}
-		o.channel <- msg
-
-		for _, onu := range o.Pons[i].Onus {
-			if err := onu.InternalState.Event("initialize"); err != nil {
-				log.Errorf("Error initializing ONU: %v", err)
-				continue
+	if rebootFlag == true {
+		for _, pon := range o.Pons {
+			if pon.InternalState.Current() == "disabled" {
+				msg := Message{
+					Type: PonIndication,
+					Data: PonIndicationMessage{
+						OperState: UP,
+						PonPortID: pon.ID,
+					},
+				}
+				o.channel <- msg
 			}
-			if err := onu.InternalState.Event("discover"); err != nil {
-				log.Errorf("Error discover ONU: %v", err)
-				return err
+		}
+	} else {
+
+		// 1. controlledActivation == Default: Send both PON and ONUs indications
+		// 2. controlledActivation == only-onu: that means only ONUs will be controlled activated, so auto send PON indications
+
+		if o.ControlledActivation == Default || o.ControlledActivation == OnlyONU {
+			// send PON Port indications
+			for _, pon := range o.Pons {
+				msg := Message{
+					Type: PonIndication,
+					Data: PonIndicationMessage{
+						OperState: UP,
+						PonPortID: pon.ID,
+					},
+				}
+				o.channel <- msg
 			}
 		}
 	}
@@ -468,25 +498,11 @@
 	}).Debug("Sent Indication_IntfOperInd for NNI")
 }
 
-func (o *OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
-	pon, _ := o.GetPonById(msg.PonPortID)
-	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)
-		}
-	}
+func (o *OltDevice) sendPonIndication(ponPortID uint32) {
+
+	stream := *o.OpenoltStream
+	pon, _ := o.GetPonById(ponPortID)
+	// Send IntfIndication for PON port
 	discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
 		IntfId:    pon.ID,
 		OperState: pon.OperState.Current(),
@@ -502,6 +518,7 @@
 		"OperState": pon.OperState.Current(),
 	}).Debug("Sent Indication_IntfInd")
 
+	// Send IntfOperIndication for PON port
 	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
 		Type:      pon.Type,
 		IntfId:    pon.ID,
@@ -561,7 +578,14 @@
 				o.sendNniIndication(msg, stream)
 			case PonIndication:
 				msg, _ := message.Data.(PonIndicationMessage)
-				o.sendPonIndication(msg, stream)
+				pon, _ := o.GetPonById(msg.PonPortID)
+				if msg.OperState == UP {
+					pon.OperState.Event("enable")
+					pon.InternalState.Event("enable")
+				} else if msg.OperState == DOWN {
+					pon.OperState.Event("disable")
+					pon.InternalState.Event("disable")
+				}
 			default:
 				oltLogger.Warnf("Received unknown message data %v for type %v in OLT Channel", message.Data, message.Type)
 			}
@@ -645,26 +669,6 @@
 	}).Warn("Stopped handling NNI Channel")
 }
 
-func (o *OltDevice) handleReenableOlt() {
-	// enable OLT
-	oltMsg := Message{
-		Type: OltIndication,
-		Data: OltIndicationMessage{
-			OperState: UP,
-		},
-	}
-	o.channel <- oltMsg
-
-	for i := range olt.Pons {
-		for _, onu := range olt.Pons[i].Onus {
-			if err := onu.InternalState.Event("discover"); err != nil {
-				log.Errorf("Error discover ONU: %v", err)
-			}
-		}
-	}
-
-}
-
 // returns an ONU with a given Serial Number
 func (o OltDevice) FindOnuBySn(serialNumber string) (*Onu, error) {
 	// TODO this function can be a performance bottleneck when we have many ONUs,
@@ -788,16 +792,17 @@
 	}).Info("Disabling OLT")
 
 	for _, pon := range o.Pons {
-		// disable PONs
-		msg := Message{
-			Type: PonIndication,
-			Data: PonIndicationMessage{
-				OperState: DOWN,
-				PonPortID: pon.ID,
-			},
+		if pon.InternalState.Current() == "enabled" {
+			// disable PONs
+			msg := Message{
+				Type: PonIndication,
+				Data: PonIndicationMessage{
+					OperState: DOWN,
+					PonPortID: pon.ID,
+				},
+			}
+			o.channel <- msg
 		}
-
-		o.channel <- msg
 	}
 
 	// Note that we are not disabling the NNI as the real OLT does not.
@@ -825,8 +830,18 @@
 	return nil
 }
 
-func (o OltDevice) EnablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error) {
-	oltLogger.Error("EnablePonIf not implemented")
+func (o OltDevice) EnablePonIf(_ context.Context, intf *openolt.Interface) (*openolt.Empty, error) {
+	oltLogger.Errorf("EnablePonIf request received for PON %d", intf.IntfId)
+	ponID := intf.GetIntfId()
+	msg := Message{
+		Type: PonIndication,
+		Data: PonIndicationMessage{
+			OperState: UP,
+			PonPortID: ponID,
+		},
+	}
+	o.channel <- msg
+
 	return new(openolt.Empty), nil
 }
 
@@ -995,20 +1010,27 @@
 		"oltId": o.ID,
 	}).Info("Received ReenableOlt request from VOLTHA")
 
-	for _, pon := range o.Pons {
-		msg := Message{
-			Type: PonIndication,
-			Data: PonIndicationMessage{
-				OperState: UP,
-				PonPortID: pon.ID,
-			},
-		}
-		o.channel <- msg
+	// enable OLT
+	oltMsg := Message{
+		Type: OltIndication,
+		Data: OltIndicationMessage{
+			OperState: UP,
+		},
 	}
+	o.channel <- oltMsg
 
-	// Openolt adapter will start processing indications only after success reponse of ReenableOlt
-	// thats why need to send OLT and ONU indications after return of this function
-	go o.handleReenableOlt()
+	for _, pon := range o.Pons {
+		if pon.InternalState.Current() == "disabled" {
+			msg := Message{
+				Type: PonIndication,
+				Data: PonIndicationMessage{
+					OperState: UP,
+					PonPortID: pon.ID,
+				},
+			}
+			o.channel <- msg
+		}
+	}
 
 	return new(openolt.Empty), nil
 }