VOL-3049 patch for alarm notification, audit and sync

Change-Id: I18fa616e94e7c33bbd8c1080bee0ab16afe505da
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index ab7c35f..6c1b5b9 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -22,6 +22,8 @@
 	"fmt"
 	pb "github.com/opencord/bbsim/api/bbsim"
 	"github.com/opencord/bbsim/internal/bbsim/alarmsim"
+	"sync"
+
 	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
 	"github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
 	"github.com/opencord/bbsim/internal/bbsim/responders/eapol"
@@ -124,6 +126,8 @@
 
 	DoneChannel       chan bool // this channel is used to signal once the onu is complete (when the struct is used by BBR)
 	TrafficSchedulers *tech_profile.TrafficSchedulers
+	onuAlarmsInfoLock sync.RWMutex
+	onuAlarmsInfo     map[omcilib.OnuAlarmInfoMapKey]omcilib.OnuAlarmInfo
 }
 
 func (o *Onu) Sn() string {
@@ -158,7 +162,7 @@
 			"ID": o.ID,
 		}).Debugf("Changing ONU OperState from %s to %s", e.Src, e.Dst)
 	})
-
+	o.onuAlarmsInfo = make(map[omcilib.OnuAlarmInfoMapKey]omcilib.OnuAlarmInfo)
 	// NOTE this state machine is used to activate the OMCI, EAPOL and DHCP clients
 	o.InternalState = fsm.NewFSM(
 		OnuStateCreated,
@@ -258,7 +262,6 @@
 						"OnuSn":  o.Sn(),
 					}).Errorf("Cannot change ONU OperState to down: %s", err.Error())
 				}
-
 				// send the OnuIndication DOWN event
 				msg := bbsim.Message{
 					Type: bbsim.OnuIndication,
@@ -279,6 +282,9 @@
 				for _, s := range o.Services {
 					s.Disable()
 				}
+				o.onuAlarmsInfoLock.Lock()
+				o.onuAlarmsInfo = make(map[omcilib.OnuAlarmInfoMapKey]omcilib.OnuAlarmInfo) //Basically reset everything on onu disable
+				o.onuAlarmsInfoLock.Unlock()
 			},
 			// BBR states
 			"enter_eapol_flow_sent": func(e *fsm.Event) {
@@ -353,23 +359,40 @@
 				o.handleOmciRequest(msg, stream)
 			case bbsim.UniStatusAlarm:
 				msg, _ := message.Data.(bbsim.UniStatusAlarmMessage)
-				pkt := omcilib.CreateUniStatusAlarm(msg.AdminState, msg.EntityID)
-				if err := o.sendOmciIndication(pkt, 0, stream); err != nil {
+				onuAlarmMapKey := omcilib.OnuAlarmInfoMapKey{
+					MeInstance: msg.EntityID,
+					MeClassID:  me.PhysicalPathTerminationPointEthernetUniClassID,
+				}
+				seqNo := o.IncrementAlarmSequenceNumber(onuAlarmMapKey)
+				o.onuAlarmsInfoLock.Lock()
+				var alarmInfo = o.onuAlarmsInfo[onuAlarmMapKey]
+				pkt, alarmBitMap := omcilib.CreateUniStatusAlarm(msg.RaiseOMCIAlarm, msg.EntityID, seqNo)
+				if pkt != nil { //pkt will be nil if we are unable to create the alarm
+					if err := o.sendOmciIndication(pkt, 0, stream); err != nil {
+						onuLogger.WithFields(log.Fields{
+							"IntfId":       o.PonPortID,
+							"SerialNumber": o.Sn(),
+							"omciPacket":   pkt,
+							"adminState":   msg.AdminState,
+							"entityID":     msg.EntityID,
+						}).Errorf("failed-to-send-UNI-Link-Alarm: %v", err)
+						alarmInfo.SequenceNo--
+					}
 					onuLogger.WithFields(log.Fields{
 						"IntfId":       o.PonPortID,
 						"SerialNumber": o.Sn(),
 						"omciPacket":   pkt,
 						"adminState":   msg.AdminState,
 						"entityID":     msg.EntityID,
-					}).Errorf("failed-to-send-UNI-Link-Alarm: %v", err)
+					}).Trace("UNI-Link-alarm-sent")
+					if alarmBitMap == [28]byte{0} {
+						delete(o.onuAlarmsInfo, onuAlarmMapKey)
+					} else {
+						alarmInfo.AlarmBitMap = alarmBitMap
+						o.onuAlarmsInfo[onuAlarmMapKey] = alarmInfo
+					}
 				}
