SEBA-432
SEBA-565
SEBA-654 (alarms)

implemented

fix Jenkins make errors
fix merge conflicts
address review comments

Change-Id: Ia2e95afb33ce55054afa1fcbd9beb6ada62dd764
diff --git a/core/api_handler.go b/core/api_handler.go
new file mode 100644
index 0000000..eb52977
--- /dev/null
+++ b/core/api_handler.go
@@ -0,0 +1,424 @@
+/*
+ * 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.
+ */
+
+package core
+
+import (
+	"errors"
+	"net"
+	"strconv"
+	"time"
+
+	pb "gerrit.opencord.org/voltha-bbsim/api"
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	"gerrit.opencord.org/voltha-bbsim/device"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// handleONUActivate process ONU status request
+func (s *Server) handleONUStatusRequest(in *pb.ONUInfo) (*pb.ONUs, error) {
+	onuInfo := &pb.ONUs{}
+	if in.OnuSerial != "" { // Get status of single ONU by SerialNumber
+		// Get OpenOlt serial number from string
+		sn, err := getOpenoltSerialNumber(in.OnuSerial)
+		if err != nil {
+			logger.Error("Invalid serial number %s", in.OnuSerial)
+			return onuInfo, status.Errorf(codes.InvalidArgument, "serial: "+in.OnuSerial+" is invalid")
+		}
+		// Get ONU by serial number
+		onu, err := getOnuBySN(s.Onumap, sn)
+		if err != nil {
+			logger.Error("ONU with serial number %v not found", sn)
+			return onuInfo, status.Errorf(codes.NotFound, "serial: "+in.OnuSerial+" not found")
+		}
+		onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+	} else {
+		// Return error if specified PON port does not exist
+		if _, exist := s.Onumap[in.PonPortId]; !exist {
+			logger.Error("PON port %d not found", in.PonPortId)
+			return onuInfo, status.Errorf(codes.NotFound, "PON Port: "+strconv.Itoa(int(in.PonPortId))+" not found")
+		}
+
+		if in.OnuId != 0 { // Get status of single ONU by ONU-ID
+			for intfid := range s.Onumap {
+				for _, onu := range s.Onumap[intfid] {
+					if in.OnuId == onu.OnuID {
+						onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+					}
+				}
+			}
+		} else {
+			// Append ONU data
+			for _, onu := range s.Onumap[in.PonPortId] {
+				onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+			}
+		}
+	}
+
+	return onuInfo, nil
+}
+
+// handleONUActivate method handles ONU activate requests from user.
+func (s *Server) handleONUActivate(in []*pb.ONUInfo) (*pb.BBSimResponse, error) {
+	logger.Info("handleONUActivate request received")
+	logger.Debug("Received values: %+v\n", in)
+
+	// Check if indication is enabled
+	if s.EnableServer == nil {
+		logger.Error(OLTNotEnabled)
+		return &pb.BBSimResponse{}, status.Errorf(codes.FailedPrecondition, OLTNotEnabled)
+	}
+
+	onuaddmap := make(map[uint32][]*device.Onu)
+	var newSerialNums []string
+
+	//Iterate request for each PON port specified
+	for _, onu := range in {
+		intfid := onu.PonPortId
+
+		// Get the free ONU object for the intfid
+		Onu, err := s.GetNextFreeOnu(intfid)
+		if err != nil {
+			markONUsFree(onuaddmap)
+			logger.Error("Failed to get free ONU object for intfID %d :%v", intfid, err)
+			return &pb.BBSimResponse{}, status.Errorf(codes.ResourceExhausted, err.Error())
+		}
+
+		// Check if Serial number is provided by user
+		if onu.OnuSerial != "" {
+			// Get OpenOlt serial number from string
+			sn, err := getOpenoltSerialNumber(onu.OnuSerial)
+			if err != nil {
+				logger.Error("Failed to get OpenOlt serial number %v", err)
+				Onu.InternalState = device.ONU_FREE
+				markONUsFree(onuaddmap)
+				return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "serial number: "+onu.OnuSerial+" is invalid")
+			}
+
+			// Check if serial number is not duplicate in requested ONUs
+			for _, sn := range newSerialNums {
+				if onu.OnuSerial == sn {
+					logger.Error("Duplicate serial number found %s", sn)
+					// Mark ONUs free
+					markONUsFree(onuaddmap)
+					Onu.InternalState = device.ONU_FREE
+					return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "duplicate serial number: "+onu.OnuSerial+" provided")
+				}
+			}
+			newSerialNums = append(newSerialNums, onu.OnuSerial)
+
+			// Check if serial number already exist
+			_, exist := s.getOnuFromSNmap(sn)
+			if exist {
+				logger.Error("Provided serial number %v already exist", sn)
+				// Mark ONUs free
+				markONUsFree(onuaddmap)
+				Onu.InternalState = device.ONU_FREE
+				return &pb.BBSimResponse{}, status.Errorf(codes.AlreadyExists, "serial number: "+onu.OnuSerial+" already exist")
+			}
+
+			// Store user provided serial number in ONU object
+			Onu.SerialNumber = sn
+		}
+		// Store onu object in map for particular intfid
+		onuaddmap[intfid] = append(onuaddmap[intfid], Onu)
+	}
+
+	if len(onuaddmap) >= 1 {
+		//Pass onumap to activateONU to handle indication to VOLTHA
+		s.activateONUs(*s.EnableServer, onuaddmap)
+	}
+
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// handleONUDeactivate deactivates ONU described by a single ONUInfo object
+func (s *Server) handleONUDeactivate(in *pb.ONUInfo) error {
+
+	if s.EnableServer == nil {
+		logger.Error(OLTNotEnabled)
+		return status.Errorf(codes.FailedPrecondition, OLTNotEnabled)
+	}
+
+	if in.OnuSerial != "" {
+		// Get OpenOlt serial number from string
+		serialNumber, err := getOpenoltSerialNumber(in.OnuSerial)
+		if err != nil {
+			logger.Error("Invalid serial number %s", in.OnuSerial)
+			return status.Errorf(codes.InvalidArgument, "serial: "+in.OnuSerial+" is invalid")
+		}
+		// Get ONU by serial number
+		onu, exist := s.getOnuFromSNmap(serialNumber)
+		if !exist {
+			logger.Error("ONU with serial number %s not found", in.OnuSerial)
+			return status.Errorf(codes.NotFound, "serial: "+in.OnuSerial+" not found")
+		}
+
+		if err := s.HandleOnuDeactivate(onu); err != nil {
+			return err
+		}
+	} else {
+		if in.OnuId != 0 { // if provided, delete ONU by ONU ID
+			onu, err := getOnuByID(s.Onumap, in.OnuId, in.PonPortId)
+			if err != nil {
+				return err
+			}
+			if err := s.HandleOnuDeactivate(onu); err != nil {
+				return err
+			}
+		} else { // delete all ONUs on provided port
+			if err := s.DeactivateAllOnuByIntfID(in.PonPortId); err != nil {
+				logger.Error("Failed in handleONUDeactivate: %v", err)
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (s *Server) handleOLTReboot() {
+	logger.Debug("HandleOLTReboot() invoked")
+	logger.Debug("Sending stop to serverActionCh")
+	s.serverActionCh <- OpenOltStop
+	time.Sleep(40 * time.Second)
+
+	logger.Debug("Sending start to serverActionCh")
+	s.serverActionCh <- OpenOltStart
+	for {
+		if s.Olt.GetIntState() == device.OLT_ACTIVE {
+			logger.Info("Info: OLT reactivated")
+			break
+		}
+		time.Sleep(2 * time.Second)
+	}
+	s.sendOnuIndicationsOnOltReboot()
+}
+
+func (s *Server) handleONUHardReboot(onu *device.Onu) {
+	logger.Debug("handleONUHardReboot() invoked")
+	_ = sendDyingGaspInd(*s.EnableServer, onu.IntfID, onu.OnuID)
+	device.UpdateOnusOpStatus(onu.IntfID, onu, "down")
+	// send operstat down to voltha
+	_ = sendOnuInd(*s.EnableServer, onu, s.IndInterval, "down", "up")
+	// Give OEH some time to perform cleanup
+	time.Sleep(30 * time.Second)
+	s.activateOnu(onu)
+}
+
+func (s *Server) handleONUSoftReboot(IntfID uint32, OnuID uint32) {
+	logger.Debug("handleONUSoftReboot() invoked")
+	onu, err := s.GetOnuByID(OnuID, IntfID)
+	if err != nil {
+		logger.Error("No onu found with given OnuID on interface %v", IntfID)
+	}
+	OnuAlarmRequest := &pb.ONUAlarmRequest{
+		OnuSerial: stringifySerialNumber(onu.SerialNumber),
+		AlarmType: OnuLossOfPloam,
+		Status:    "on",
+	}
+	// Raise alarm
+	_, err = s.handleOnuAlarm(OnuAlarmRequest)
+	if err != nil {
+		logger.Error(err.Error())
+	}
+	// Clear alarm
+	time.Sleep(10 * time.Second)
+	OnuAlarmRequest.Status = "off"
+	_, err = s.handleOnuAlarm(OnuAlarmRequest)
+	if err != nil {
+		logger.Error(err.Error())
+	}
+}
+
+// GetNextFreeOnu returns free onu object for specified interface ID
+func (s *Server) GetNextFreeOnu(intfid uint32) (*device.Onu, error) {
+	onus, ok := s.Onumap[intfid]
+	if !ok {
+		return nil, errors.New("interface " + strconv.Itoa(int(intfid)) + " not present in ONU map")
+	}
+	for _, onu := range onus {
+		if onu.InternalState == device.ONU_FREE {
+			// If auto generated serial number is already used by some other ONU,
+			// continue to find for other free object
+			snkey := stringifySerialNumber(onu.SerialNumber)
+			if _, exist := s.SNmap.Load(snkey); exist {
+				continue
+			}
+			// Update Onu Internal State
+			onu.InternalState = device.ONU_INACTIVE
+			return onu, nil
+		}
+	}
+	return nil, errors.New("no free ONU found for pon port: " + strconv.Itoa(int(intfid)))
+}
+
+// DeactivateAllOnuByIntfID deletes all ONUs for given PON port ID
+func (s *Server) DeactivateAllOnuByIntfID(intfid uint32) error {
+	for _, onu := range s.Onumap[intfid] {
+		if onu.InternalState == device.ONU_FREE || onu.InternalState == device.ONU_INACTIVE {
+			continue
+		}
+		if err := s.HandleOnuDeactivate(onu); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// HandleOnuDeactivate method handles ONU state changes and sending Indication to voltha
+func (s *Server) HandleOnuDeactivate(onu *device.Onu) error {
+	logger.Debug("Deactivating ONU %d for Intf: %d", onu.OnuID, onu.IntfID)
+
+	// Update ONU internal state to ONU_INACTIVE
+	s.updateDevIntState(onu, device.ONU_INACTIVE)
+
+	// Update ONU operstate to down
+	onu.OperState = "down"
+
+	// Send DyingGasp Alarm to VOLTHA
+	_ = sendDyingGaspInd(*s.EnableServer, onu.IntfID, onu.OnuID)
+	_ = sendOnuInd(*s.EnableServer, onu, s.IndInterval, onu.OperState, "down")
+	return nil
+}
+
+func markONUsFree(onumap map[uint32][]*device.Onu) {
+	for intfid := range onumap {
+		for _, onu := range onumap[intfid] {
+			onu.UpdateIntState(device.ONU_FREE)
+		}
+	}
+}
+
+func copyONUInfo(onu *device.Onu) *pb.ONUInfo {
+	onuData := &pb.ONUInfo{
+		OnuId:     onu.OnuID,
+		PonPortId: onu.IntfID,
+		OnuSerial: stringifySerialNumber(onu.SerialNumber),
+		OnuState:  device.ONUState[onu.InternalState],
+		OperState: onu.OperState,
+	}
+	return onuData
+}
+
+func (s *Server) fetchPortDetail(intfID uint32, portType string) (*pb.PortInfo, error) {
+	logger.Debug("fetchPortDetail() invoked")
+	portInfo := &pb.PortInfo{}
+	switch portType {
+	case device.IntfNni:
+		if !s.isNniIntfPresentInOlt(intfID) {
+			return &pb.PortInfo{}, errors.New("NNI " + strconv.Itoa(int(intfID)) + " not present in " +
+				strconv.Itoa(int(s.Olt.ID)))
+		}
+		portInfo = &pb.PortInfo{
+			PortType:          portType,
+			PortId:            intfID,
+			PonPortMaxOnus:    0,
+			PonPortActiveOnus: 0,
+			PortState:         s.Olt.NniIntfs[intfID].OperState,
+			AlarmState:        device.OLTAlarmStateToString[s.Olt.NniIntfs[intfID].AlarmState],
+		}
+		return portInfo, nil
+
+	case device.IntfPon:
+		if !s.isPonIntfPresentInOlt(intfID) {
+			return &pb.PortInfo{}, errors.New("PON " + strconv.Itoa(int(intfID)) + " not present in OLT-" +
+				strconv.Itoa(int(s.Olt.ID)))
+		}
+		portInfo = &pb.PortInfo{
+			PortType:          portType,
+			PortId:            intfID,
+			PonPortMaxOnus:    int32(len(s.Onumap[uint32(intfID)])),
+			PonPortActiveOnus: s.getNoOfActiveOnuByPortID(intfID),
+			PortState:         s.Olt.PonIntfs[intfID].OperState,
+			AlarmState:        device.OLTAlarmStateToString[s.Olt.PonIntfs[intfID].AlarmState],
+		}
+		return portInfo, nil
+	default:
+		return &pb.PortInfo{}, errors.New(portType + " is not a valid port type")
+	}
+}
+
+func (s *Server) validateDeviceActionRequest(request *pb.DeviceAction) (*pb.DeviceAction, error) {
+	switch request.DeviceType {
+	case DeviceTypeOnu:
+		if request.DeviceSerialNumber == "" {
+			return request, errors.New("onu serial number can not be blank")
+		}
+
+		if len(request.DeviceSerialNumber) != SerialNumberLength {
+			return request, errors.New("invalid serial number provided")
+		}
+
+		if request.DeviceAction != SoftReboot && request.DeviceAction != HardReboot {
+			return request, errors.New("invalid device action provided")
+		}
+		return request, nil
+	case DeviceTypeOlt:
+		request.DeviceType = DeviceTypeOlt
+		request.DeviceAction = HardReboot
+		return request, nil
+	default:
+		return request, errors.New("invalid device type")
+	}
+}
+
+func (s *Server) getNoOfActiveOnuByPortID(portID uint32) uint32 {
+	var noOfActiveOnus uint32
+	for _, onu := range s.Onumap[portID] {
+		if onu.InternalState == device.ONU_ACTIVE || onu.InternalState == device.ONU_OMCIACTIVE {
+			noOfActiveOnus++
+		}
+	}
+	return noOfActiveOnus
+}
+
+func (s *Server) isPonIntfPresentInOlt(intfID uint32) bool {
+	for _, intf := range s.Olt.PonIntfs {
+		if intf.IntfID == intfID {
+			return true
+		}
+	}
+	return false
+}
+
+func (s *Server) isNniIntfPresentInOlt(intfID uint32) bool {
+	for _, intf := range s.Olt.NniIntfs {
+		if intf.IntfID == intfID {
+			return true
+		}
+	}
+	return false
+}
+
+func getOltIP() net.IP {
+	// TODO make this better
+	conn, err := net.Dial("udp", "8.8.8.8:80")
+	if err != nil {
+		logger.Error(err.Error())
+		return net.IP{}
+	}
+	defer func() {
+		err := conn.Close()
+		if err != nil {
+			logger.Error(err.Error())
+		}
+	}()
+
+	localAddr := conn.LocalAddr().(*net.UDPAddr)
+
+	return localAddr.IP
+}