[VOL-3380] Functional area specific logging

Change-Id: I67414da013d8fc82827fcdb69d4f8a34040625d3
diff --git a/internal/pkg/uniprt/common.go b/internal/pkg/uniprt/common.go
new file mode 100755
index 0000000..3dcfe83
--- /dev/null
+++ b/internal/pkg/uniprt/common.go
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020-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 uniprt provides the utilities for uni port configuration
+package uniprt
+
+import (
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+)
+
+var logger log.CLogger
+
+func init() {
+	// Setup this package so that it's log level can be modified at run time
+	var err error
+	logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{"pkg": "uniprt"})
+	if err != nil {
+		panic(err)
+	}
+}
diff --git a/internal/pkg/uniprt/uniportadmin.go b/internal/pkg/uniprt/uniportadmin.go
new file mode 100755
index 0000000..a9de1dd
--- /dev/null
+++ b/internal/pkg/uniprt/uniportadmin.go
@@ -0,0 +1,507 @@
+/*
+ * Copyright 2020-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 uniprt provides the utilities for uni port configuration
+package uniprt
+
+import (
+	"context"
+	"fmt"
+	"sync"
+	"time"
+
+	"github.com/looplab/fsm"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+)
+
+//LockStateFsm defines the structure for the state machine to lock/unlock the ONU UNI ports via OMCI
+type LockStateFsm struct {
+	deviceID                 string
+	pDeviceHandler           cmn.IdeviceHandler
+	pOnuDeviceEntry          cmn.IonuDeviceEntry
+	pOmciCC                  *cmn.OmciCC
+	mutexAdminState          sync.RWMutex
+	adminState               bool
+	requestEvent             cmn.OnuDeviceEvent
+	omciLockResponseReceived chan bool //seperate channel needed for checking UNI port OMCi message responses
+	PAdaptFsm                *cmn.AdapterFsm
+	mutexPLastTxMeInstance   sync.RWMutex
+	pLastTxMeInstance        *me.ManagedEntity
+}
+
+// events of lock/unlock UNI port FSM
+const (
+	UniEvStart         = "UniEvStart"
+	UniEvStartAdmin    = "UniEvStartAdmin"
+	UniEvRxUnisResp    = "UniEvRxUnisResp"
+	UniEvRxOnugResp    = "UniEvRxOnugResp"
+	UniEvTimeoutSimple = "UniEvTimeoutSimple"
+	UniEvTimeoutUnis   = "UniEvTimeoutUnis"
+	UniEvReset         = "UniEvReset"
+	UniEvRestart       = "UniEvRestart"
+)
+
+// states of lock/unlock UNI port FSM
+const (
+	UniStDisabled    = "UniStDisabled"
+	UniStStarting    = "UniStStarting"
+	UniStSettingUnis = "UniStSettingUnis"
+	UniStSettingOnuG = "UniStSettingOnuG"
+	UniStAdminDone   = "UniStAdminDone"
+	UniStResetting   = "UniStResetting"
+)
+
+// CUniFsmIdleState - TODO: add comment
+const CUniFsmIdleState = UniStDisabled
+
+//NewLockStateFsm is the 'constructor' for the state machine to lock/unlock the ONU UNI ports via OMCI
+func NewLockStateFsm(ctx context.Context, aAdminState bool, aRequestEvent cmn.OnuDeviceEvent,
+	aName string, apDeviceHandler cmn.IdeviceHandler, apOnuDeviceEntry cmn.IonuDeviceEntry, aCommChannel chan cmn.Message) *LockStateFsm {
+	instFsm := &LockStateFsm{
+		deviceID:        apDeviceHandler.GetDeviceID(),
+		pDeviceHandler:  apDeviceHandler,
+		pOnuDeviceEntry: apOnuDeviceEntry,
+		pOmciCC:         apOnuDeviceEntry.GetDevOmciCC(),
+		adminState:      aAdminState,
+		requestEvent:    aRequestEvent,
+	}
+	instFsm.PAdaptFsm = cmn.NewAdapterFsm(aName, instFsm.deviceID, aCommChannel)
+	if instFsm.PAdaptFsm == nil {
+		logger.Errorw(ctx, "LockStateFsm's cmn.AdapterFsm could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+	if aAdminState { //port locking requested
+		instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+			UniStDisabled,
+			fsm.Events{
+
+				{Name: UniEvStart, Src: []string{UniStDisabled}, Dst: UniStStarting},
+
+				{Name: UniEvStartAdmin, Src: []string{UniStStarting}, Dst: UniStSettingUnis},
+				// the settingUnis state is used for multi ME config for all UNI related ports
+				// maybe such could be reflected in the state machine as well (port number parametrized)
+				// but that looks not straightforward here - so we keep it simple here for the beginning(?)
+				{Name: UniEvRxUnisResp, Src: []string{UniStSettingUnis}, Dst: UniStSettingOnuG},
+				{Name: UniEvRxOnugResp, Src: []string{UniStSettingOnuG}, Dst: UniStAdminDone},
+
+				{Name: UniEvTimeoutSimple, Src: []string{UniStSettingOnuG}, Dst: UniStStarting},
+				{Name: UniEvTimeoutUnis, Src: []string{UniStSettingUnis}, Dst: UniStStarting},
+
+				{Name: UniEvReset, Src: []string{UniStStarting, UniStSettingOnuG, UniStSettingUnis,
+					UniStAdminDone}, Dst: UniStResetting},
+				// exceptional treatment for all states except UniStResetting
+				{Name: UniEvRestart, Src: []string{UniStStarting, UniStSettingOnuG, UniStSettingUnis,
+					UniStAdminDone, UniStResetting}, Dst: UniStDisabled},
+			},
+
+			fsm.Callbacks{
+				"enter_state":                 func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+				("enter_" + UniStStarting):    func(e *fsm.Event) { instFsm.enterAdminStartingState(ctx, e) },
+				("enter_" + UniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(ctx, e) },
+				("enter_" + UniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(ctx, e) },
+				("enter_" + UniStAdminDone):   func(e *fsm.Event) { instFsm.enterAdminDoneState(ctx, e) },
+				("enter_" + UniStResetting):   func(e *fsm.Event) { instFsm.enterResettingState(ctx, e) },
+			},
+		)
+	} else { //port unlocking requested
+		instFsm.PAdaptFsm.PFsm = fsm.NewFSM(
+			UniStDisabled,
+			fsm.Events{
+
+				{Name: UniEvStart, Src: []string{UniStDisabled}, Dst: UniStStarting},
+
+				{Name: UniEvStartAdmin, Src: []string{UniStStarting}, Dst: UniStSettingOnuG},
+				{Name: UniEvRxOnugResp, Src: []string{UniStSettingOnuG}, Dst: UniStSettingUnis},
+				// the settingUnis state is used for multi ME config for all UNI related ports
+				// maybe such could be reflected in the state machine as well (port number parametrized)
+				// but that looks not straightforward here - so we keep it simple here for the beginning(?)
+				{Name: UniEvRxUnisResp, Src: []string{UniStSettingUnis}, Dst: UniStAdminDone},
+
+				{Name: UniEvTimeoutSimple, Src: []string{UniStSettingOnuG}, Dst: UniStStarting},
+				{Name: UniEvTimeoutUnis, Src: []string{UniStSettingUnis}, Dst: UniStStarting},
+
+				{Name: UniEvReset, Src: []string{UniStStarting, UniStSettingOnuG, UniStSettingUnis,
+					UniStAdminDone}, Dst: UniStResetting},
+				// exceptional treatment for all states except UniStResetting
+				{Name: UniEvRestart, Src: []string{UniStStarting, UniStSettingOnuG, UniStSettingUnis,
+					UniStAdminDone, UniStResetting}, Dst: UniStDisabled},
+			},
+
+			fsm.Callbacks{
+				"enter_state":                 func(e *fsm.Event) { instFsm.PAdaptFsm.LogFsmStateChange(ctx, e) },
+				("enter_" + UniStStarting):    func(e *fsm.Event) { instFsm.enterAdminStartingState(ctx, e) },
+				("enter_" + UniStSettingOnuG): func(e *fsm.Event) { instFsm.enterSettingOnuGState(ctx, e) },
+				("enter_" + UniStSettingUnis): func(e *fsm.Event) { instFsm.enterSettingUnisState(ctx, e) },
+				("enter_" + UniStAdminDone):   func(e *fsm.Event) { instFsm.enterAdminDoneState(ctx, e) },
+				("enter_" + UniStResetting):   func(e *fsm.Event) { instFsm.enterResettingState(ctx, e) },
+			},
+		)
+	}
+	if instFsm.PAdaptFsm.PFsm == nil {
+		logger.Errorw(ctx, "LockStateFsm's Base FSM could not be instantiated!!", log.Fields{
+			"device-id": instFsm.deviceID})
+		return nil
+	}
+
+	logger.Debugw(ctx, "LockStateFsm created", log.Fields{"device-id": instFsm.deviceID})
+	return instFsm
+}
+
+//SetSuccessEvent modifies the requested event notified on success
+//assumption is that this is only called in the disabled (idle) state of the FSM, hence no sem protection required
+func (oFsm *LockStateFsm) SetSuccessEvent(aEvent cmn.OnuDeviceEvent) {
+	oFsm.requestEvent = aEvent
+}
+
+func (oFsm *LockStateFsm) enterAdminStartingState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "LockStateFSM start", log.Fields{"in state": e.FSM.Current(),
+		"device-id": oFsm.deviceID})
+	// in case the used channel is not yet defined (can be re-used after restarts)
+	if oFsm.omciLockResponseReceived == nil {
+		oFsm.omciLockResponseReceived = make(chan bool)
+		logger.Debug(ctx, "LockStateFSM - OMCI UniLock RxChannel defined")
+	} else {
+		// as we may 're-use' this instance of FSM and the connected channel
+		// make sure there is no 'lingering' request in the already existing channels:
+		// (simple loop sufficient as we are the only receiver)
+		for len(oFsm.omciLockResponseReceived) > 0 {
+			<-oFsm.omciLockResponseReceived
+		}
+		for len(oFsm.PAdaptFsm.CommChan) > 0 {
+			<-oFsm.PAdaptFsm.CommChan
+		}
+	}
+	// start go routine for processing of LockState messages
+	go oFsm.processOmciLockMessages(ctx)
+
+	//let the state machine run forward from here directly
+	pLockStateAFsm := oFsm.PAdaptFsm
+	if pLockStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+				_ = a_pAFsm.PFsm.Event(UniEvStartAdmin)
+			}
+		}(pLockStateAFsm)
+	}
+}
+
+func (oFsm *LockStateFsm) enterSettingOnuGState(ctx context.Context, e *fsm.Event) {
+	var omciAdminState uint8 = 1 //default locked
+	oFsm.mutexAdminState.RLock()
+	if !oFsm.adminState {
+		omciAdminState = 0
+	}
+	oFsm.mutexAdminState.RUnlock()
+	logger.Debugw(ctx, "LockStateFSM Tx Set::ONU-G:admin", log.Fields{
+		"omciAdmin": omciAdminState, "in state": e.FSM.Current(), "device-id": oFsm.deviceID})
+	requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+	oFsm.mutexPLastTxMeInstance.Lock()
+	meInstance, err := oFsm.pOmciCC.SendSetOnuGLS(log.WithSpanFromContext(context.TODO(), ctx), oFsm.pDeviceHandler.GetOmciTimeout(), true,
+		requestedAttributes, oFsm.PAdaptFsm.CommChan)
+	if err != nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "OnuGLS set failed, aborting LockStateFSM", log.Fields{"device-id": oFsm.deviceID})
+		pLockStateAFsm := oFsm.PAdaptFsm
+		if pLockStateAFsm != nil {
+			go func(a_pAFsm *cmn.AdapterFsm) {
+				if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+					_ = a_pAFsm.PFsm.Event(UniEvReset)
+				}
+			}(pLockStateAFsm)
+		}
+		return
+	}
+	//accept also nil as (error) return value for writing to LastTx
+	//  - this avoids misinterpretation of new received OMCI messages
+	oFsm.pLastTxMeInstance = meInstance
+	if oFsm.pLastTxMeInstance == nil {
+		oFsm.mutexPLastTxMeInstance.Unlock()
+		logger.Errorw(ctx, "could not send OMCI message from LockStateFsm", log.Fields{
+			"device-id": oFsm.deviceID})
+		//some more sophisticated approach is possible, e.g. repeating once, by now let's reset the state machine in order to release all resources now
+		pLockStateAFsm := oFsm.PAdaptFsm
+		if pLockStateAFsm != nil {
+
+			// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+			go func(a_pAFsm *cmn.AdapterFsm) {
+				if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+					_ = a_pAFsm.PFsm.Event(UniEvReset)
+				}
+			}(pLockStateAFsm)
+		}
+		return
+	}
+	oFsm.mutexPLastTxMeInstance.Unlock()
+}
+
+func (oFsm *LockStateFsm) enterSettingUnisState(ctx context.Context, e *fsm.Event) {
+	oFsm.mutexAdminState.RLock()
+	logger.Debugw(ctx, "LockStateFSM - starting UniTP adminState loop", log.Fields{
+		"in state": e.FSM.Current(), "device-id": oFsm.deviceID, "LockState": oFsm.adminState})
+	oFsm.mutexAdminState.RUnlock()
+	go oFsm.performUniPortAdminSet(ctx)
+}
+
+func (oFsm *LockStateFsm) enterAdminDoneState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "LockStateFSM", log.Fields{"send notification to core in State": e.FSM.Current(), "device-id": oFsm.deviceID})
+	//use DeviceHandler event notification directly, no need/support to update DeviceEntryState for lock/unlock
+	oFsm.pDeviceHandler.DeviceProcStatusUpdate(ctx, oFsm.requestEvent)
+
+	//let's reset the state machine in order to release all resources now
+	pLockStateAFsm := oFsm.PAdaptFsm
+	if pLockStateAFsm != nil {
+		// obviously calling some FSM event here directly does not work - so trying to decouple it ...
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+				_ = a_pAFsm.PFsm.Event(UniEvReset)
+			}
+		}(pLockStateAFsm)
+	}
+}
+
+func (oFsm *LockStateFsm) enterResettingState(ctx context.Context, e *fsm.Event) {
+	logger.Debugw(ctx, "LockStateFSM resetting", log.Fields{"device-id": oFsm.deviceID})
+	pLockStateAFsm := oFsm.PAdaptFsm
+	if pLockStateAFsm != nil {
+		// abort running message processing
+		fsmAbortMsg := cmn.Message{
+			Type: cmn.TestMsg,
+			Data: cmn.TestMessage{
+				TestMessageVal: cmn.AbortMessageProcessing,
+			},
+		}
+		pLockStateAFsm.CommChan <- fsmAbortMsg
+
+		//try to restart the FSM to 'disabled'
+		// see DownloadedState: decouple event transfer
+		go func(a_pAFsm *cmn.AdapterFsm) {
+			if a_pAFsm != nil && a_pAFsm.PFsm != nil {
+				_ = a_pAFsm.PFsm.Event(UniEvRestart)
+			}
+		}(pLockStateAFsm)
+		oFsm.mutexPLastTxMeInstance.Lock()
+		oFsm.pLastTxMeInstance = nil
+		oFsm.mutexPLastTxMeInstance.Unlock()
+	}
+}
+
+func (oFsm *LockStateFsm) processOmciLockMessages(ctx context.Context) {
+	logger.Debugw(ctx, "Start LockStateFsm Msg processing", log.Fields{"for device-id": oFsm.deviceID})
+loop:
+	for {
+		// case <-ctx.Done():
+		// 	logger.Info(ctx,"MibSync Msg", log.Fields{"Message handling canceled via context for device-id": oFsm.deviceID})
+		// 	break loop
+		message, ok := <-oFsm.PAdaptFsm.CommChan
+		if !ok {
+			logger.Info(ctx, "LockStateFsm Rx Msg - could not read from channel", log.Fields{"device-id": oFsm.deviceID})
+			// but then we have to ensure a restart of the FSM as well - as exceptional procedure
+			_ = oFsm.PAdaptFsm.PFsm.Event(UniEvRestart)
+			break loop
+		}
+		logger.Debugw(ctx, "LockStateFsm Rx Msg", log.Fields{"device-id": oFsm.deviceID})
+
+		switch message.Type {
+		case cmn.TestMsg:
+			msg, _ := message.Data.(cmn.TestMessage)
+			if msg.TestMessageVal == cmn.AbortMessageProcessing {
+				logger.Debugw(ctx, "LockStateFsm abort ProcessMsg", log.Fields{"for device-id": oFsm.deviceID})
+				break loop
+			}
+			logger.Warnw(ctx, "LockStateFsm unknown TestMessage", log.Fields{"device-id": oFsm.deviceID, "MessageVal": msg.TestMessageVal})
+		case cmn.OMCI:
+			msg, _ := message.Data.(cmn.OmciMessage)
+			oFsm.handleOmciLockStateMessage(ctx, msg)
+		default:
+			logger.Warn(ctx, "LockStateFsm Rx unknown message", log.Fields{"device-id": oFsm.deviceID,
+				"message.Type": message.Type})
+		}
+	}
+	logger.Debugw(ctx, "End LockStateFsm Msg processing", log.Fields{"device-id": oFsm.deviceID})
+}
+
+func (oFsm *LockStateFsm) handleOmciLockStateMessage(ctx context.Context, msg cmn.OmciMessage) {
+	logger.Debugw(ctx, "Rx OMCI LockStateFsm Msg", log.Fields{"device-id": oFsm.deviceID,
+		"msgType": msg.OmciMsg.MessageType})
+
+	if msg.OmciMsg.MessageType == omci.SetResponseType {
+		msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeSetResponse)
+		if msgLayer == nil {
+			logger.Errorw(ctx, "LockStateFsm - Omci Msg layer could not be detected for SetResponse",
+				log.Fields{"device-id": oFsm.deviceID})
+			return
+		}
+		msgObj, msgOk := msgLayer.(*omci.SetResponse)
+		if !msgOk {
+			logger.Errorw(ctx, "LockStateFsm - Omci Msg layer could not be assigned for SetResponse",
+				log.Fields{"device-id": oFsm.deviceID})
+			return
+		}
+		logger.Debugw(ctx, "LockStateFsm SetResponse Data", log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj})
+		if msgObj.Result != me.Success {
+			logger.Errorw(ctx, "LockStateFsm - Omci SetResponse Error - later: drive FSM to abort state ?", log.Fields{"Error": msgObj.Result})
+			// possibly force FSM into abort or ignore some errors for some messages? store error for mgmt display?
+			return
+		}
+
+		//should never appear, left here for robustness
+		oFsm.mutexPLastTxMeInstance.RLock()
+		if oFsm.pLastTxMeInstance != nil {
+			// compare comments above for CreateResponse (apply also here ...)
+			if msgObj.EntityClass == oFsm.pLastTxMeInstance.GetClassID() &&
+				msgObj.EntityInstance == oFsm.pLastTxMeInstance.GetEntityID() {
+				//store the created ME into DB //TODO??? obviously the Python code does not store the config ...
+				// if, then something like:
+				//oFsm.pOnuDB.StoreMe(msgObj)
+
+				switch oFsm.pLastTxMeInstance.GetName() {
+				case "OnuG":
+					{ // let the FSM proceed ...
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						_ = oFsm.PAdaptFsm.PFsm.Event(UniEvRxOnugResp)
+					}
+				case "PhysicalPathTerminationPointEthernetUni", "VirtualEthernetInterfacePoint":
+					{ // let the PPTP init proceed by stopping the wait function
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+						oFsm.omciLockResponseReceived <- true
+					}
+				default:
+					{
+						logger.Warnw(ctx, "Unsupported ME name received!",
+							log.Fields{"ME name": oFsm.pLastTxMeInstance.GetName(), "device-id": oFsm.deviceID})
+						oFsm.mutexPLastTxMeInstance.RUnlock()
+					}
+				}
+			} else {
+				oFsm.mutexPLastTxMeInstance.RUnlock()
+				logger.Warnf(ctx, "LockStateFsm - Received SetResponse Data for %s with wrong classID or entityID ",
+					log.Fields{"device-id": oFsm.deviceID, "data-fields": msgObj}, msgObj.EntityClass)
+			}
+		} else {
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+			logger.Errorw(ctx, "pLastTxMeInstance is nil", log.Fields{"device-id": oFsm.deviceID})
+			return
+		}
+	} else {
+		logger.Errorw(ctx, "LockStateFsm - Rx OMCI unhandled MsgType", log.Fields{"omciMsgType": msg.OmciMsg.MessageType})
+		return
+	}
+}
+
+func (oFsm *LockStateFsm) performUniPortAdminSet(ctx context.Context) {
+	var omciAdminState uint8 = 1 //default locked
+	oFsm.mutexAdminState.RLock()
+	if !oFsm.adminState {
+		omciAdminState = 0
+	}
+	oFsm.mutexAdminState.RUnlock()
+	//set PPTPEthUni or VEIP AdminState
+	requestedAttributes := me.AttributeValueMap{"AdministrativeState": omciAdminState}
+
+	for uniNo, uniPort := range *oFsm.pDeviceHandler.GetUniEntityMap() {
+		// only unlock the UniPort in case it is defined for usage (R2.6 limit only one port),
+		// compare also limitation for logical voltha port in dh.EnableUniPortStateUpdate()
+
+		if (omciAdminState == 1) || (1<<uniPort.UniID)&oFsm.pDeviceHandler.GetUniPortMask() == (1<<uniPort.UniID) {
+			var meInstance *me.ManagedEntity
+			if uniPort.PortType == cmn.UniPPTP {
+				logger.Debugw(ctx, "Setting PPTP admin state", log.Fields{
+					"device-id": oFsm.deviceID, "for PortNo": uniNo, "state (0-unlock)": omciAdminState})
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendSetPptpEthUniLS(log.WithSpanFromContext(context.TODO(), ctx),
+					uniPort.EntityID, oFsm.pDeviceHandler.GetOmciTimeout(),
+					true, requestedAttributes, oFsm.PAdaptFsm.CommChan)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "SetPptpEthUniLS set failed, aborting LockStateFsm!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(UniEvReset)
+					return
+				}
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+			} else if uniPort.PortType == cmn.UniVEIP {
+				logger.Debugw(ctx, "Setting VEIP admin state", log.Fields{
+					"device-id": oFsm.deviceID, "for PortNo": uniNo, "state (0-unlock)": omciAdminState})
+				oFsm.mutexPLastTxMeInstance.Lock()
+				meInstance, err := oFsm.pOmciCC.SendSetVeipLS(log.WithSpanFromContext(context.TODO(), ctx),
+					uniPort.EntityID, oFsm.pDeviceHandler.GetOmciTimeout(),
+					true, requestedAttributes, oFsm.PAdaptFsm.CommChan)
+				if err != nil {
+					oFsm.mutexPLastTxMeInstance.Unlock()
+					logger.Errorw(ctx, "SetVeipLS set failed, aborting LockStateFsm!",
+						log.Fields{"device-id": oFsm.deviceID})
+					_ = oFsm.PAdaptFsm.PFsm.Event(UniEvReset)
+					return
+				}
+				oFsm.pLastTxMeInstance = meInstance
+				oFsm.mutexPLastTxMeInstance.Unlock()
+			} else {
+				logger.Warnw(ctx, "Unsupported UniTP type - skip",
+					log.Fields{"device-id": oFsm.deviceID, "Port": uniNo})
+				continue
+			}
+			oFsm.mutexPLastTxMeInstance.RLock()
+			if oFsm.pLastTxMeInstance == nil {
+				oFsm.mutexPLastTxMeInstance.RUnlock()
+				logger.Errorw(ctx, "could not send PortAdmin OMCI message from LockStateFsm", log.Fields{
+					"device-id": oFsm.deviceID, "Port": uniNo})
+				//some more sophisticated approach is possible, e.g. repeating once, by now let's reset the state machine in order to release all resources now
+				_ = oFsm.PAdaptFsm.PFsm.Event(UniEvReset)
+				return
+			}
+			oFsm.mutexPLastTxMeInstance.RUnlock()
+
+			//verify response
+			err := oFsm.waitforOmciResponse(ctx, meInstance)
+			if err != nil {
+				logger.Errorw(ctx, "UniTP Admin State set failed, aborting LockState set!",
+					log.Fields{"device-id": oFsm.deviceID, "Port": uniNo})
+				_ = oFsm.PAdaptFsm.PFsm.Event(UniEvReset)
+				return
+			}
+		}
+	} //for all UNI ports
+	// if Config has been done for all UNI related instances let the FSM proceed
+	// while we did not check here, if there is some port at all - !?
+	logger.Infow(ctx, "UniTP adminState loop finished", log.Fields{"device-id": oFsm.deviceID})
+	_ = oFsm.PAdaptFsm.PFsm.Event(UniEvRxUnisResp)
+}
+
+func (oFsm *LockStateFsm) waitforOmciResponse(ctx context.Context, apMeInstance *me.ManagedEntity) error {
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	// case <-ctx.Done():
+	// 		logger.Infow(ctx,"LockState-bridge-init message reception canceled", log.Fields{"for device-id": oFsm.deviceID})
+	case <-time.After(oFsm.pOmciCC.GetMaxOmciTimeoutWithRetries() * time.Second): //3s was detected to be to less in 8*8 bbsim test with debug Info/Debug
+		logger.Warnw(ctx, "lockStateFSM uni-set timeout", log.Fields{"for device-id": oFsm.deviceID})
+		return fmt.Errorf("lockStateFsm uni-set timeout for device-id %s", oFsm.deviceID)
+	case success := <-oFsm.omciLockResponseReceived:
+		if success {
+			logger.Debug(ctx, "LockStateFSM uni-set response received")
+			return nil
+		}
+		// should not happen so far
+		logger.Warnw(ctx, "lockStateFSM uni-set response error", log.Fields{"for device-id": oFsm.deviceID})
+		return fmt.Errorf("lockStateFsm uni-set responseError for device-id %s", oFsm.deviceID)
+	}
+}
diff --git a/internal/pkg/uniprt/uniportstatus.go b/internal/pkg/uniprt/uniportstatus.go
new file mode 100755
index 0000000..8f8dedd
--- /dev/null
+++ b/internal/pkg/uniprt/uniportstatus.go
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2020-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 uniprt provides the utilities for uni port configuration
+package uniprt
+
+import (
+	"context"
+	"time"
+
+	"github.com/opencord/omci-lib-go"
+	me "github.com/opencord/omci-lib-go/generated"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+	cmn "github.com/opencord/voltha-openonu-adapter-go/internal/pkg/common"
+	"github.com/opencord/voltha-protos/v5/go/extension"
+)
+
+const (
+	uniStatusTimeout = 3
+	adminState       = "AdministrativeState"
+	operationalState = "OperationalState"
+	configInd        = "ConfigurationInd"
+)
+
+//UniPortStatus implements methods to get uni port status info
+type UniPortStatus struct {
+	pDeviceHandler    cmn.IdeviceHandler
+	pOmiCC            *cmn.OmciCC
+	omciRespChn       chan cmn.Message
+	pLastTxMeInstance *me.ManagedEntity
+}
+
+//NewUniPortStatus creates a new instance of UniPortStatus
+func NewUniPortStatus(apDeviceHandler cmn.IdeviceHandler, apOmicc *cmn.OmciCC) *UniPortStatus {
+	return &UniPortStatus{
+		pDeviceHandler: apDeviceHandler,
+		pOmiCC:         apOmicc,
+		omciRespChn:    make(chan cmn.Message),
+	}
+
+}
+
+// GetUniPortStatus - TODO: add comment
+func (portStatus *UniPortStatus) GetUniPortStatus(ctx context.Context, uniIdx uint32) *extension.SingleGetValueResponse {
+	for _, uniPort := range *portStatus.pDeviceHandler.GetUniEntityMap() {
+
+		if uniPort.UniID == uint8(uniIdx) && uniPort.PortType == cmn.UniPPTP {
+
+			requestedAttributes := me.AttributeValueMap{adminState: 0, operationalState: 0, configInd: 0}
+			// Note: No reference to fetch the OMCI timeout configuration value, so hard code it to 10s
+			meInstance, err := portStatus.pOmiCC.SendGetMe(ctx, me.PhysicalPathTerminationPointEthernetUniClassID, uniPort.EntityID, requestedAttributes, 10, true, portStatus.omciRespChn)
+			if err != nil {
+				return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+			}
+			if meInstance != nil {
+				portStatus.pLastTxMeInstance = meInstance
+
+				//verify response
+				return portStatus.waitforGetUniPortStatus(ctx, meInstance)
+			}
+		}
+	}
+	logger.Errorw(ctx, "GetUniPortStatus uniIdx is not valid", log.Fields{"uniIdx": uniIdx})
+	return PostUniStatusErrResponse(extension.GetValueResponse_INVALID_PORT_TYPE)
+}
+
+func (portStatus *UniPortStatus) waitforGetUniPortStatus(ctx context.Context, apMeInstance *me.ManagedEntity) *extension.SingleGetValueResponse {
+
+	select {
+	// maybe be also some outside cancel (but no context modeled for the moment ...)
+	case <-ctx.Done():
+		logger.Errorf(ctx, "waitforGetUniPortStatus Context done")
+		return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+	case <-time.After(uniStatusTimeout * time.Second):
+		logger.Errorf(ctx, "waitforGetUniPortStatus  timeout")
+		return PostUniStatusErrResponse(extension.GetValueResponse_TIMEOUT)
+
+	case omciMsg := <-portStatus.omciRespChn:
+		if omciMsg.Type != cmn.OMCI {
+			return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+		}
+		msg, _ := omciMsg.Data.(cmn.OmciMessage)
+		return portStatus.processGetUnitStatusResp(ctx, msg)
+	}
+
+}
+
+func (portStatus *UniPortStatus) processGetUnitStatusResp(ctx context.Context, msg cmn.OmciMessage) *extension.SingleGetValueResponse {
+	logger.Debugw(ctx, "processGetUniStatusResp:", log.Fields{"msg.Omci.MessageType": msg.OmciMsg.MessageType,
+		"msg.OmciMsg.TransactionID": msg.OmciMsg.TransactionID, "DeviceIdentfier": msg.OmciMsg.DeviceIdentifier})
+
+	if msg.OmciMsg.MessageType != omci.GetResponseType {
+		logger.Debugw(ctx, "processGetUniStatusResp error", log.Fields{"incorrect RespType": msg.OmciMsg.MessageType,
+			"expected": omci.GetResponseType})
+		return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+	}
+
+	msgLayer := (*msg.OmciPacket).Layer(omci.LayerTypeGetResponse)
+	if msgLayer == nil {
+		logger.Errorf(ctx, "processGetUniStatusResp omci Msg layer not found - ")
+		return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+
+	}
+	msgObj, msgOk := msgLayer.(*omci.GetResponse)
+	if !msgOk {
+		logger.Errorf(ctx, "processGetUniStatusResp omci msgObj layer could not be found ")
+		return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+
+	}
+
+	if msgObj.Result != me.Success {
+		return PostUniStatusErrResponse(extension.GetValueResponse_INTERNAL_ERROR)
+	}
+	meAttributes := msgObj.Attributes
+
+	singleValResp := extension.SingleGetValueResponse{
+		Response: &extension.GetValueResponse{
+			Status: extension.GetValueResponse_OK,
+			Response: &extension.GetValueResponse_UniInfo{
+				UniInfo: &extension.GetOnuUniInfoResponse{},
+			},
+		},
+	}
+	if meAttributes[operationalState].(uint8) == 0 {
+		singleValResp.Response.GetUniInfo().OperState = extension.GetOnuUniInfoResponse_ENABLED
+	} else if meAttributes[operationalState].(uint8) == 1 {
+		singleValResp.Response.GetUniInfo().OperState = extension.GetOnuUniInfoResponse_DISABLED
+	} else {
+		singleValResp.Response.GetUniInfo().OperState = extension.GetOnuUniInfoResponse_OPERSTATE_UNDEFINED
+	}
+
+	if meAttributes[adminState].(uint8) == 0 {
+		singleValResp.Response.GetUniInfo().AdmState = extension.GetOnuUniInfoResponse_UNLOCKED
+	} else if meAttributes[adminState].(uint8) == 1 {
+		singleValResp.Response.GetUniInfo().AdmState = extension.GetOnuUniInfoResponse_LOCKED
+	} else {
+		singleValResp.Response.GetUniInfo().AdmState = extension.GetOnuUniInfoResponse_ADMSTATE_UNDEFINED
+	}
+	configIndMap := map[uint8]extension.GetOnuUniInfoResponse_ConfigurationInd{
+		0:  0,
+		1:  extension.GetOnuUniInfoResponse_TEN_BASE_T_FDX,
+		2:  extension.GetOnuUniInfoResponse_HUNDRED_BASE_T_FDX,
+		3:  extension.GetOnuUniInfoResponse_GIGABIT_ETHERNET_FDX,
+		4:  extension.GetOnuUniInfoResponse_TEN_G_ETHERNET_FDX,
+		17: extension.GetOnuUniInfoResponse_TEN_BASE_T_HDX,
+		18: extension.GetOnuUniInfoResponse_HUNDRED_BASE_T_HDX,
+		19: extension.GetOnuUniInfoResponse_GIGABIT_ETHERNET_HDX,
+	}
+	configInd := meAttributes[configInd].(uint8)
+	singleValResp.Response.GetUniInfo().ConfigInd = configIndMap[configInd]
+	return &singleValResp
+}
+
+// PostUniStatusErrResponse - TODO: add comment
+func PostUniStatusErrResponse(reason extension.GetValueResponse_ErrorReason) *extension.SingleGetValueResponse {
+	return &extension.SingleGetValueResponse{
+		Response: &extension.GetValueResponse{
+			Status:    extension.GetValueResponse_ERROR,
+			ErrReason: reason,
+		},
+	}
+}