-				onuLogger.WithFields(log.Fields{
-					"IntfId":       o.PonPortID,
-					"SerialNumber": o.Sn(),
-					"omciPacket":   pkt,
-					"adminState":   msg.AdminState,
-					"entityID":     msg.EntityID,
-				}).Trace("UNI-Link-alarm-sent")
+				o.onuAlarmsInfoLock.Unlock()
 			case bbsim.FlowAdd:
 				msg, _ := message.Data.(bbsim.OnuFlowUpdateMessage)
 				o.handleFlowAdd(msg)
@@ -450,13 +473,9 @@
 }
 
 func NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
-
 	sn := new(openolt.SerialNumber)
-
-	//sn = new(openolt.SerialNumber)
 	sn.VendorId = []byte("BBSM")
 	sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
-
 	return sn
 }
 
@@ -546,7 +565,7 @@
 
 		return err
 	}
-
+	o.SendOMCIAlarmNotificationMsg(true, losReq.AlarmType)
 	// TODO if it's the last ONU on the PON, then send a PON LOS
 
 	if err := o.InternalState.Event(OnuTxDisable); err != nil {
@@ -591,6 +610,7 @@
 		}).Errorf("Cannot send LOS: %s", err.Error())
 		return err
 	}
+	o.SendOMCIAlarmNotificationMsg(false, losReq.AlarmType)
 
 	// Send a ONU Discovery indication
 	if err := o.InternalState.Event(OnuTxDiscover); err != nil {
@@ -630,6 +650,11 @@
 	if err != nil {
 		return err
 	}
+	raiseAlarm := false
+	if alarmReq.Status == "on" {
+		raiseAlarm = true
+	}
+	o.SendOMCIAlarmNotificationMsg(raiseAlarm, alarmReq.AlarmType)
 	return nil
 }
 
@@ -730,13 +755,18 @@
 				// omci/mibpackets.go where the PhysicalPathTerminationPointEthernetUni
 				// are reported during the MIB Upload sequence
 				adminState := msgObj.Attributes["AdministrativeState"].(uint8)
+				raiseOMCIAlarm := false
+				if adminState == 1 {
+					raiseOMCIAlarm = true
+				}
 				msg := bbsim.Message{
 					Type: bbsim.UniStatusAlarm,
 					Data: bbsim.UniStatusAlarmMessage{
-						OnuSN:      o.SerialNumber,
-						OnuID:      o.ID,
-						AdminState: adminState,
-						EntityID:   msgObj.EntityInstance,
+						OnuSN:          o.SerialNumber,
+						OnuID:          o.ID,
+						AdminState:     adminState,
+						EntityID:       msgObj.EntityInstance,
+						RaiseOMCIAlarm: raiseOMCIAlarm,
 					},
 				}
 				o.Channel <- msg
@@ -1056,6 +1086,20 @@
 				"CommittedImageEntityId": o.CommittedImageEntityId,
 			}).Info("onu-software-image-committed")
 		}
+	case omci.GetAllAlarmsRequestType:
+		// Reset the alarm sequence number on receiving get all alarms request.
+		o.onuAlarmsInfoLock.Lock()
+		for key, alarmInfo := range o.onuAlarmsInfo {
+			// reset the alarm sequence no
+			alarmInfo.SequenceNo = 0
+			o.onuAlarmsInfo[key] = alarmInfo
+		}
+		o.onuAlarmsInfoLock.Unlock()
+		responsePkt, _ = omcilib.CreateGetAllAlarmsResponse(omciMsg.TransactionID, o.onuAlarmsInfo)
+	case omci.GetAllAlarmsNextRequestType:
+		if responsePkt, errResp = omcilib.CreateGetAllAlarmsNextResponse(omciPkt, omciMsg, o.onuAlarmsInfo); errResp != nil {
+			responsePkt = nil //Do not send any response for error case
+		}
 	default:
 		onuLogger.WithFields(log.Fields{
 			"omciBytes":    hex.EncodeToString(omciPkt.Data()),
@@ -1527,3 +1571,40 @@
 	}
 	return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
 }
+
+func (o *Onu) SendOMCIAlarmNotificationMsg(raiseOMCIAlarm bool, alarmType string) {
+	switch alarmType {
+	case "ONU_ALARM_LOS":
+		msg := bbsim.Message{
+			Type: bbsim.UniStatusAlarm,
+			Data: bbsim.UniStatusAlarmMessage{
+				OnuSN:          o.SerialNumber,
+				OnuID:          o.ID,
+				EntityID:       257,
+				RaiseOMCIAlarm: raiseOMCIAlarm,
+			},
+		}
+		o.Channel <- msg
+	}
+
+}
+
+func (o *Onu) IncrementAlarmSequenceNumber(key omcilib.OnuAlarmInfoMapKey) uint8 {
+	o.onuAlarmsInfoLock.Lock()
+	defer o.onuAlarmsInfoLock.Unlock()
+	if alarmInfo, ok := o.onuAlarmsInfo[key]; ok {
+		if alarmInfo.SequenceNo == 255 {
+			alarmInfo.SequenceNo = 1
+		} else {
+			alarmInfo.SequenceNo++
+		}
+		o.onuAlarmsInfo[key] = alarmInfo
+		return alarmInfo.SequenceNo
+	} else {
+		// This is the first time alarm notification message is being sent
+		o.onuAlarmsInfo[key] = omcilib.OnuAlarmInfo{
+			SequenceNo: 1,
+		}
+		return 1
+	}
+}
diff --git a/internal/bbsim/types/messageTypes.go b/internal/bbsim/types/messageTypes.go
index 3afe7fb..4068dfa 100644
--- a/internal/bbsim/types/messageTypes.go
+++ b/internal/bbsim/types/messageTypes.go
@@ -117,10 +117,11 @@
 }
 
 type UniStatusAlarmMessage struct {
-	OnuSN      *openolt.SerialNumber
-	OnuID      uint32
-	AdminState uint8
-	EntityID   uint16
+	OnuSN          *openolt.SerialNumber
+	OnuID          uint32
+	AdminState     uint8
+	EntityID       uint16
+	RaiseOMCIAlarm bool
 }
 
 // these are OMCI messages going from the OLT to VOLTHA
diff --git a/internal/common/omci/alarms.go b/internal/common/omci/alarms.go
index aaa2fdd..5d8144b 100644
--- a/internal/common/omci/alarms.go
+++ b/internal/common/omci/alarms.go
@@ -16,32 +16,168 @@
 
 package omci
 
+import (
+	"errors"
+	"fmt"
+	"github.com/google/gopacket"
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	log "github.com/sirupsen/logrus"
+)
+
+type OnuAlarmInfo struct {
+	SequenceNo  uint8
+	AlarmBitMap [28]byte
+}
+type OnuAlarmInfoMapKey struct {
+	MeInstance uint16
+	MeClassID  me.ClassID
+}
+
 // CreateUniStatusAlarm will generate an Alarm packet to report that the Link is UP or DOWN
 // as a consequence of a SetRequest on PhysicalPathTerminationPointEthernetUniClassID
-func CreateUniStatusAlarm(adminState uint8, entityId uint16) []byte {
+func CreateUniStatusAlarm(raiseAlarm bool, entityId uint16, sequenceNo uint8) ([]byte, [28]byte) {
+	notif := &omci.AlarmNotificationMsg{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass:    me.PhysicalPathTerminationPointEthernetUniClassID,
+			EntityInstance: entityId,
+		},
+		AlarmSequenceNumber: byte(sequenceNo),
+	}
+	if raiseAlarm {
+		//PPTP class has only 0th bit alarm for UNI LOS
+		if err := notif.ActivateAlarm(0); err != nil {
+			omciLogger.WithFields(log.Fields{
+				"Err": err,
+			}).Error("Cannot Create AlarmNotificationMsg")
+			return nil, [28]byte{}
+		}
+	} else {
+		if err := notif.ClearAlarm(0); err != nil {
+			omciLogger.WithFields(log.Fields{
+				"Err": err,
+			}).Error("Cannot Create AlarmNotificationMsg")
+			return nil, [28]byte{}
+		}
+	}
+	pkt, err := Serialize(omci.AlarmNotificationType, notif, 0)
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Error("Cannot Serialize AlarmNotificationMsg")
+		return nil, [28]byte{}
+	}
+	return pkt, notif.AlarmBitmap
+}
 
-	// TODO generate using omci-lib-go
-	linkMsgDown := []byte{
-		0x00, 0x00, 0x10, 0x0a, 0x00, 0x0b, 0x01, 0x01,
-		0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+func CreateGetAllAlarmsResponse(tid uint16, onuAlarmDetails map[OnuAlarmInfoMapKey]OnuAlarmInfo) ([]byte, error) {
+	var alarmEntityClass me.ClassID
+	var meInstance uint16
+	var noOfCommands uint16 = 0
+	alarmEntityClass = me.PhysicalPathTerminationPointEthernetUniClassID //Currently doing for PPTP classID
+	meInstance = 257
+	key := OnuAlarmInfoMapKey{
+		MeInstance: meInstance,
+		MeClassID:  alarmEntityClass,
+	}
+	if _, ok := onuAlarmDetails[key]; ok {
+		noOfCommands = 1
+	}
+	numberOfCommands := uint16(noOfCommands)
 
-	linkMsgUp := []byte{
-		0x00, 0x00, 0x10, 0x0a, 0x00, 0x0b, 0x01, 0x01,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+	request := &omci.GetAllAlarmsResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuDataClassID,
+		},
+		NumberOfCommands: numberOfCommands,
+	}
+	pkt, err := Serialize(omci.GetAllAlarmsResponseType, request, tid)
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Error("Cannot Serialize GetAllAlarmsResponse")
+		return nil, err
+	}
+	return pkt, nil
+}
+func ParseGetAllAlarmsNextRequest(omciPkt gopacket.Packet) (*omci.GetAllAlarmsNextRequest, error) {
+	msgLayer := omciPkt.Layer(omci.LayerTypeGetAllAlarmsNextRequest)
+	if msgLayer == nil {
+		err := "omci Msg layer could not be detected for LayerTypeGetAllAlarmsNextRequest"
+		omciLogger.Error(err)
+		return nil, errors.New(err)
+	}
+	msgObj, msgOk := msgLayer.(*omci.GetAllAlarmsNextRequest)
+	if !msgOk {
+		err := "omci Msg layer could not be assigned for GetAllAlarmsNextRequest"
+		omciLogger.Error(err)
+		return nil, errors.New(err)
+	}
+	return msgObj, nil
+}
 
-	if adminState == 0 {
-		return linkMsgUp
-	} else if adminState == 1 {
-		return linkMsgDown
+func CreateGetAllAlarmsNextResponse(omciPkt gopacket.Packet, omciMsg *omci.OMCI, onuAlarmDetails map[OnuAlarmInfoMapKey]OnuAlarmInfo) ([]byte, error) {
+
+	msgObj, err := ParseGetAllAlarmsNextRequest(omciPkt)
+	if err != nil {
+		err := "omci Msg layer could not be assigned for LayerTypeGetRequest"
+		omciLogger.Error(err)
+		return nil, errors.New(err)
 	}
 
-	return nil
+	omciLogger.WithFields(log.Fields{
+		"EntityClass":           msgObj.EntityClass,
+		"EntityInstance":        msgObj.EntityInstance,
+		"CommandSequenceNumber": msgObj.CommandSequenceNumber,
+	}).Trace("received-omci-get-all-alarms-next-request")
+
+	var alarmEntityClass me.ClassID
+	var meInstance uint16
+	var alarmBitMap [28]byte
+
+	switch msgObj.CommandSequenceNumber {
+	case 0:
+		alarmEntityClass = me.PhysicalPathTerminationPointEthernetUniClassID
+		meInstance = 257
+		//Checking if the alarm is raised in the bitmap, we will send clear just to generate missed clear alarm and
+		// vice versa.
+		key := OnuAlarmInfoMapKey{
+			MeInstance: meInstance,
+			MeClassID:  alarmEntityClass,
+		}
+		if alarmInfo, ok := onuAlarmDetails[key]; ok {
+			alarmBitMap = alarmInfo.AlarmBitMap
+		} else {
+			return nil, fmt.Errorf("alarm-info-for-me-not-present-in-alarm-info-map")
+		}
+	default:
+		omciLogger.Warn("unsupported-CommandSequenceNumber-in-get-all-alarm-next", msgObj.CommandSequenceNumber)
+		return nil, fmt.Errorf("unspported-command-sequence-number-in-get-all-alarms-next")
+	}
+
+	response := &omci.GetAllAlarmsNextResponse{
+		MeBasePacket: omci.MeBasePacket{
+			EntityClass: me.OnuDataClassID,
+		},
+		AlarmEntityClass:    alarmEntityClass,
+		AlarmEntityInstance: meInstance,
+		AlarmBitMap:         alarmBitMap,
+	}
+
+	omciLogger.WithFields(log.Fields{
+		"AlarmEntityClass":    alarmEntityClass,
+		"AlarmEntityInstance": meInstance,
+		"AlarmBitMap":         alarmBitMap,
+	}).Trace("created-omci-getAllAlarmsNext-response")
+
+	pkt, err := Serialize(omci.GetAllAlarmsNextResponseType, response, omciMsg.TransactionID)
+
+	if err != nil {
+		omciLogger.WithFields(log.Fields{
+			"Err": err,
+		}).Fatalf("Cannot Serialize GetAllAlarmsNextRequest")
+		return nil, err
+	}
+
+	return pkt, nil
 }