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

implemented

fix Jenkins make errors
fix merge conflicts
address review comments

Change-Id: Ia2e95afb33ce55054afa1fcbd9beb6ada62dd764
diff --git a/.gitignore b/.gitignore
index 203f81b..32fcb27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
-bbsim
+api/swagger/bbsim.swagger.json
+/bbsim
 protos/openolt.pb.go
 coverage.txt
diff --git a/Makefile b/Makefile
index d73ab70..e2c0242 100644
--- a/Makefile
+++ b/Makefile
@@ -19,16 +19,17 @@
 .PHONY: dep test clean docker
 
 prereq:
-	go get -u google.golang.org/grpc
+	go get -v google.golang.org/grpc
 	go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
 	go get -v github.com/golang/protobuf/protoc-gen-go
+	go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
 	go get -v github.com/google/gopacket
 	go get -v github.com/opencord/omci-sim
 
-bbsim: prereq protos/openolt.pb.go dep
+bbsim: prereq protos/openolt.pb.go bbsimapi dep
 	go build -i -v -o $@
 
-dep: protos/openolt.pb.go
+dep: protos/openolt.pb.go bbsimapi
 	go get -v -d ./...
 
 protos/openolt.pb.go: openolt.proto
@@ -38,6 +39,23 @@
 	--go_out=plugins=grpc:protos/ \
 	$<
 
+bbsimapi: api/bbsim.proto
+	@protoc -I ./api \
+	-I${GOPATH}/src \
+	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
+	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway \
+	--go_out=plugins=grpc:api/ \
+	--grpc-gateway_out=logtostderr=true,allow_delete_body=true:api/ \
+	bbsim.proto
+
+swagger:						 ## Generate swagger documentation for BBsim API
+	@protoc -I ./api \
+	-I${GOPATH}/src \
+	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
+	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway \
+	--swagger_out=logtostderr=true,allow_delete_body=true:api/swagger/ \
+	bbsim.proto
+
 test:
 	go test -v ./...
 	go test -v ./... -cover
@@ -52,7 +70,12 @@
 	gometalinter --vendor --exclude ../../golang.org --skip protos --sort path --sort line ./...
 
 clean:
-	rm -f bbsim openolt/openolt.pb.go
+	@rm -vf bbsim \
+			protos/openolt.pb.go \
+			api/bbsim.pb.go \
+	        api/bbsim.pb.gw.go \
+	        api/swagger/*.json
 
 docker:
 	docker build -t ${REGISTRY}voltha/voltha-bbsim:${DOCKERTAG} .
+	docker save voltha/voltha-bbsim:${DOCKERTAG} -o voltha-bbsim_${DOCKERTAG}.tgz
diff --git a/README.md b/README.md
index 45ece4c..a3f3fd7 100644
--- a/README.md
+++ b/README.md
@@ -113,4 +113,10 @@
       DHCP Server IP Address (default "182.21.0.128")
   -v int
       Interval each ONU Discovery Indication (ms) (default 1000)
+  -ia bool
+      Interactive activation of ONUs: if true ONUs must be activated explicitly using the management API (no ONUs are activated at startup) (default false)
+  -grpc int
+      Management API gRPC port (default 50061)
+  -rest int
+      Management API rest port (default 50062)
 ```
diff --git a/api/README.md b/api/README.md
new file mode 100644
index 0000000..02f9125
--- /dev/null
+++ b/api/README.md
@@ -0,0 +1,65 @@
+This directory contains protobuf files for the BBSim control API.
+
+# Examples
+
+```
+# start BBSim 
+
+    ./bbsim -i 8 -n 16 -ia false
+
+# get OLT status
+
+    curl -X GET http://127.0.0.1:50062/v1/olt | jq
+
+# get status of the OLT's PON ports
+
+    curl -X GET http://127.0.0.1:50062/v1/olt/ports/pon | jq
+
+# get status of all active ONUs
+
+    curl -X GET http://127.0.0.1:50062/v1/olt/onus | jq
+
+# get the status of the ONU with ONU-ID 2 (on PON port 1)
+
+    curl -X GET "http://127.0.0.1:50062/v1/olt/onus?onu_id=2&pon_port_id=1" | jq
+
+# activate single ONU with a given serial number:
+
+    curl -X POST http://127.0.0.1:50062/v1/olt/ports/1/onus/BBSM00000201 | jq
+
+# get the status of an ONU using its serial number:
+
+    curl -X GET http://127.0.0.1:50062/v1/olt/onus/BBSM00000201 | jq
+
+# deactivate ONU using its serial number:
+
+    curl -X DELETE  http://127.0.0.1:50062/v1/olt/onus/BBSM00000201 | jq
+
+# or 
+
+    curl -X DELETE  http://127.0.0.1:50062/v1/olt/onus?onu_serial=BBSM00000201 | jq
+```
+
+
+# Activate multiple ONUs
+```
+cat <<EOF > onus_request.json
+{
+  "onus": [
+    {
+      "pon_port_id": 1,
+      "onu_serial": "BBSIMONU0001"
+    },
+    {
+      "pon_port_id": 2,
+      "onu_serial": "BBSIMONU0002"
+    }
+  ]
+}
+EOF
+
+
+    curl -H  "Content-Type: application/json" -X POST "http://127.0.0.1:50062/v1/olt/onus" -d @onus_request.json
+
+
+```
diff --git a/api/bbsim.proto b/api/bbsim.proto
new file mode 100644
index 0000000..494f947
--- /dev/null
+++ b/api/bbsim.proto
@@ -0,0 +1,196 @@
+// Copyright (c) 2018 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.
+
+syntax = "proto3";
+package bbsim.api.v1;
+
+import "google/api/annotations.proto";
+import "protoc-gen-swagger/options/annotations.proto";
+
+option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
+    info: {
+	title: "BBSim API";
+	version: "1.0";
+    };
+    schemes: HTTP;
+    consumes: "application/json";
+    produces: "application/json";
+    responses: {
+	key: "404";
+	value: {
+	    description: "Returned when the resource does not exist.";
+	    schema: {
+		    json_schema: {
+		      type: STRING;
+		}
+	    }
+	}
+    }
+};
+
+// OLT information
+message OLTInfo {
+    int64   olt_id = 1;
+    string  olt_serial = 2;
+    string  olt_ip = 3;
+    string  olt_state = 4;
+    string  olt_vendor = 5;
+}
+
+// ONU information
+message ONUInfo {
+    uint32 onu_id = 1;
+    uint32 pon_port_id = 2;
+    // ONU serial number
+    string onu_serial = 3;
+    // ONU oper state
+    string oper_state = 4;
+    // ONU internal state
+    string onu_state = 5;
+}
+
+// Bulk ONU operations
+message ONUs {
+    repeated ONUInfo onus = 1;
+}
+
+message ONURequest {
+    ONUInfo onu = 1;
+    ONUs onus_batch = 2;
+}
+
+// Port information
+message PortInfo {
+    string port_type = 1;
+    uint32 port_id = 2;
+    int32  pon_port_max_onus = 3;
+    uint32 pon_port_active_onus = 4;
+    string port_state = 5;
+    string alarm_state = 6;
+}
+
+// Bulk port information
+message Ports {
+    repeated PortInfo ports = 1;
+}
+
+// BBSim status
+message OLTStatusResponse {
+    OLTInfo olt = 1;
+    repeated PortInfo ports = 2;
+}
+
+// BBSim response message
+message BBSimResponse {
+    string status_msg = 1;
+}
+
+// ONU alarm request
+message ONUAlarmRequest {
+    // ONU serial number
+    string onu_serial = 1; 
+    // Alarm types are:
+    //   "signaldegrade"
+    //   "lossofomcichannel"
+    //   "lossofploam"
+    string alarm_type = 2;
+    // "on"/"off" indicates raised or cleared alarm 
+    string status = 3;                 }
+
+// OLT alarm request
+message OLTAlarmRequest {
+    uint32 port_id = 1;
+    string port_type = 2;
+    string status = 3;
+}
+
+// Device action
+message DeviceAction {
+    string device_type = 1;             // ONU or OLT
+    string device_serial_number = 2;    // Device serial number
+    string device_action = 3;           // soft or hard reboot
+}
+
+message Empty {}
+
+service BBSimService {
+
+    // Get current status of OLT
+    rpc OLTStatus(Empty) returns (OLTStatusResponse) {
+        option (google.api.http) = {
+            get : "/v1/olt"
+            additional_bindings {get : "/v1/olt/status"}
+        };
+    }
+
+    // Get status of a PON/NNI port
+    rpc PortStatus(PortInfo) returns (Ports) {
+        option (google.api.http) = {
+            get : "/v1/olt/ports/{port_type}"
+        };
+    }
+
+    // Get status of all or specific ONUs
+    rpc ONUStatus(ONURequest) returns (ONUs) {
+        option (google.api.http) = {
+            get : "/v1/olt/onus"
+            additional_bindings { get : "/v1/olt/ports/{onu.pon_port_id}/onus" }
+            additional_bindings { get : "/v1/olt/onus/{onu.onu_serial}" }
+        };
+    }
+
+    // Single/bulk activate ONU(s) for specific PON port(s)
+    rpc ONUActivate(ONURequest) returns (BBSimResponse) {
+        option (google.api.http) = {
+            post : "/v1/olt/onus"
+            body: "onus_batch"
+            additional_bindings { post : "/v1/olt/ports/{onu.pon_port_id}/onus" }
+            additional_bindings { post : "/v1/olt/ports/{onu.pon_port_id}/onus/{onu.onu_serial}" }
+        };
+    }
+    
+    // Deactivate ONU(s) for specific PON port(s) specified by  
+    // a given onu_serial, onu_id, or pon_port_id
+    rpc ONUDeactivate(ONURequest) returns (BBSimResponse) {
+        option (google.api.http) = {
+            delete : "/v1/olt/onus"
+            body: "onus_batch"
+            additional_bindings { delete: "/v1/olt/onus/{onu.onu_serial}" }
+            additional_bindings { delete: "/v1/olt/ports/{onu.pon_port_id}/onus" }
+            additional_bindings { delete: "/v1/olt/ports/{onu.pon_port_id}/onus/{onu.onu_id}" }
+        };
+    }
+
+    // Generate ONU related alarms
+    rpc GenerateONUAlarm(ONUAlarmRequest) returns (BBSimResponse) {
+        option (google.api.http) = {
+            post : "/v1/olt/onus/{onu_serial}/alarms/{alarm_type}/{status}"
+        };
+    }
+
+    // Generate OLT related alarms
+    rpc GenerateOLTAlarm(OLTAlarmRequest) returns (BBSimResponse) {
+        option (google.api.http) = {
+            post : "/v1/olt/ports/{port_type}/{port_id}/alarms/los/{status}"
+        };
+    }
+
+    // Perform actions on OLT/ONU devices (e.g. reboot)
+    rpc PerformDeviceAction(DeviceAction) returns (BBSimResponse) {
+        option (google.api.http) = {
+            patch: "/v1/{device_type}/reboot"
+            additional_bindings { patch : "/v1/olt/{device_type}/{device_serial_number}/reboot/{device_action}"}
+        };
+    }
+}
diff --git a/api/swagger/.gitignore b/api/swagger/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/api/swagger/.gitignore
diff --git a/common/logger/logger.go b/common/logger/logger.go
index e3d1e90..cb7229f 100644
--- a/common/logger/logger.go
+++ b/common/logger/logger.go
@@ -26,13 +26,14 @@
 	myLogger *log.Entry
 )
 
+// Setup logger
 func Setup(kafkaBroker string, level string) {
 
 	logger := log.New()
 	//logger.SetReportCaller(true)
 	myLogger = logger.WithField("topics", []string{"bbsim.log"})
 
-	var logLevel log.Level = log.DebugLevel
+	var logLevel = log.DebugLevel
 	switch level{
 		case "TRACE":
 			logLevel = log.TraceLevel
@@ -75,38 +76,47 @@
 	myLogger.WithField("kafkaBroker", kafkaBroker).Debug("Logger setup done")
 }
 
+// GetLogger return logger instance
 func GetLogger() *log.Entry {
 	return myLogger
 }
 
+// WithField logs message with specified field
 func WithField(key string, value interface{}) *log.Entry {
 	return myLogger.WithField(key, value)
 }
 
+// WithFields logs message with multiple fields
 func WithFields(fields log.Fields) *log.Entry {
 	return myLogger.WithFields(fields)
 }
 
+// Panic logs with log level panic
 func Panic(msg string, args ...interface{}) {
 	myLogger.Panicf(msg, args...)
 }
 
+// Fatal logs woth loge level fatal
 func Fatal(msg string, args ...interface{}) {
 	myLogger.Fatalf(msg, args...)
 }
 
+// Error logs with log level error
 func Error(msg string, args ...interface{}) {
 	myLogger.Errorf(msg, args...)
 }
 
+// Warn logs with log level warn
 func Warn(msg string, args ...interface{}) {
 	myLogger.Warnf(msg, args...)
 }
 
+// Info logs with log level info
 func Info(msg string, args ...interface{}) {
 	myLogger.Infof(msg, args...)
 }
 
+// Debug logs with log level debug
 func Debug(msg string, args ...interface{}) {
 	myLogger.Debugf(msg, args...)
 }
diff --git a/common/utils/utils.go b/common/utils/utils.go
index f36cae4..3bfd0de 100644
--- a/common/utils/utils.go
+++ b/common/utils/utils.go
@@ -17,20 +17,28 @@
 package utils
 
 import (
-	"fmt"
+	"strconv"
 
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
-
 	"gerrit.opencord.org/voltha-bbsim/device"
 	log "github.com/sirupsen/logrus"
 )
 
-func OnuToSn(onu *device.Onu) string {
-	// FIXME
-	// see https://github.com/opencord/voltha/blob/master/voltha/adapters/openolt/openolt_device.py#L929-L943
-	return string(onu.SerialNumber.VendorId) + "00000" + fmt.Sprint(onu.IntfID) + "0" + fmt.Sprintf("%x", onu.OnuID-1)
+// ConvB2S converts byte array to string
+func ConvB2S(b []byte) string {
+	s := ""
+	for _, i := range b {
+		s = s + strconv.FormatInt(int64(i/16), 16) + strconv.FormatInt(int64(i%16), 16)
+	}
+	return s
 }
 
+// OnuToSn returns serial number in string format for given ONU
+func OnuToSn(onu *device.Onu) string {
+	return string(onu.SerialNumber.VendorId) + ConvB2S(onu.SerialNumber.VendorSpecific)
+}
+
+// LoggerWithOnu method logs ONU fields
 func LoggerWithOnu(onu *device.Onu) *log.Entry {
 
 	if onu == nil {
@@ -40,8 +48,8 @@
 
 	return logger.GetLogger().WithFields(log.Fields{
 		"serial_number": OnuToSn(onu),
-		"interfaceId":   onu.IntfID,
-		"onuId":         onu.OnuID,
-		"oltId":         onu.OltID,
+		"interfaceID":   onu.IntfID,
+		"onuID":         onu.OnuID,
+		"oltID":         onu.OltID,
 	})
 }
diff --git a/core/alarms.go b/core/alarms.go
new file mode 100644
index 0000000..5c7d287
--- /dev/null
+++ b/core/alarms.go
@@ -0,0 +1,291 @@
+/*
+ * 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 (
+	"strconv"
+
+	pb "gerrit.opencord.org/voltha-bbsim/api"
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	"gerrit.opencord.org/voltha-bbsim/device"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+const (
+	// OnuLossOfPloam is state on onu los
+	OnuLossOfPloam = "lossofploam"
+	// OnuLossOfOmciChannel is the state on omci channel loss alarm
+	OnuLossOfOmciChannel = "lossofomcichannel"
+	// OnuSignalDegrade is the state on signal degrade alarm
+	OnuSignalDegrade = "signaldegrade"
+	// AlarmOn is for raising the alarm
+	AlarmOn = "on"
+	// AlarmOff is for clearing the alarm
+	AlarmOff = "off"
+)
+
+func (s *Server) handleOnuAlarm(in *pb.ONUAlarmRequest) (*pb.BBSimResponse, error) {
+	value, ok := s.SNmap.Load(in.OnuSerial)
+	onu := value.(*device.Onu)
+	if !ok {
+		return &pb.BBSimResponse{}, status.Errorf(codes.NotFound, "no active or discovered onu found with serial number "+in.OnuSerial)
+	}
+
+	if (onu.InternalState == device.ONU_LOS_RAISED || onu.InternalState == device.ONU_LOS_ON_OLT_PON_LOS) &&
+		(in.AlarmType != OnuLossOfPloam) {
+		return &pb.BBSimResponse{}, status.Errorf(codes.Aborted, in.OnuSerial+" is not reachable, can not send onu alarm")
+	}
+
+	if s.Olt.PonIntfs[onu.IntfID].AlarmState == device.PonLosRaised && (in.AlarmType != OnuLossOfPloam) {
+		// Don't send onu alarm as OLT-PON is down
+		return &pb.BBSimResponse{}, status.Errorf(codes.Aborted, "pon-port down, can not send onu alarm")
+	}
+	switch in.AlarmType {
+	case OnuLossOfOmciChannel:
+		Ind := formulateLossOfOmciChannelAlarm(in.Status, onu)
+		if in.Status == AlarmOn {
+			onu.UpdateIntState(device.ONU_OMCI_CHANNEL_LOS_RAISED)
+		} else {
+			onu.UpdateIntState(device.ONU_ACTIVE)
+		}
+		s.alarmCh <- Ind
+		return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+
+	case OnuSignalDegrade:
+		Ind := formulateSignalDegradeAlarm(in.Status, onu)
+		s.alarmCh <- Ind
+		return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+
+	case OnuLossOfPloam:
+		if in.Status == AlarmOn {
+			onu.UpdateIntState(device.ONU_LOS_RAISED)
+			device.UpdateOnusOpStatus(onu.IntfID, onu, "down")
+		} else {
+			onu.UpdateIntState(device.ONU_ACTIVE)
+			device.UpdateOnusOpStatus(onu.IntfID, onu, "up")
+			// TODO is it required to check onu state?
+			err := sendOnuDiscInd(*s.EnableServer, onu)
+			if err != nil {
+				logger.Error("Error: %v", err.Error())
+			}
+		}
+		Ind := formulateLossOfPLOAM(in.Status, onu)
+		s.alarmCh <- Ind
+		er := sendOnuInd(*s.EnableServer, onu, 0, onu.OperState, "up")
+		if er != nil {
+			logger.Debug(er.Error())
+		}
+
+		resp, err := s.checkAndSendOltPonLos(in.OnuSerial, in.Status, device.IntfPon) // Send olt los if all the onus attached to a pon-port shows los
+		if err != nil {
+			return resp, err
+		}
+		return resp, nil
+
+	default:
+		logger.Debug("Unhandled alarm type")
+		return &pb.BBSimResponse{}, status.Errorf(codes.Unimplemented, "Unhandled alarm type")
+	}
+
+}
+
+func (s *Server) handleOltAlarm(in *pb.OLTAlarmRequest) (*pb.BBSimResponse, error) {
+	switch in.PortType {
+	case device.IntfNni:
+
+		if !s.isNniIntfPresentInOlt(in.PortId) {
+			return &pb.BBSimResponse{}, status.Errorf(codes.NotFound, strconv.Itoa(int(in.PortId))+" NNI not present in olt")
+		}
+
+		Ind := formulateOLTLOSAlarm(in.Status, in.PortId, device.IntfNni)
+		s.alarmCh <- Ind
+		s.setNNIPortState(in.PortId, in.Status)
+
+	case device.IntfPon:
+		if !s.isPonIntfPresentInOlt(in.PortId) {
+			return &pb.BBSimResponse{}, status.Errorf(codes.NotFound, strconv.Itoa(int(in.PortId))+" PON not present in olt")
+		}
+		Ind := formulateOLTLOSAlarm(in.Status, in.PortId, in.PortType)
+		s.alarmCh <- Ind
+		onusOperstat := s.setPONPortState(in.PortId, in.Status)
+		for _, onu := range s.Onumap[in.PortId] {
+			if onu.InternalState == device.ONU_LOS_RAISED || onu.InternalState == device.ONU_FREE {
+				continue // Skip for onus which have independently raised onu los
+			}
+
+			er := sendOnuInd(*s.EnableServer, onu, 0, onusOperstat, "up")
+			if er != nil {
+				logger.Debug(er.Error())
+			}
+			s.sendOnuLosOnOltPonLos(onu, in.Status)
+		}
+	default:
+		return &pb.BBSimResponse{}, status.Errorf(codes.Internal, "invalid interface type provided")
+	}
+
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+func (s *Server) setNNIPortState(portID uint32, alarmstatus string) {
+	switch alarmstatus {
+	case AlarmOn:
+		s.Olt.UpdateNniPortState(portID, device.NniLosRaised, "down")
+
+	case AlarmOff:
+		s.Olt.UpdateNniPortState(portID, device.NniLosCleared, "up")
+	}
+}
+
+func (s *Server) setPONPortState(portID uint32, alarmstatus string) string {
+	switch alarmstatus {
+	case AlarmOn:
+		s.Olt.UpdatePonPortState(portID, device.PonLosRaised, "down")
+		return "down"
+
+	case AlarmOff:
+		s.Olt.UpdatePonPortState(portID, device.PonLosCleared, "up")
+		return "up"
+	}
+	return ""
+}
+
+func (s *Server) sendOnuLosOnOltPonLos(onu *device.Onu, status string) {
+	var internalState device.DeviceState
+
+	if status == AlarmOn {
+		internalState = device.ONU_LOS_ON_OLT_PON_LOS
+	} else if status == AlarmOff {
+		internalState = device.ONU_ACTIVE
+	}
+
+	Ind := formulateLossOfPLOAM(status, onu)
+	onu.UpdateIntState(internalState)
+
+	// update onus slice on alarm off
+	if status == "off" {
+		err := sendOnuDiscInd(*s.EnableServer, onu)
+		if err != nil {
+			logger.Error(err.Error())
+		}
+	}
+
+	s.alarmCh <- Ind
+}
+
+func formulateLossOfOmciChannelAlarm(status string, onu *device.Onu) *openolt.Indication {
+	logger.Debug("formulateLossofOmciChannelAlarm() invoked")
+
+	alarmIndication := &openolt.AlarmIndication_OnuLossOmciInd{
+		OnuLossOmciInd: &openolt.OnuLossOfOmciChannelIndication{
+			IntfId: onu.IntfID,
+			OnuId:  onu.OnuID,
+			Status: status,
+		},
+	}
+
+	alarmind := &openolt.AlarmIndication{
+		Data: alarmIndication,
+	}
+
+	msg := &openolt.Indication_AlarmInd{AlarmInd: alarmind}
+	Ind := &openolt.Indication{Data: msg}
+	return Ind
+}
+
+func formulateSignalDegradeAlarm(status string, onu *device.Onu) *openolt.Indication {
+	logger.Debug("formulateSignalDegrade() invoked")
+	alarmIndication := &openolt.AlarmIndication_OnuSignalDegradeInd{
+		OnuSignalDegradeInd: &openolt.OnuSignalDegradeIndication{
+			IntfId:              onu.IntfID,
+			OnuId:               onu.OnuID,
+			Status:              status,
+			InverseBitErrorRate: 0,
+		},
+	}
+	alarmind := &openolt.AlarmIndication{
+		Data: alarmIndication,
+	}
+	msg := &openolt.Indication_AlarmInd{AlarmInd: alarmind}
+	Ind := &openolt.Indication{Data: msg}
+	return Ind
+}
+
+func formulateLossOfPLOAM(status string, onu *device.Onu) *openolt.Indication {
+	logger.Debug("formulateLossOfPLOAM() invoked")
+
+	alarmIndication := &openolt.AlarmIndication_OnuAlarmInd{OnuAlarmInd: &openolt.OnuAlarmIndication{
+		IntfId:             onu.IntfID,
+		OnuId:              onu.OnuID,
+		LosStatus:          status,
+		LobStatus:          status,
+		LopcMissStatus:     status,
+		LopcMicErrorStatus: status,
+	}}
+
+	alarmind := &openolt.AlarmIndication{Data: alarmIndication}
+	msg := &openolt.Indication_AlarmInd{AlarmInd: alarmind}
+	Ind := &openolt.Indication{Data: msg}
+	return Ind
+}
+
+func formulateOLTLOSAlarm(status string, PortID uint32, intfType string) *openolt.Indication {
+	intfID := interfaceIDToPortNo(PortID, intfType)
+
+	alarmIndication := &openolt.AlarmIndication_LosInd{LosInd: &openolt.LosIndication{
+		IntfId: intfID,
+		Status: status,
+	}}
+
+	alarmind := &openolt.AlarmIndication{Data: alarmIndication}
+	msg := &openolt.Indication_AlarmInd{AlarmInd: alarmind}
+	Ind := &openolt.Indication{Data: msg}
+	return Ind
+}
+
+func (s *Server) checkAndSendOltPonLos(serial string, status string, intfType string) (*pb.BBSimResponse, error) {
+	value, _ := s.SNmap.Load(serial)
+	onu := value.(*device.Onu)
+	if s.getNoOfActiveOnuByPortID(onu.IntfID) == 0 {
+		logger.Warn("Warning: Sending OLT-LOS, as all onus on pon-port %v raised los", onu.IntfID)
+		request := &pb.OLTAlarmRequest{PortId: onu.IntfID, Status: AlarmOn, PortType: device.IntfPon}
+		resp, err := s.handleOltAlarm(request)
+		return resp, err
+	}
+	if s.Olt.PonIntfs[onu.IntfID].AlarmState == device.PonLosRaised && status == AlarmOff {
+		s.setPONPortState(onu.IntfID, status)
+		Ind := formulateOLTLOSAlarm(status, onu.IntfID, intfType)
+		s.alarmCh <- Ind
+	}
+
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+func interfaceIDToPortNo(intfid uint32, intfType string) uint32 {
+	// Converts interface-id to port-numbers that can be understood by the voltha
+	if intfType == device.IntfNni {
+		// nni at voltha starts with 65536
+		// nni = 65536 + interface_id
+		return 0x1<<16 + intfid
+	} else if intfType == device.IntfPon {
+		// pon = 536,870,912 + interface_id
+		return (0x2 << 28) + intfid // In openolt code, stats_collection.cc line number 196, pon starts from 0
+		// In bbsim, pon starts from 1
+	}
+	return 0
+}
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
+}
diff --git a/core/api_service.go b/core/api_service.go
new file mode 100644
index 0000000..a826594
--- /dev/null
+++ b/core/api_service.go
@@ -0,0 +1,247 @@
+/*
+ * 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 (
+	"context"
+	"net"
+	"net/http"
+	"sync"
+
+	"gerrit.opencord.org/voltha-bbsim/device"
+
+	pb "gerrit.opencord.org/voltha-bbsim/api"
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+)
+
+// Response Constants
+const (
+	RequestAccepted = "API request accepted"
+	OLTNotEnabled   = "OLT not enabled"
+	RequestFailed   = "API request failed"
+	SoftReboot      = "soft-reboot"
+	HardReboot      = "hard-reboot"
+	DeviceTypeOlt   = "olt"
+	DeviceTypeOnu   = "onu"
+)
+
+// OLTStatus method returns OLT status.
+func (s *Server) OLTStatus(ctx context.Context, in *pb.Empty) (*pb.OLTStatusResponse, error) {
+	logger.Debug("OLTStatus request received")
+	oltInfo := &pb.OLTStatusResponse{
+		Olt: &pb.OLTInfo{
+			OltId:     int64(s.Olt.ID),
+			OltSerial: s.Olt.SerialNumber,
+			OltIp:     getOltIP().String(),
+			OltState:  s.Olt.OperState,
+			OltVendor: s.Olt.Manufacture,
+		},
+	}
+
+	for _, nniPort := range s.Olt.NniIntfs {
+		nniPortInfo, _ := s.fetchPortDetail(nniPort.IntfID, nniPort.Type)
+		oltInfo.Ports = append(oltInfo.Ports, nniPortInfo)
+	}
+	for _, ponPort := range s.Olt.PonIntfs {
+		ponPortInfo, _ := s.fetchPortDetail(ponPort.IntfID, ponPort.Type)
+		oltInfo.Ports = append(oltInfo.Ports, ponPortInfo)
+	}
+
+	logger.Info("OLT Info: %v\n", oltInfo)
+	return oltInfo, nil
+}
+
+// PortStatus method returns Port status.
+func (s *Server) PortStatus(ctx context.Context, in *pb.PortInfo) (*pb.Ports, error) {
+	portInfo := &pb.Ports{}
+	logger.Debug("PortStatus() invoked")
+	if in.PortType == device.IntfNni {
+		for _, nniPort := range s.Olt.NniIntfs {
+			nniPortInfo, _ := s.fetchPortDetail(nniPort.IntfID, nniPort.Type)
+			portInfo.Ports = append(portInfo.Ports, nniPortInfo)
+		}
+	} else if in.PortType == device.IntfPon {
+		for _, ponPort := range s.Olt.PonIntfs {
+			ponPortInfo, _ := s.fetchPortDetail(ponPort.IntfID, ponPort.Type)
+			portInfo.Ports = append(portInfo.Ports, ponPortInfo)
+		}
+	} else {
+		return &pb.Ports{}, status.Errorf(codes.InvalidArgument, "Invalid port type")
+
+	}
+	return portInfo, nil
+}
+
+// ONUStatus method returns ONU status.
+func (s *Server) ONUStatus(ctx context.Context, in *pb.ONURequest) (*pb.ONUs, error) {
+	logger.Debug("ONUStatus request received")
+	if in.GetOnu() != nil {
+		logger.Debug("Received single ONU: %+v, %d\n", in.GetOnu(), in.GetOnu().PonPortId)
+		return s.handleONUStatusRequest(in.GetOnu())
+	}
+	logger.Debug("Received bulk ONUs status request")
+	onuInfo := &pb.ONUs{}
+	for intfid := range s.Onumap {
+		for _, onu := range s.Onumap[intfid] {
+			if onu.InternalState != device.ONU_FREE {
+				onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+			}
+		}
+	}
+	return onuInfo, nil
+}
+
+// ONUActivate method handles ONU activate requests from user.
+func (s *Server) ONUActivate(ctx context.Context, in *pb.ONURequest) (*pb.BBSimResponse, error) {
+	logger.Info("ONUActivate request received")
+	logger.Debug("Received values: %+v\n", in)
+
+	var onuInfo = []*pb.ONUInfo{}
+	// Activate single ONU
+	if in.GetOnu() != nil {
+		logger.Debug("Received single ONU: %+v\n", in.GetOnu())
+		onuInfo = append(onuInfo, in.GetOnu())
+	} else if len(in.GetOnusBatch().GetOnus()) != 0 { // Activate multiple ONUs
+		logger.Debug("Received multiple ONUs")
+		onuInfo = in.GetOnusBatch().GetOnus()
+	} else {
+		logger.Debug("Received empty request body")
+		return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, RequestFailed)
+	}
+	resp, err := s.handleONUActivate(onuInfo)
+	return resp, err
+}
+
+// ONUDeactivate method handles ONU deactivation request.
+func (s *Server) ONUDeactivate(ctx context.Context, in *pb.ONURequest) (*pb.BBSimResponse, error) {
+	logger.Info("ONUDeactivate request received")
+
+	// deactivate single ONU
+	if in.GetOnu() != nil {
+		logger.Debug("Received single ONU: %+v\n", in.GetOnu())
+		err := s.handleONUDeactivate(in.GetOnu())
+		if err != nil {
+			return &pb.BBSimResponse{}, status.Errorf(codes.Aborted, RequestFailed)
+		}
+	} else if len(in.GetOnusBatch().GetOnus()) != 0 { // bulk deactivate
+		logger.Debug("Received multiple ONUs")
+		for _, onuinfo := range in.GetOnusBatch().GetOnus() {
+			logger.Debug("ONU values: %+v\n", onuinfo)
+			err := s.handleONUDeactivate(onuinfo)
+			if err != nil {
+				return &pb.BBSimResponse{}, status.Errorf(codes.Aborted, RequestFailed)
+			}
+		}
+	} else {
+		// Empty request body is passed, delete all ONUs from all PON ports
+		for intfID := range s.Onumap {
+			if err := s.DeactivateAllOnuByIntfID(intfID); err != nil {
+				logger.Error("Failed in ONUDeactivate: %v", err)
+				return &pb.BBSimResponse{}, status.Errorf(codes.Aborted, RequestFailed)
+			}
+		}
+	}
+
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// GenerateONUAlarm RPC generates alarm for the onu
+func (s *Server) GenerateONUAlarm(ctx context.Context, in *pb.ONUAlarmRequest) (*pb.BBSimResponse, error) {
+	logger.Debug("GenerateONUAlarms() invoked")
+	if in.OnuSerial == "" {
+		return &pb.BBSimResponse{}, status.Errorf(codes.FailedPrecondition, "serial number can not be blank")
+	}
+	if len(in.OnuSerial) != SerialNumberLength {
+		return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "invalid serial number given (length mismatch)")
+	}
+	if in.Status != "on" && in.Status != "off" {
+		return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "invalid alarm status provided")
+	}
+	if s.alarmCh == nil {
+		return &pb.BBSimResponse{}, status.Errorf(codes.Internal, "alarm-channel not created, can not send alarm")
+	}
+	// TODO put these checks inside handleOnuAlarm for modularity
+	resp, err := s.handleOnuAlarm(in)
+	return resp, err
+}
+
+// GenerateOLTAlarm RPC generates alarm for the OLT
+func (s *Server) GenerateOLTAlarm(ctx context.Context, in *pb.OLTAlarmRequest) (*pb.BBSimResponse, error) {
+	logger.Debug("GenerateOLTAlarm() invoked")
+	if in.Status != "on" && in.Status != "off" {
+		return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, "invalid alarm status provided")
+	}
+	if s.alarmCh == nil {
+		return &pb.BBSimResponse{}, status.Errorf(codes.Internal, "alarm-channel not created, can not send alarm")
+	}
+	resp, err := s.handleOltAlarm(in)
+	if err != nil {
+		return resp, err
+	}
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// PerformDeviceAction rpc take the device request and performs OLT and ONU hard and soft reboot
+func (s *Server) PerformDeviceAction(ctx context.Context, in *pb.DeviceAction) (*pb.BBSimResponse, error) {
+	logger.Debug("PerformDeviceAction() invoked")
+	if s.deviceActionCh == nil {
+		return &pb.BBSimResponse{}, status.Errorf(codes.Internal, "device action channel not created, can not entertain request")
+	}
+	in, err := s.validateDeviceActionRequest(in)
+	if err != nil {
+		return &pb.BBSimResponse{}, status.Errorf(codes.InvalidArgument, err.Error())
+	}
+	s.deviceActionCh <- in
+	return &pb.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// NewMgmtAPIServer method starts BBSim gRPC server.
+func NewMgmtAPIServer(addrport string) (l net.Listener, g *grpc.Server, e error) {
+	logger.Info("BBSim gRPC server listening %s ...", addrport)
+	g = grpc.NewServer()
+	l, e = net.Listen("tcp", addrport)
+	return
+}
+
+// StartRestGatewayService method starts REST server for BBSim.
+func StartRestGatewayService(grpcAddress string, hostandport string, wg *sync.WaitGroup) {
+	ctx := context.Background()
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	mux := runtime.NewServeMux()
+	opts := []grpc.DialOption{grpc.WithInsecure()}
+	// Register REST endpoints
+	err := pb.RegisterBBSimServiceHandlerFromEndpoint(ctx, mux, grpcAddress, opts)
+	if err != nil {
+		logger.Error("%v", err)
+		return
+	}
+
+	logger.Info("BBSim REST server listening %s ...", hostandport)
+	err = http.ListenAndServe(hostandport, mux)
+	if err != nil {
+		logger.Error("%v", err)
+		return
+	}
+	return
+}
diff --git a/core/core_server.go b/core/core_server.go
index ba0727a..4c4fcab 100644
--- a/core/core_server.go
+++ b/core/core_server.go
@@ -18,54 +18,73 @@
 
 import (
 	"context"
+	"encoding/hex"
 	"errors"
+	"reflect"
 	"strconv"
 	"sync"
-	"reflect"
 
-	omci "github.com/opencord/omci-sim"
+	pb "gerrit.opencord.org/voltha-bbsim/api"
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
 	"gerrit.opencord.org/voltha-bbsim/common/utils"
 	"gerrit.opencord.org/voltha-bbsim/device"
+	flowHandler "gerrit.opencord.org/voltha-bbsim/flow"
 	openolt "gerrit.opencord.org/voltha-bbsim/protos"
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	"github.com/google/gopacket/pcap"
+	omci "github.com/opencord/omci-sim"
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/sync/errgroup"
 	"google.golang.org/grpc"
 )
 
+// Constants
 const (
-	NNI_VETH_NORTH_PFX = "nni_north"
-	NNI_VETH_SOUTH_PFX = "nni_south"
-	MAX_ONUS_PER_PON   = 64 // This value should be the same with the value in AdapterPlatrorm class
+	NniVethNorthPfx    = "nni_north"
+	NniVethSouthPfx    = "nni_south"
+	MaxPonPorts        = 64
+	MaxOnusPerPon      = 64 // This value should be the same with the value in AdapterPlatform class
+	VendorIDLength     = 4
+	SerialNumberLength = 12
+	OpenOltStart       = "start"
+	OpenOltStop        = "stop"
 )
 
 // Server structure consists of all the params required for BBsim.
 type Server struct {
-	wg           *sync.WaitGroup
-	Olt          *device.Olt
-	Onumap       map[uint32][]*device.Onu
-	Ioinfos      []*Ioinfo
-	gRPCserver   *grpc.Server
-	gRPCAddress  string
-	gRPCPort     uint32
-	Vethnames    []string
-	IndInterval  int
-	Processes    []string
-	EnableServer *openolt.Openolt_EnableIndicationServer
-	CtagMap      map[string]uint32
-	cancel       context.CancelFunc
-	stateRepCh   chan stateReport
-	omciIn       chan openolt.OmciIndication
-	omciOut      chan openolt.OmciMsg
-	eapolIn      chan *byteMsg
-	eapolOut     chan *byteMsg
-	dhcpIn       chan *byteMsg
-	dhcpOut      chan *byteMsg
+	wg              *sync.WaitGroup
+	Olt             *device.Olt
+	Onumap          map[uint32][]*device.Onu
+	SNmap           sync.Map
+	AutoONUActivate bool
+	Ioinfos         []*Ioinfo
+	gRPCserver      *grpc.Server
+	gRPCAddress     string
+	gRPCPort        uint32
+	mgmtServer      *grpc.Server
+	mgmtGrpcPort    uint32
+	mgmtRestPort    uint32
+	Vethnames       []string
+	IndInterval     int
+	Processes       []string
+	EnableServer    *openolt.Openolt_EnableIndicationServer
+	CtagMap         map[string]uint32
+	cancel          context.CancelFunc
+	stateRepCh      chan stateReport
+	omciIn          chan openolt.OmciIndication
+	omciOut         chan openolt.OmciMsg
+	eapolIn         chan *byteMsg
+	eapolOut        chan *byteMsg
+	dhcpIn          chan *byteMsg
+	dhcpOut         chan *byteMsg
+	FlowMap         map[FlowKey]*openolt.Flow
+	alarmCh         chan *openolt.Indication
+	deviceActionCh  chan *pb.DeviceAction
+	serverActionCh  chan string
 }
 
+// Packet structure
 type Packet struct {
 	Info *Ioinfo
 	Pkt  gopacket.Packet
@@ -83,56 +102,89 @@
 	next    device.DeviceState
 }
 
+// FlowKey used for FlowMap key
+type FlowKey struct {
+	FlowID        uint32
+	FlowDirection string
+}
+
 // NewCore initialize OLT and ONU objects
 func NewCore(opt *option) *Server {
 	// TODO: make it decent
 	oltid := opt.oltid
 	npon := opt.npon
-	nonus := opt.nonus
-	s := Server{
-		Olt:          device.NewOlt(oltid, npon, 1),
-		Onumap:       make(map[uint32][]*device.Onu),
-		Ioinfos:      []*Ioinfo{},
-		gRPCAddress:  opt.address,
-		gRPCPort:     opt.port,
-		Vethnames:    []string{},
-		IndInterval:  opt.intvl,
-		Processes:    []string{},
-		EnableServer: nil,
-		stateRepCh:   make(chan stateReport, 8),
-		omciIn:       make(chan openolt.OmciIndication, 1024),
-		omciOut:      make(chan openolt.OmciMsg, 1024),
-		eapolIn:      make(chan *byteMsg, 1024),
-		eapolOut:     make(chan *byteMsg, 1024),
-		dhcpIn:       make(chan *byteMsg, 1024),
-		dhcpOut:      make(chan *byteMsg, 1024),
+	if npon > MaxPonPorts {
+		logger.Warn("Provided number of PON ports exceeds limit of %d", MaxPonPorts)
+		logger.Info("Setting number of PON ports to %d", MaxPonPorts)
+		npon = MaxPonPorts
 	}
+	nonus := opt.nonus
+	if nonus > MaxOnusPerPon {
+		logger.Warn("Provided number of ONUs per PON port exceeds limit of %d", MaxOnusPerPon)
+		logger.Info("Setting number of ONUs per PON port to %d", MaxOnusPerPon)
+		nonus = MaxOnusPerPon
+	}
+	s := Server{
+		Olt:             device.NewOlt(oltid, npon, 1), // TODO nnni is to be taken from options
+		Onumap:          make(map[uint32][]*device.Onu),
+		Ioinfos:         []*Ioinfo{},
+		gRPCAddress:     opt.address,
+		gRPCPort:        opt.port,
+		Vethnames:       []string{},
+		IndInterval:     opt.intvl,
+		AutoONUActivate: !opt.interactiveOnuActivation,
+		Processes:       []string{},
+		mgmtGrpcPort:    opt.mgmtGrpcPort,
+		mgmtRestPort:    opt.mgmtRestPort,
+		EnableServer:    nil,
+		stateRepCh:      make(chan stateReport, 8),
+		omciIn:          make(chan openolt.OmciIndication, 1024),
+		omciOut:         make(chan openolt.OmciMsg, 1024),
+		eapolIn:         make(chan *byteMsg, 1024),
+		eapolOut:        make(chan *byteMsg, 1024),
+		dhcpIn:          make(chan *byteMsg, 1024),
+		dhcpOut:         make(chan *byteMsg, 1024),
+		FlowMap:         make(map[FlowKey]*openolt.Flow),
+		serverActionCh:  make(chan string),
+	}
+	logger.Info("OLT %d created: %v", s.Olt.ID, s.Olt)
 
 	nnni := s.Olt.NumNniIntf
 	logger.Info("OLT ID: %d was retrieved.", s.Olt.ID)
-	for intfid := nnni; intfid < npon+nnni; intfid++ {
+	logger.Info("OLT Serial-Number: %v", s.Olt.SerialNumber)
+	// Creating Onu Map
+	for intfid := uint32(0); intfid < npon; intfid++ {
 		s.Onumap[intfid] = device.NewOnus(oltid, intfid, nonus, nnni)
 	}
 
-	//TODO: To be fixed because it is hardcoded
+	logger.Debug("Onu Map:")
+	for _, onus := range s.Onumap {
+		for _, onu := range onus {
+			logger.Debug("%+v", *onu)
+		}
+	}
+
+	// TODO: To be fixed because it is hardcoded
 	s.CtagMap = make(map[string]uint32)
-	for i := 0; i < MAX_ONUS_PER_PON; i++ {
+	for i := 0; i < MaxOnusPerPon; i++ {
 		oltid := s.Olt.ID
 		intfid := uint32(1)
-		sn := convB2S(device.NewSN(oltid, intfid, uint32(i)))
+		sn := utils.ConvB2S(device.NewSN(oltid, intfid, uint32(i)))
 		s.CtagMap[sn] = uint32(900 + i) // This is hard coded for BBWF
 	}
+
+	flowHandler.InitializeFlowManager(s.Olt.ID)
 	return &s
 }
 
-// Start starts the BBSim and openolt gRPC servers (blocking)
+// Start starts the openolt gRPC server (blocking)
 func (s *Server) Start() error {
-	s.wg = &sync.WaitGroup{}
-	logger.Debug("Start() Start")
+	logger.Debug("Starting OpenOLT gRPC Server")
 	defer func() {
-		close(s.stateRepCh)
-		logger.Debug("Start() Done")
+		logger.Debug("OpenOLT gRPC Server Stopped")
 	}()
+
+	// Start Openolt gRPC server
 	addressport := s.gRPCAddress + ":" + strconv.Itoa(int(s.gRPCPort))
 	listener, gserver, err := NewGrpcServer(addressport)
 	if err != nil {
@@ -145,32 +197,67 @@
 		logger.Error("Failed to run gRPC server: %v", err)
 		return err
 	}
-	s.wg.Wait()
 	return nil
 }
 
-// Stop stops the BBSim and openolt gRPC servers (non-blocking).
+// Stop stops the openolt gRPC servers (non-blocking).
 func (s *Server) Stop() {
-	logger.Debug("Stop() Start")
-	defer logger.Debug("Stop() Done")
+	logger.Debug("Stopping OpenOLT gRPC Server & PktLoops")
+	defer logger.Debug("OpenOLT gRPC Server & PktLoops Stopped")
+
 	if s.gRPCserver != nil {
 		s.gRPCserver.Stop()
 		logger.Debug("gRPCserver.Stop()")
 	}
+
 	s.StopPktLoops()
 	return
 }
 
+func (s *Server) startMgmtServer(wg *sync.WaitGroup) {
+	defer logger.Debug("Management api server exited")
+
+	grpcAddressPort := s.gRPCAddress + ":" + strconv.Itoa(int(s.mgmtGrpcPort))
+	restAddressPort := s.gRPCAddress + ":" + strconv.Itoa(int(s.mgmtRestPort))
+	// Start rest gateway for BBSim server
+	go StartRestGatewayService(grpcAddressPort, restAddressPort, wg)
+	addressPort := s.gRPCAddress + ":" + strconv.Itoa(int(s.mgmtGrpcPort))
+
+	listener, apiserver, err := NewMgmtAPIServer(addressPort)
+	if err != nil {
+		logger.Error("Unable to create management api server %v", err)
+		return
+	}
+
+	s.mgmtServer = apiserver
+	pb.RegisterBBSimServiceServer(apiserver, s)
+	if e := apiserver.Serve(listener); e != nil {
+		logger.Error("Failed to run management api server %v", e)
+		return
+	}
+
+}
+
+func (s *Server) stopMgmtServer() error {
+	if s.mgmtServer != nil {
+		s.mgmtServer.GracefulStop()
+		logger.Debug("Management server stopped")
+		return nil
+	}
+	return errors.New("can not stop management server, server not created")
+}
+
 // Enable invokes methods for activation of OLT and ONU (blocking)
 func (s *Server) Enable(sv *openolt.Openolt_EnableIndicationServer) error {
 	olt := s.Olt
 	defer func() {
 		olt.Initialize()
-		for intfid := range s.Onumap {
-			for _, onu := range s.Onumap[intfid] {
-				onu.Initialize()
-			}
-		}
+		// Below lines commented as we dont want to change the onu state on restart
+		// for intfid := range s.Onumap {
+		// 	for _, onu := range s.Onumap[intfid] {
+		// 		onu.Initialize()
+		// 	}
+		// }
 		s.updateDevIntState(olt, device.OLT_INACTIVE)
 		logger.Debug("Enable() Done")
 	}()
@@ -185,11 +272,29 @@
 	coreCtx := context.Background()
 	coreCtx, corecancel := context.WithCancel(coreCtx)
 	s.cancel = corecancel
-	go s.sendDiscovertoONUs(*sv)
 
-	if err := s.StartPktLoops(coreCtx, *sv); err != nil {
-		return err
+	errorchan := make(chan error, 5)
+	go s.StartPktLoops(coreCtx, *sv, errorchan)
+
+	if s.AutoONUActivate == true {
+		// Initialize all ONUs
+		for intfid := range s.Onumap {
+			for _, onu := range s.Onumap[intfid] {
+				onu.Initialize()
+			}
+		}
+		// Activate all ONUs
+		s.activateONUs(*sv, s.Onumap)
 	}
+
+	select {
+	case err := <-errorchan:
+		if err != nil {
+			logger.Debug("Error: %v", err)
+			return err
+		}
+	}
+
 	return nil
 }
 
@@ -224,10 +329,28 @@
 	s.updateDevIntState(onu, state)
 	return nil
 }
-func (s *Server) Activate(onu *device.Onu) error {
-	utils.LoggerWithOnu(onu).Info("sending ONUInd Onuid")
-	go sendOnuIndtoONU(*s.EnableServer, onu)
-	return nil
+
+func (s *Server) activateOnu(onu *device.Onu) {
+	snKey := stringifySerialNumber(onu.SerialNumber)
+	s.SNmap.Store(snKey, onu)
+	device.UpdateOnusOpStatus(onu.IntfID, onu, "up")
+
+	err := sendOnuDiscInd(*s.EnableServer, onu)
+	if err != nil {
+		logger.Error(err.Error())
+		return
+	}
+	logger.Info("OLT id:%d sent ONUDiscInd.", s.Olt.ID)
+	logger.Debug("activateONUs Entry in SNmap %v", snKey)
+}
+
+func (s *Server) activateONUs(stream openolt.Openolt_EnableIndicationServer, Onumap map[uint32][]*device.Onu) {
+	// Add all ONUs to SerialNumber Map
+	for intfid := range Onumap {
+		for _, onu := range Onumap[intfid] {
+			s.activateOnu(onu)
+		}
+	}
 }
 
 func (s *Server) activateOLT(stream openolt.Openolt_EnableIndicationServer) error {
@@ -249,37 +372,16 @@
 	logger.Info("OLT %s sent IntfInd.", olt.Name)
 
 	// OLT sends Operation Indication to Adapter after activating each interface
-	//time.Sleep(IF_UP_TIME * time.Second)
 	if err := sendOperInd(stream, olt); err != nil {
 		logger.Error("Fail to sendOperInd: %v", err)
 		return err
 	}
 	logger.Info("OLT %s sent OperInd.", olt.Name)
-
 	return nil
 }
-func (s *Server) sendDiscovertoONUs(stream openolt.Openolt_EnableIndicationServer) {
-	// OLT sends ONU Discover Indication to Adapter after ONU discovery
-	for intfid := range s.Onumap {
-		device.UpdateOnusOpStatus(intfid, s.Onumap[intfid], "up")
-	}
-
-	// Initialize all ONUs
-	for intfid := range s.Onumap {
-		for _, onu := range s.Onumap[intfid] {
-			onu.Initialize()
-		}
-	}
-
-	// Send discovery indication for all ONUs
-	for intfid, _ := range s.Onumap {
-		sendOnuDiscInd(stream, s.Onumap[intfid], s.IndInterval)
-		logger.Info("OLT sent ONUDiscInd for intfId:%d.", intfid)
-	}
-}
 
 // StartPktLoops creates veth pairs and invokes runPktLoops (blocking)
-func (s *Server) StartPktLoops(ctx context.Context, stream openolt.Openolt_EnableIndicationServer) error {
+func (s *Server) StartPktLoops(ctx context.Context, stream openolt.Openolt_EnableIndicationServer, errorchan chan error) {
 	logger.Debug("StartPktLoops () Start")
 	defer func() {
 		RemoveVeths(s.Vethnames)
@@ -289,11 +391,14 @@
 		s.updateDevIntState(s.Olt, device.OLT_PREACTIVE)
 		logger.Debug("StartPktLoops () Done")
 	}()
+	s.alarmCh = make(chan *openolt.Indication, 10)
+	go startAlarmLoop(stream, s.alarmCh)
+	go s.startDeviceActionLoop()
 	s.wg.Add(1)
 	ioinfos, veths, err := createIoinfos(s.Olt.ID, s.Vethnames)
 	if err != nil {
 		logger.Error("createIoinfos failed: %v", err)
-		return err
+		errorchan <- err
 	}
 	s.Ioinfos = ioinfos
 	s.Vethnames = veths
@@ -305,9 +410,9 @@
 
 	if err = s.runPktLoops(child, stream); err != nil {
 		logger.Error("runPktLoops failed: %v", err)
-		return err
+		errorchan <- err
 	}
-	return nil
+	errorchan <- nil
 }
 
 // StopPktLoops (non-blocking)
@@ -335,13 +440,13 @@
 	return ioinfos, Vethnames, nil
 }
 
-//Blocking
+// Blocking
 func (s *Server) runPktLoops(ctx context.Context, stream openolt.Openolt_EnableIndicationServer) error {
 	logger.Debug("runPacketPktLoops Start")
 	defer logger.Debug("runPacketLoops Done")
 
 	errchOmci := make(chan error)
-	RunOmciResponder(ctx, s.omciOut, s.omciIn, errchOmci)
+	s.RunOmciResponder(ctx, s.omciOut, s.omciIn, errchOmci)
 	eg, child := errgroup.WithContext(ctx)
 	child, cancel := context.WithCancel(child)
 
@@ -356,7 +461,7 @@
 		defer logger.Debug("runOMCIResponder Done")
 		select {
 		case v, ok := <-errchOmci: // Wait for OmciInitialization
-			if ok { //Error
+			if ok { // Error
 				logger.Error("Error happend in Omci: %s", v)
 				return v
 			}
@@ -371,7 +476,7 @@
 		defer logger.Debug("runEapolResponder Done")
 		select {
 		case v, ok := <-errchEapol:
-			if ok { //Error
+			if ok { // Error
 				logger.Error("Error happend in Eapol:%s", v)
 				return v
 			}
@@ -386,7 +491,7 @@
 		defer logger.Debug("runDhcpResponder Done")
 		select {
 		case v, ok := <-errchDhcp:
-			if ok { //Error
+			if ok { // Error
 				logger.Error("Error happend in Dhcp:%s", v)
 				return v
 			}
@@ -451,7 +556,7 @@
 				logger.Error("Fail to send EAPOL PktInd indication. %v", err)
 				return err
 			}
-		case msg := <-s.dhcpIn: //TODO: We should put omciIn, eapolIn, dhcpIn toghether
+		case msg := <-s.dhcpIn: // TODO: We should put omciIn, eapolIn, dhcpIn toghether
 			intfid := msg.IntfId
 			onuid := msg.OnuId
 			gemid, err := s.getGemPortID(intfid, onuid)
@@ -468,7 +573,7 @@
 				logger.Error("Failed to GetOnuByID:%d", onuid)
 				continue
 			}
-			sn := convB2S(onu.SerialNumber.VendorSpecific)
+			sn := utils.ConvB2S(onu.SerialNumber.VendorSpecific)
 			if ctag, ok := s.CtagMap[sn]; ok == true {
 				tagpkt, err := PushVLAN(pkt, uint16(ctag), onu)
 				if err != nil {
@@ -499,7 +604,12 @@
 				logger.Debug("WARNING: This packet does not come from NNI ")
 				continue
 			}
+
+			onuid := nnipkt.Info.onuid
 			intfid := nnipkt.Info.intfid
+			onu, _ := s.GetOnuByID(onuid, intfid)
+
+			utils.LoggerWithOnu(onu).Info("Received packet from NNI in grpc Server.")
 
 			pkt := nnipkt.Pkt
 			data = &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{IntfType: "nni", IntfId: intfid, Pkt: pkt.Data()}}
@@ -513,7 +623,6 @@
 			return nil
 		}
 	}
-	return nil
 }
 
 func (s *Server) onuPacketOut(intfid uint32, onuid uint32, rawpkt gopacket.Packet) error {
@@ -622,7 +731,7 @@
 			}
 		}
 	}
-	err := errors.New("No mathced SN is found ")
+	err := errors.New("no matching serial number found")
 	logger.Error("%s", err)
 	return nil, err
 }
@@ -638,7 +747,7 @@
 			return onu, nil
 		}
 	}
-	err := errors.New("No matched OnuID is found ")
+	err := errors.New("no matching OnuID found")
 	logger.WithFields(log.Fields{
 		"onumap": onumap,
 		"onuid":  onuid,
@@ -647,10 +756,111 @@
 	return nil, err
 }
 
-func convB2S(b []byte) string {
-	s := ""
-	for _, i := range b {
-		s = s + strconv.FormatInt(int64(i/16), 16) + strconv.FormatInt(int64(i%16), 16)
+// getOnuFromSNmap method returns onu object from SNmap if found
+func (s *Server) getOnuFromSNmap(serialNumber *openolt.SerialNumber) (*device.Onu, bool) {
+	snkey := stringifySerialNumber(serialNumber)
+
+	logger.Debug("getOnuFromSNmap received serial number %s", snkey)
+
+	if onu, exist := s.SNmap.Load(snkey); exist {
+		logger.Info("Serial number found in map")
+		return onu.(*device.Onu), true
 	}
-	return s
+	logger.Info("Serial number not found in map")
+	return nil, false
+}
+
+func stringifySerialNumber(serialNum *openolt.SerialNumber) string {
+	return string(serialNum.VendorId) + utils.ConvB2S(serialNum.VendorSpecific)
+}
+
+func getOpenoltSerialNumber(SerialNumber string) (*openolt.SerialNumber, error) {
+	if len(SerialNumber) != SerialNumberLength {
+		logger.Error("Invalid serial number %s", SerialNumber)
+		return nil, errors.New("invalid serial number")
+	}
+	// First four characters are vendorId
+	vendorID := SerialNumber[:VendorIDLength]
+	vendorSpecific := SerialNumber[VendorIDLength:]
+
+	vsbyte, _ := hex.DecodeString(vendorSpecific)
+
+	// Convert to Openolt serial number
+	serialNum := new(openolt.SerialNumber)
+	serialNum.VendorId = []byte(vendorID)
+	serialNum.VendorSpecific = vsbyte
+
+	return serialNum, nil
+}
+
+// TODO move to device_onu.go
+func (s *Server) sendOnuIndicationsOnOltReboot() {
+	if AutoONUActivate == 1 {
+		// For auto activate mode, onu indications is sent in Enable()
+		return
+	}
+
+	s.SNmap.Range(
+		func(key, value interface{}) bool {
+			onu := value.(*device.Onu)
+			if onu.InternalState == device.ONU_LOS_RAISED {
+				return true
+			}
+
+			err := sendOnuDiscInd(*s.EnableServer, onu)
+			if err != nil {
+				logger.Error(err.Error())
+			}
+
+			return true
+		})
+}
+
+// StartServerActionLoop reads on server-action channel, and starts and stops the server as per the value received
+func (s *Server) StartServerActionLoop(wg *sync.WaitGroup) {
+	for {
+		select {
+		case Req := <-s.serverActionCh:
+			logger.Debug("Request Received On serverActionCh: %+v", Req)
+			switch Req {
+			case "start":
+				logger.Debug("Server Start Request Received On ServerActionChannel")
+				go s.Start() // blocking
+			case "stop":
+				logger.Debug("Server Stop Request Received On ServerActionChannel")
+				s.Stop()
+			default:
+				logger.Error("Invalid value received in deviceActionCh")
+			}
+		}
+	}
+}
+
+// startDeviceActionLoop reads on the action-channel, and performs onu and olt reboot related actions
+// TODO all onu and olt related actions (like alarms) should be handled using this function
+func (s *Server) startDeviceActionLoop() {
+	logger.Debug("startDeviceActionLoop invoked")
+	s.deviceActionCh = make(chan *pb.DeviceAction, 10)
+	for {
+		logger.Debug("Action channel loop started")
+		select {
+		case Req := <-s.deviceActionCh:
+			logger.Debug("Reboot Action Type: %+v", Req.DeviceAction)
+			switch Req.DeviceType {
+			case DeviceTypeOnu:
+				value, _ := s.SNmap.Load(Req.DeviceSerialNumber)
+				onu := value.(*device.Onu)
+				if Req.DeviceAction == SoftReboot {
+					s.handleONUSoftReboot(onu.IntfID, onu.OnuID)
+				} else if Req.DeviceAction == HardReboot {
+					s.handleONUHardReboot(onu)
+				}
+			case DeviceTypeOlt:
+				logger.Debug("Reboot For OLT Received")
+				s.handleOLTReboot()
+			default:
+				logger.Error("Invalid value received in deviceActionCh")
+			}
+		}
+	}
 }
diff --git a/core/dhcp.go b/core/dhcp.go
index 6aa5753..ae890ba 100644
--- a/core/dhcp.go
+++ b/core/dhcp.go
@@ -21,15 +21,17 @@
 	"encoding/hex"
 	"errors"
 	"fmt"
-	"gerrit.opencord.org/voltha-bbsim/common/logger"
-	"github.com/google/gopacket"
-	"github.com/google/gopacket/layers"
 	"math/rand"
 	"net"
 	"reflect"
 	"sync"
+
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
 )
 
+// Constants for DHCP states
 const (
 	DHCP_INIT clientState = iota + 1
 	DHCP_SELECTING
@@ -78,6 +80,7 @@
 	layers.DHCPOptNTPServers,
 }
 
+// RunDhcpResponder responds to the DHCP client messages
 func RunDhcpResponder(ctx context.Context, dhcpOut chan *byteMsg, dhcpIn chan *byteMsg, errch chan error) {
 	responder := getDHCPResponder()
 	responder.dhcpIn = dhcpIn
@@ -310,7 +313,7 @@
 	// Send our packet
 	msg := byteMsg{IntfId: c.key.intfid,
 		OnuId: c.key.onuid,
-		Byte: bytes}
+		Byte:  bytes}
 	dhcpIn <- &msg
 	logger.Debug("sendBytes intfid:%d onuid:%d", c.key.intfid, c.key.onuid)
 	logger.Debug(hex.Dump(msg.Byte))
diff --git a/core/eapol.go b/core/eapol.go
index 85a9f07..cc9f24c 100644
--- a/core/eapol.go
+++ b/core/eapol.go
@@ -22,17 +22,19 @@
 	"encoding/hex"
 	"errors"
 	"fmt"
+	"net"
+	"sync"
+
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
-	"net"
-	"sync"
 )
 
 type clientState int
 
+// Constants for eapol states
 const (
-	EAP_START clientState = iota + 1 //TODO: This state definition should support 802.1X
+	EAP_START clientState = iota + 1 // TODO: This state definition should support 802.1X
 	EAP_RESPID
 	EAP_RESPCHA
 	EAP_SUCCESS
@@ -72,7 +74,7 @@
 	return resp
 }
 
-//RunEapolResponder starts go routine which processes and responds for received eapol messages
+// RunEapolResponder starts go routine which processes and responds for received eapol messages
 func RunEapolResponder(ctx context.Context, eapolOut chan *byteMsg, eapolIn chan *byteMsg, errch chan error) {
 	responder := getEAPResponder()
 	responder.eapolIn = eapolIn
@@ -82,7 +84,7 @@
 		defer logger.Debug("EAPOL response process was done")
 		for {
 			select {
-			case msg := <- eapolOut:
+			case msg := <-eapolOut:
 				logger.Debug("Received eapol from eapolOut intfid:%d onuid:%d", msg.IntfId, msg.OnuId)
 				responder := getEAPResponder()
 				clients := responder.clients
@@ -136,7 +138,7 @@
 		return errors.New("Failed to send EAPStart")
 	}
 	logger.Debug("Sending EAPStart")
-	//clients[key{intfid: intfid, onuid: onuid}] = &client
+	// clients[key{intfid: intfid, onuid: onuid}] = &client
 	resp.clients[clientKey{intfid: intfid, onuid: onuid}] = &client
 	return nil
 }
@@ -254,10 +256,10 @@
 	return &eap
 }
 
-func getMD5Data (id uint8, eap *layers.EAP) []byte {
+func getMD5Data(id uint8, eap *layers.EAP) []byte {
 	i := byte(id)
 	C := []byte(eap.BaseLayer.Contents)[6:]
-	P := []byte{i, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64} //"password"
+	P := []byte{i, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64} // "password"
 	data := md5.Sum(append(P, C...))
 	ret := make([]byte, 16)
 	for j := 0; j < 16; j++ {
diff --git a/core/grpc_service.go b/core/grpc_service.go
index cb1ee95..2d216d8 100644
--- a/core/grpc_service.go
+++ b/core/grpc_service.go
@@ -22,7 +22,8 @@
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
 	"gerrit.opencord.org/voltha-bbsim/common/utils"
 	"gerrit.opencord.org/voltha-bbsim/device"
-        "gerrit.opencord.org/voltha-bbsim/protos"
+	flowHandler "gerrit.opencord.org/voltha-bbsim/flow"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	omci "github.com/opencord/omci-sim"
@@ -36,6 +37,12 @@
 // DisableOlt method sends OLT down indication
 func (s *Server) DisableOlt(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
 	logger.Debug("OLT receives DisableOLT()")
+
+	err := flowHandler.PortDown(0)
+	if err != nil {
+		logger.Error("Failed in port down %v", err)
+	}
+
 	if s.EnableServer != nil {
 		if err := sendOltIndDown(*s.EnableServer); err != nil {
 			return new(openolt.Empty), err
@@ -48,6 +55,12 @@
 // ReenableOlt method sends OLT up indication for re-enabling OLT
 func (s *Server) ReenableOlt(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
 	logger.Debug("OLT receives Reenable()")
+
+	err := flowHandler.PortUp(0)
+	if err != nil {
+		logger.Error("Failed in port up %v", err)
+	}
+
 	if s.EnableServer != nil {
 		if err := sendOltIndUp(*s.EnableServer, s.Olt); err != nil {
 			logger.Error("Failed to send OLT UP indication for reenable OLT: %v", err)
@@ -91,16 +104,19 @@
 func (s *Server) ActivateOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
 	logger.Debug("OLT receives ActivateONU()")
 
-	matched, err := getOnuBySN(s.Onumap, onu.SerialNumber)
-	if err != nil {
-		logger.Fatal("%s", err)
+	matched, exist := s.getOnuFromSNmap(onu.SerialNumber)
+	if !exist {
+		logger.Fatal("ONU not found with serial nnumber %v", onu.SerialNumber)
 		return new(openolt.Empty), status.Errorf(codes.NotFound, "ONU not found with serial number %v", onu.SerialNumber)
 	}
 	onuid := onu.OnuId
 	matched.OnuID = onuid
 	s.updateDevIntState(matched, device.ONU_ACTIVE)
-	s.Activate(matched)
 	logger.Debug("ONU IntfID: %d OnuID: %d activated succesufully.", onu.IntfId, onu.OnuId)
+	if err := sendOnuInd(*s.EnableServer, matched, s.IndInterval, "up", "up"); err != nil {
+		logger.Error("Failed to send ONU Indication intfID %d, onuID %d", matched.IntfID, matched.OnuID)
+		return new(openolt.Empty), err
+	}
 
 	return new(openolt.Empty), nil
 }
@@ -125,7 +141,7 @@
 
 // DeleteOnu handles ONU deletion request from VOLTHA
 func (s *Server) DeleteOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
-	logger.Debug("OLT receives DeleteONU()")
+	logger.Debug("OLT receives DeleteONU() intfID: %d, onuID: %d", onu.IntfId, onu.OnuId)
 	Onu, err := s.GetOnuByID(onu.OnuId, onu.IntfId)
 	if err != nil {
 		return new(openolt.Empty), err
@@ -135,6 +151,12 @@
 	Onu.InternalState = device.ONU_FREE
 	Onu.OnuID = 0
 
+	// Get snMap key for the ONU serial number
+	snkey := stringifySerialNumber(Onu.SerialNumber)
+
+	// Delete Serial number entry from SNmap
+	logger.Info("Deleting serial number %s from SNmap", snkey)
+	s.SNmap.Delete(snkey)
 	return new(openolt.Empty), nil
 }
 
@@ -150,8 +172,8 @@
 	state := onu.GetIntState()
 	logger.Debug("ONU-ID: %v, ONU state: %d", msg.OnuId, state)
 
-	// If ONU is ONU_INACTIVE or ONU_FREE do not send omci response
-	if state == device.ONU_INACTIVE || state == device.ONU_FREE {
+	// If ONU is ONU_INACTIVE, ONU_FREE or ONU_OMCI_CHANNEL_LOS_RAISED drop
+	if state != device.ONU_ACTIVE && state != device.ONU_OMCIACTIVE {
 		logger.Info("ONU (IF %v ONU-ID: %v) is not ACTIVE, so not processing OmciMsg", msg.IntfId, msg.OnuId)
 		return new(openolt.Empty), nil
 	}
@@ -187,12 +209,29 @@
 
 // FlowAdd method should handle flows addition to datapath for OLT and ONU
 func (s *Server) FlowAdd(c context.Context, flow *openolt.Flow) (*openolt.Empty, error) {
-	logger.Debug("OLT %d receives FlowAdd() IntfID:%d OnuID:%d EType:%x GemPortID:%d", s.Olt.ID, flow.AccessIntfId, flow.OnuId, flow.Classifier.EthType, flow.GemportId)
-	onu, err := s.GetOnuByID(uint32(flow.OnuId), uint32(flow.AccessIntfId))
+	logger.Debug("OLT %d receives FlowAdd() %v", s.Olt.ID, flow)
+	// Check if flow already present
+	flowKey := FlowKey{
+		FlowID:        flow.FlowId,
+		FlowDirection: flow.FlowType,
+	}
+	if _, exist := s.FlowMap[flowKey]; exist {
+		logger.Error("Flow already exists %v", flow)
+		return new(openolt.Empty), status.Errorf(codes.AlreadyExists, "Flow already exists")
+	}
 
+	// Send flow to flowHandler
+	err := flowHandler.AddFlow(flow)
+	if err != nil {
+		logger.Error("Error in pushing flow to datapath")
+		return new(openolt.Empty), err
+	}
+
+	// Update flowMap
+	s.FlowMap[flowKey] = flow
+
+	onu, err := s.GetOnuByID(uint32(flow.OnuId), uint32(flow.AccessIntfId))
 	if err == nil {
-		intfid := onu.IntfID
-		onuid := onu.OnuID
 		onu.GemportID = uint16(flow.GemportId)
 
 		utils.LoggerWithOnu(onu).WithFields(log.Fields{
@@ -200,14 +239,13 @@
 			"c_tag": flow.Action.IVid,
 		}).Debug("OLT receives FlowAdd().")
 
-
 		// EAPOL flow
 		if flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) {
 			omcistate := omci.GetOnuOmciState(onu.IntfID, onu.OnuID)
 			if omcistate != omci.DONE {
 				logger.Warn("FlowAdd() OMCI state %d is not \"DONE\"", omci.GetOnuOmciState(onu.OnuID, onu.IntfID))
 			}
-			s.updateOnuIntState(intfid, onuid, device.ONU_OMCIACTIVE)
+			_ = s.updateOnuIntState(onu.IntfID, onu.OnuID, device.ONU_OMCIACTIVE)
 		}
 
 		// DHCP flow
@@ -219,23 +257,52 @@
 				if omcistate != omci.DONE {
 					logger.Warn("FlowAdd() OMCI state %d is not \"DONE\"", omci.GetOnuOmciState(onu.OnuID, onu.IntfID))
 				}
-				s.updateOnuIntState(intfid, onuid, device.ONU_AUTHENTICATED)
+				_ = s.updateOnuIntState(onu.IntfID, onu.OnuID, device.ONU_AUTHENTICATED)
 			}
 		}
+		// Update flow ID in ONU object
+		onu.FlowIDs = append(onu.FlowIDs, flow.FlowId)
 	}
 	return new(openolt.Empty), nil
 }
 
-// FlowRemove should handle flow deletion from datapath
+// FlowRemove handles flow deletion from datapath
 func (s *Server) FlowRemove(c context.Context, flow *openolt.Flow) (*openolt.Empty, error) {
-	logger.Debug("OLT %d receives FlowRemove()", s.Olt.ID)
+	logger.Debug("OLT %d receives FlowRemove(): %v", s.Olt.ID, flow)
+
+	// Check if flow exists
+	flowKey := FlowKey{
+		FlowID:        flow.FlowId,
+		FlowDirection: flow.FlowType,
+	}
+	if _, exist := s.FlowMap[flowKey]; !exist {
+		logger.Error("Flow %v not found", flow)
+		return new(openolt.Empty), status.Errorf(codes.NotFound, "Flow not found")
+	}
+
+	flow = s.FlowMap[flowKey]
+	// Send delete flow to flowHandler
+	err := flowHandler.DeleteFlow(flow)
+	if err != nil {
+		return new(openolt.Empty), err
+	}
+
 	onu, err := s.GetOnuByID(uint32(flow.OnuId), uint32(flow.AccessIntfId))
-	if err == nil {
+	if err != nil {
+		logger.Warn("Failed flow remove %v", err)
+	} else {
+		// Delete flowID from onu
+		onu.DeleteFlowID(flow.FlowId)
 		utils.LoggerWithOnu(onu).WithFields(log.Fields{
 			"olt":   s.Olt.ID,
 			"c_tag": flow.Action.IVid,
 		}).Debug("OLT receives FlowRemove().")
+		logger.Debug("Flows %v in ONU %d", onu.FlowIDs, onu.OnuID)
 	}
+
+	// Delete flow from flowMap
+	delete(s.FlowMap, flowKey)
+
 	return new(openolt.Empty), nil
 }
 
@@ -261,7 +328,7 @@
 	logger.Debug("OLT %d receives Reboot ().", s.Olt.ID)
 	// Initialize OLT & Env
 	logger.Debug("Initialized by Reboot")
-	s.Disable()
+	s.handleOLTReboot()
 	return new(openolt.Empty), nil
 }
 
@@ -280,7 +347,7 @@
 
 // NewGrpcServer starts openolt gRPC server
 func NewGrpcServer(addrport string) (l net.Listener, g *grpc.Server, e error) {
-	logger.Debug("Listening %s ...", addrport)
+	logger.Debug("OpenOLT gRPC server listening %s ...", addrport)
 	g = grpc.NewServer()
 	l, e = net.Listen("tcp", addrport)
 	return
diff --git a/core/io_info.go b/core/io_info.go
index e56d4b6..36eb67f 100644
--- a/core/io_info.go
+++ b/core/io_info.go
@@ -25,10 +25,11 @@
 	log "github.com/sirupsen/logrus"
 )
 
+// Ioinfo represents the input/output
 type Ioinfo struct {
 	Name    string
-	iotype  string //nni or uni
-	ioloc   string //inside or outsode
+	iotype  string // nni or uni
+	ioloc   string // inside or outside
 	intfid  uint32
 	onuid   uint32
 	handler *pcap.Handle
@@ -45,6 +46,7 @@
 	return nil, err
 }
 
+// IdentifyNniIoinfo returns matched ioinfo
 func (s *Server) IdentifyNniIoinfo(ioloc string) (*Ioinfo, error) {
 	for _, ioinfo := range s.Ioinfos {
 		if ioinfo.iotype == "nni" && ioinfo.ioloc == ioloc {
@@ -56,6 +58,7 @@
 	return nil, err
 }
 
+// CreateVethPairs creates veth pairs with given names
 func CreateVethPairs(veth1 string, veth2 string) (err error) {
 	err = exec.Command("ip", "link", "add", veth1, "type", "veth", "peer", "name", veth2).Run()
 	if err != nil {
@@ -80,6 +83,7 @@
 	return
 }
 
+// RemoveVeth deletes veth by given name
 func RemoveVeth(name string) error {
 	err := exec.Command("ip", "link", "del", name).Run()
 	if err != nil {
@@ -89,6 +93,7 @@
 	return err
 }
 
+// RemoveVeths deletes veth
 func RemoveVeths(names []string) {
 	for _, name := range names {
 		RemoveVeth(name)
diff --git a/core/io_worker.go b/core/io_worker.go
index 0496b9c..c59b903 100644
--- a/core/io_worker.go
+++ b/core/io_worker.go
@@ -18,24 +18,23 @@
 
 import (
 	"errors"
-	"net"
-	"strconv"
-	"time"
-
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	"gerrit.opencord.org/voltha-bbsim/common/utils"
+	"gerrit.opencord.org/voltha-bbsim/device"
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	"github.com/google/gopacket/pcap"
-	"gerrit.opencord.org/voltha-bbsim/device"
-	"gerrit.opencord.org/voltha-bbsim/common/utils"
+	"net"
+	"strconv"
 )
 
+// RecvWorker receives the packet and forwards to the channel
 func RecvWorker(io *Ioinfo, handler *pcap.Handle, r chan Packet) {
 	logger.Debug("recvWorker runs. handler: %v", *handler)
 	packetSource := gopacket.NewPacketSource(handler, handler.LinkType())
 	for packet := range packetSource.Packets() {
 		logger.Debug("recv packet from IF: %v ", *handler)
-		//logger.Println(packet.Dump())
+		// logger.Println(packet.Dump())
 		pkt := Packet{}
 		pkt.Info = io
 		pkt.Pkt = packet
@@ -43,6 +42,7 @@
 	}
 }
 
+// SendUni sends packet to UNI interface
 func SendUni(handle *pcap.Handle, packet gopacket.Packet, onu *device.Onu) {
 	err := handle.WritePacketData(packet.Data())
 	if err != nil {
@@ -51,15 +51,17 @@
 	utils.LoggerWithOnu(onu).Debugf("Successfully send packet to UNI-IF: %v", *handle)
 }
 
+// SendNni sends packaet to NNI interface
 func SendNni(handle *pcap.Handle, packet gopacket.Packet) {
 	err := handle.WritePacketData(packet.Data())
 	if err != nil {
 		logger.Error("Error in send packet to NNI e:%s", err)
 	}
 	logger.Debug("send packet to NNI-IF: %v ", *handle)
-	//logger.Println(packet.Dump())
+	// logger.Println(packet.Dump())
 }
 
+// PopVLAN pops the vlan ans return packet
 func PopVLAN(pkt gopacket.Packet) (gopacket.Packet, uint16, error) {
 	if layer := getDot1QLayer(pkt); layer != nil {
 		if eth := getEthernetLayer(pkt); eth != nil {
@@ -84,9 +86,10 @@
 		}
 	}
 	return pkt, 0, nil
-	//return nil, 0, errors.New("failed to pop vlan")
+	// return nil, 0, errors.New("failed to pop vlan")
 }
 
+// PushVLAN pushes the vlan header to the packet and returns tha packet
 func PushVLAN(pkt gopacket.Packet, vid uint16, onu *device.Onu) (gopacket.Packet, error) {
 	if eth := getEthernetLayer(pkt); eth != nil {
 		ethernetLayer := &layers.Ethernet{
@@ -145,8 +148,8 @@
 }
 
 func makeNniName(oltid uint32) (upif string, dwif string) {
-	upif = NNI_VETH_NORTH_PFX + strconv.Itoa(int(oltid))
-	dwif = NNI_VETH_SOUTH_PFX + strconv.Itoa(int(oltid))
+	upif = NniVethNorthPfx + strconv.Itoa(int(oltid))
+	dwif = NniVethSouthPfx + strconv.Itoa(int(oltid))
 	return
 }
 
@@ -170,13 +173,13 @@
 
 func getVethHandler(vethname string) (*pcap.Handle, error) {
 	var (
-		device       string = vethname
-		snapshot_len int32  = 1518
-		promiscuous  bool   = false
-		err          error
-		timeout      time.Duration = pcap.BlockForever
+		device            = vethname
+		snapshotLen int32 = 1518
+		promiscuous       = false
+		err         error
+		timeout           = pcap.BlockForever
 	)
-	handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
+	handle, err := pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
 	if err != nil {
 		return nil, err
 	}
diff --git a/core/mediator.go b/core/mediator.go
index b1d098d..c4cdc21 100644
--- a/core/mediator.go
+++ b/core/mediator.go
@@ -18,17 +18,16 @@
 
 import (
 	"flag"
-	"fmt"
 	"os"
 	"os/signal"
+	"reflect"
 	"strconv"
 	"strings"
 	"sync"
 
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
-	log "github.com/sirupsen/logrus"
 	"gerrit.opencord.org/voltha-bbsim/device"
-	"reflect"
+	log "github.com/sirupsen/logrus"
 )
 
 const (
@@ -37,21 +36,28 @@
 	BOTH
 )
 
+// Store emulation mode
 type Mode int
 
+// AutoONUActivate is flag for Auto ONU Add on/off.
+var AutoONUActivate int
+
 type option struct {
-	address     string
-	port        uint32
-	oltid       uint32
-	npon        uint32
-	nonus       uint32
-	aaawait     int
-	dhcpwait    int
-	dhcpservip  string
-	intvl       int
-	Mode        Mode
-	KafkaBroker string
-	Debuglvl	string
+	address                  string
+	port                     uint32
+	mgmtGrpcPort             uint32
+	mgmtRestPort             uint32
+	oltid                    uint32
+	npon                     uint32
+	nonus                    uint32
+	aaawait                  int
+	dhcpwait                 int
+	dhcpservip               string
+	intvl                    int
+	interactiveOnuActivation bool
+	Mode                     Mode
+	KafkaBroker              string
+	Debuglvl                 string
 }
 
 // GetOptions receives command line options and stores them in option structure
@@ -67,6 +73,9 @@
 	dhcpservip := flag.String("s", "182.21.0.128", "DHCP Server IP Address")
 	intvl := flag.Int("v", 1000, "Interval each Indication (ms)")
 	kafkaBroker := flag.String("k", "", "Kafka broker")
+	interactiveOnuActivation := flag.Bool("ia", false, "Enable interactive activation of ONUs")
+	mgmtGrpcPort := flag.Int("grpc", 50061, "BBSim API server gRPC port")
+	mgmtRestPort := flag.Int("rest", 50062, "BBSim API server REST port")
 	o.Mode = DEFAULT
 	debg := flag.String("d", "DEBUG", "Debug Level(TRACE DEBUG INFO WARN ERROR)")
 	flag.Parse()
@@ -83,10 +92,18 @@
 	o.dhcpwait = *dhcpwait
 	o.dhcpservip = *dhcpservip
 	o.intvl = *intvl
+	o.interactiveOnuActivation = *interactiveOnuActivation
 	o.KafkaBroker = *kafkaBroker
-	o.address = (strings.Split(*addressport, ":")[0])
+	o.address = strings.Split(*addressport, ":")[0]
 	tmp, _ := strconv.Atoi(strings.Split(*addressport, ":")[1])
 	o.port = uint32(tmp)
+	o.mgmtGrpcPort = uint32(*mgmtGrpcPort)
+	o.mgmtRestPort = uint32(*mgmtRestPort)
+
+	if o.interactiveOnuActivation == true {
+		log.Info("Automatic ONU activation disabled: use BBSim API to activate ONUs")
+	}
+
 	return o
 }
 
@@ -115,14 +132,11 @@
 	var wg sync.WaitGroup
 	opt := m.opt
 	server := NewCore(opt)
-	wg.Add(1)
-	go func() {
-		if err := server.Start(); err != nil { //Blocking
-			logger.Error("Start %s", err)
-		}
-		wg.Done()
-		return
-	}()
+	server.wg = &sync.WaitGroup{}
+	server.wg.Add(1)
+	go server.StartServerActionLoop(&wg)
+	server.serverActionCh <- "start"
+	go server.startMgmtServer(&wg)
 
 	tm := NewTestManager(opt)
 	m.server = server
@@ -140,15 +154,17 @@
 		}()
 		for sig := range c {
 			wg.Add(1)
-			fmt.Println("SIGINT", sig)
+			logger.Debug("SIGINT %v", sig)
 			close(c)
-			server.Stop() //Non-blocking
-			tm.Stop()     //Non-blocking
+			server.Stop() // Non-blocking
+			tm.Stop()     // Non-blocking
+			server.stopMgmtServer()
+			server.wg.Done()
 			return
 		}
 	}()
 	wg.Wait()
-	logger.Debug("Reach to the end line")
+	server.wg.Wait()
 }
 
 // Mediate method is invoked on OLT and ONU state change
@@ -158,7 +174,7 @@
 		next := sr.next
 		current := sr.current
 		dev := sr.device
-		if reflect.TypeOf(dev) == reflect.TypeOf(&device.Olt{}){
+		if reflect.TypeOf(dev) == reflect.TypeOf(&device.Olt{}) {
 			logger.Debug("Received OLT Device %v Current: %d Next: %d", dev, current, next)
 			if err := transitOlt(current, next, m.testmanager, m.opt); err != nil {
 				logger.Error("%v", err)
@@ -173,19 +189,22 @@
 	}
 }
 
-func transitOlt (current device.DeviceState, next device.DeviceState, tm *TestManager, o *option) error {
+func transitOlt(current device.DeviceState, next device.DeviceState, tm *TestManager, o *option) error {
 	logger.Debug("trnsitOlt called current:%d , next:%d", current, next)
 	if current == device.OLT_PREACTIVE && next == device.OLT_ACTIVE {
 		tm.Start()
 		nniup, _ := makeNniName(o.oltid)
 		activateDHCPServer(nniup, o.dhcpservip)
-	} else if current == device.OLT_ACTIVE && next == device.OLT_PREACTIVE{
+	} else if current == device.OLT_ACTIVE && next == device.OLT_PREACTIVE {
 		tm.Stop()
+	} else if current == device.OLT_ACTIVE && next == device.OLT_INACTIVE {
+		// Reboot case
+		// TODO Point of discussion
 	}
 	return nil
 }
 
-func transitOnu (key device.Devkey, current device.DeviceState, next device.DeviceState, tm *TestManager, o *option) error {
+func transitOnu(key device.Devkey, current device.DeviceState, next device.DeviceState, tm *TestManager, o *option) error {
 	logger.Debug("trnsitOnu called with key: %v, current: %d, next: %d", key, current, next)
 	if o.Mode == AAA || o.Mode == BOTH {
 		if current == device.ONU_ACTIVE && next == device.ONU_OMCIACTIVE {
@@ -200,7 +219,7 @@
 		}
 	}
 
-	if o.Mode == BOTH{
+	if o.Mode == BOTH {
 		if current == device.ONU_OMCIACTIVE && next == device.ONU_AUTHENTICATED {
 			t := tm.CreateTester("DHCP", o, key, activateDHCPClient, o.dhcpwait)
 			if err := tm.StartTester(t); err != nil {
diff --git a/core/omci.go b/core/omci.go
index 711bbc7..a83c07a 100644
--- a/core/omci.go
+++ b/core/omci.go
@@ -18,14 +18,13 @@
 
 import (
 	"context"
-
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
-	"gerrit.opencord.org/voltha-bbsim/protos"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
 	omci "github.com/opencord/omci-sim"
 )
 
 // RunOmciResponder starts a go routine to process/respond to OMCI messages from VOLTHA
-func RunOmciResponder(ctx context.Context, omciOut chan openolt.OmciMsg, omciIn chan openolt.OmciIndication, errch chan error) {
+func (s *Server) RunOmciResponder(ctx context.Context, omciOut chan openolt.OmciMsg, omciIn chan openolt.OmciIndication, errch chan error) {
 	go func() {
 		defer logger.Debug("Omci response process was done")
 
@@ -34,14 +33,17 @@
 		for {
 			select {
 			case m := <-omciOut:
-				resp_pkt, err := omci.OmciSim(m.IntfId, m.OnuId, HexDecode(m.Pkt))
+				respPkt, err := omci.OmciSim(m.IntfId, m.OnuId, HexDecode(m.Pkt))
 				switch err := err.(type) {
 				case nil:
 					// Success
 					resp.IntfId = m.IntfId
 					resp.OnuId = m.OnuId
-					resp.Pkt = resp_pkt
+					resp.Pkt = respPkt
 					omciIn <- resp
+					s.handleOmciAction(resp.Pkt, resp.IntfId, resp.OnuId)
+
+
 				case *omci.OmciError:
 					// Error in processing omci message. Log and carry on.
 					logger.Debug("%s", err.Msg)
@@ -72,3 +74,17 @@
 	return p
 }
 
+func (s *Server) handleOmciAction(pkt []byte, IntfID uint32, OnuID uint32) {
+	logger.Debug("handleOmciAction invoked")
+	MEClass := omci.OmciClass(uint16(pkt[5]) | uint16(pkt[4])<<8)
+	msgType := omci.OmciMsgType(pkt[2] & 0x1F)
+	logger.Debug("ME Class %d, msgType %d", MEClass, msgType)
+
+	if MEClass == omci.ONUG {
+		switch msgType {
+		case omci.Reboot:
+			logger.Info("ONU reboot recieved")
+			s.handleONUSoftReboot(IntfID, OnuID)
+		}
+	}
+}
diff --git a/core/openolt_service.go b/core/openolt_service.go
index c34468a..dc48577 100644
--- a/core/openolt_service.go
+++ b/core/openolt_service.go
@@ -17,11 +17,12 @@
 package core
 
 import (
+	"time"
+
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
 	"gerrit.opencord.org/voltha-bbsim/common/utils"
 	"gerrit.opencord.org/voltha-bbsim/device"
-	"gerrit.opencord.org/voltha-bbsim/protos"
-	"time"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
 )
 
 func sendOltIndUp(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
@@ -43,65 +44,95 @@
 }
 
 func sendIntfInd(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
-	for i := uint32(0); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
-		intf := olt.Intfs[i]
-		if intf.Type == "pon" { // There is no need to send IntfInd for NNI
-			data := &openolt.Indication_IntfInd{&openolt.IntfIndication{IntfId: intf.IntfID, OperState: intf.OperState}}
-			if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-				logger.Error("Failed to send Intf [id: %d] indication : %v", i, err)
-				return err
-			}
-			logger.Info("SendIntfInd olt:%d intf:%d (%s)", olt.ID, intf.IntfID, intf.Type)
+	// There is no need to send IntfInd for NNI
+	for i := uint32(0); i < olt.NumPonIntf; i++ {
+		intf := olt.PonIntfs[i]
+		data := &openolt.Indication_IntfInd{&openolt.IntfIndication{IntfId: intf.IntfID, OperState: intf.OperState}}
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			logger.Error("Failed to send Intf [id: %d] indication : %v", i, err)
+			return err
 		}
+		logger.Info("SendIntfInd olt:%d intf:%d (%s)", olt.ID, intf.IntfID, intf.Type)
 	}
 	return nil
 }
 
 func sendOperInd(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
-	for i := uint32(0); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
-		intf := olt.Intfs[i]
+	// Send OperInd for Nni
+	for i := uint32(0); i < olt.NumNniIntf; i++ {
+		intf := olt.NniIntfs[i]
 		data := &openolt.Indication_IntfOperInd{&openolt.IntfOperIndication{Type: intf.Type, IntfId: intf.IntfID, OperState: intf.OperState}}
 		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			logger.Error("Failed to send IntfOper [id: %d] indication : %v", i, err)
+			logger.Error("Failed to send NNI IntfOper [id: %d] indication : %v", i, err)
 			return err
 		}
-		logger.Info("SendOperInd olt:%d intf:%d (%s)", olt.ID, intf.IntfID, intf.Type)
+		logger.Info("SendOperInd NNI olt:%d intf:%d (%s)", olt.ID, intf.IntfID, intf.Type)
 	}
-	return nil
-}
 
-func sendOnuDiscInd(stream openolt.Openolt_EnableIndicationServer, onus []*device.Onu, delay int) error {
-	for i, onu := range onus {
-		data := &openolt.Indication_OnuDiscInd{&openolt.OnuDiscIndication{IntfId: onu.IntfID, SerialNumber: onu.SerialNumber}}
-		time.Sleep(time.Duration(delay) * time.Millisecond)
+	// Send OperInd for Pon
+	for i := uint32(0); i < olt.NumPonIntf; i++ {
+		intf := olt.PonIntfs[i]
+		data := &openolt.Indication_IntfOperInd{&openolt.IntfOperIndication{Type: intf.Type, IntfId: intf.IntfID, OperState: intf.OperState}}
 		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			logger.Error("Failed to send ONUDiscInd [id: %d]: %v", i, err)
+			logger.Error("Failed to send PON IntfOper [id: %d] indication : %v", i, err)
 			return err
 		}
-		utils.LoggerWithOnu(onu).Info("sendONUDiscInd Onuid")
+		logger.Info("SendOperInd PON olt:%d intf:%d (%s)", olt.ID, intf.IntfID, intf.Type)
 	}
 	return nil
 }
 
-func sendOnuInd(stream openolt.Openolt_EnableIndicationServer, onus []*device.Onu, delay int) error {
-	for i, onu := range onus {
-//		time.Sleep(time.Duration(delay) * time.Millisecond)
-		data := &openolt.Indication_OnuInd{&openolt.OnuIndication{IntfId: onu.IntfID, OnuId: onu.OnuID, OperState: "up", AdminState: "up", SerialNumber: onu.SerialNumber}}
-		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			logger.Error("Failed to send ONUInd [id: %d]: %v", i, err)
-			return err
-		}
-		utils.LoggerWithOnu(onu).Info("sendONUInd Onuid")
-	}
-	return nil
-}
-
-func sendOnuIndtoONU(stream openolt.Openolt_EnableIndicationServer, onu *device.Onu ) {
-	time.Sleep(time.Duration(10000) * time.Millisecond)    //TODO:This sleep added because of a known race condition in VOLTHA. Can be removed after fix.
-	data := &openolt.Indication_OnuInd{&openolt.OnuIndication{IntfId: onu.IntfID, OnuId: onu.OnuID, OperState: "up", AdminState: "up", SerialNumber: onu.SerialNumber}}
+func sendOnuDiscInd(stream openolt.Openolt_EnableIndicationServer, onu *device.Onu) error {
+	data := &openolt.Indication_OnuDiscInd{OnuDiscInd: &openolt.OnuDiscIndication{IntfId: onu.IntfID, SerialNumber: onu.SerialNumber}}
 	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			logger.Error("Failed to send ONUInd [id: %d]: %v", onu.OnuID, err)
+		logger.Error("Failed to send ONUDiscInd [id: %d]: %v", onu.OnuID, err)
+		return err
+	}
+	utils.LoggerWithOnu(onu).Info("sendONUDiscInd Onuid")
+	return nil
+}
+
+func sendOnuInd(stream openolt.Openolt_EnableIndicationServer, onu *device.Onu, delay int, operState string, adminState string) error {
+	time.Sleep(time.Duration(delay) * time.Millisecond)
+	data := &openolt.Indication_OnuInd{OnuInd: &openolt.OnuIndication{
+		IntfId:         onu.IntfID,
+		OnuId:          onu.OnuID,
+		OperState:      operState,
+		AdminState:     adminState,
+		SerialNumber:   onu.SerialNumber,
+	}}
+	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+		logger.Error("Failed to send ONUInd [id: %d]: %v", onu.OnuID, err)
+		return err
 	}
 	utils.LoggerWithOnu(onu).Info("sendONUInd Onuid")
+	return nil
+}
 
+func sendDyingGaspInd(stream openolt.Openolt_EnableIndicationServer, intfID uint32, onuID uint32) error {
+	// Form DyingGasp Indication with ONU-ID and Intf-ID
+	alarmData := &openolt.AlarmIndication_DyingGaspInd{DyingGaspInd: &openolt.DyingGaspIndication{IntfId: intfID, OnuId: onuID, Status: "on"}}
+	data := &openolt.Indication_AlarmInd{AlarmInd: &openolt.AlarmIndication{Data: alarmData}}
+
+	// Send Indication to VOLTHA
+	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+		logger.Error("Failed to send DyingGaspInd : %v", err)
+		return err
+	}
+	logger.Info("sendDyingGaspInd Intf-ID:%d ONU-ID:%d", intfID, onuID)
+	return nil
+}
+
+func startAlarmLoop(stream openolt.Openolt_EnableIndicationServer, alarmchan chan *openolt.Indication) {
+	logger.Debug("SendAlarm() Invoked")
+	for {
+		select {
+		case ind := <-alarmchan:
+			logger.Debug("Alarm recieved at alarm-channel to send to voltha %+v", ind)
+			err := stream.Send(ind)
+			if err != nil {
+				logger.Debug("Error occured while sending alarm indication %v", err)
+			}
+		}
+	}
 }
diff --git a/core/tester.go b/core/tester.go
index 4d1118e..fe2aa39 100644
--- a/core/tester.go
+++ b/core/tester.go
@@ -18,14 +18,16 @@
 
 import (
 	"context"
-	"os/exec"
-	"gerrit.opencord.org/voltha-bbsim/common/logger"
-	"time"
-	"strconv"
-	"gerrit.opencord.org/voltha-bbsim/device"
 	"fmt"
+	"os/exec"
+	"strconv"
+	"time"
+
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	"gerrit.opencord.org/voltha-bbsim/device"
 )
 
+// TestManager is the structure for test manager
 type TestManager struct {
 	DhcpServerIP string
 	Pid          []int
@@ -34,22 +36,25 @@
 	cancel       context.CancelFunc
 }
 
+// Tester is the structure for Test
 type Tester struct {
-	Type string
-	Key device.Devkey
+	Type     string
+	Key      device.Devkey
 	Testfunc func(device.Devkey) error
 	Waitsec  int
-	ctx          context.Context
-	cancel       context.CancelFunc
+	ctx      context.Context
+	cancel   context.CancelFunc
 }
 
+// NewTestManager returns new TestManager
 func NewTestManager(opt *option) *TestManager {
 	t := new(TestManager)
 	t.DhcpServerIP = opt.dhcpservip
 	return t
 }
 
-func (*TestManager) CreateTester(testtype string, opt *option, key device.Devkey, fn func(device.Devkey) error, waitsec int) *Tester{
+// CreateTester creates instance of Tester
+func (*TestManager) CreateTester(testtype string, opt *option, key device.Devkey, fn func(device.Devkey) error, waitsec int) *Tester {
 	logger.Debug("CreateTester() called")
 	t := new(Tester)
 	t.Type = testtype
@@ -59,7 +64,7 @@
 	return t
 }
 
-//Blocking
+// Start does starting action - Blocking Call
 func (tm *TestManager) Start() error {
 	ctx, cancel := context.WithCancel(context.Background())
 	tm.ctx = ctx
@@ -71,6 +76,7 @@
 	return nil
 }
 
+// Stop does stopping action
 func (tm *TestManager) Stop() error {
 	if tm.cancel != nil {
 		tm.cancel()
@@ -80,7 +86,7 @@
 	return nil
 }
 
-func (tm *TestManager) StartTester (t *Tester) error {
+func (tm *TestManager) StartTester(t *Tester) error {
 	testtype := t.Type
 	key := t.Key
 	waitsec := t.Waitsec
@@ -99,10 +105,10 @@
 
 	L:
 		for counter < waitsec {
-			select{
+			select {
 			case <-tick.C:
-				counter ++
-				if counter == waitsec {	// TODO: This should be fixed
+				counter++
+				if counter == waitsec { // TODO: This should be fixed
 					break L
 				}
 			case <-child.Done():
@@ -120,22 +126,25 @@
 	return nil
 }
 
-func (tm *TestManager) StopTester (testtype string, key device.Devkey) error {
+// StopTester stops the test
+func (tm *TestManager) StopTester(testtype string, key device.Devkey) error {
 	ts := tm.testers[testtype][key]
 	ts.cancel()
 	delete(tm.testers[testtype], key)
 	return nil
 }
 
+// Initialize test manager
 func (tm *TestManager) Initialize() {
 	logger.Info("TestManager Initialize () called")
 	pids := tm.Pid
 	logger.Debug("Runnig Process: %v", pids)
 	KillProcesses(pids)
-	exec.Command("rm", "/var/run/dhcpd.pid").Run()    //This is for DHCP server activation
-	exec.Command("touch", "/var/run/dhcpd.pid").Run() //This is for DHCP server activation
+	exec.Command("rm", "/var/run/dhcpd.pid").Run()    // This is for DHCP server activation
+	exec.Command("touch", "/var/run/dhcpd.pid").Run() // This is for DHCP server activation
 }
 
+// KillProcesses kill process by specified pid
 func KillProcesses(pids []int) error {
 	for _, pname := range pids {
 		killProcess(pname)
@@ -170,7 +179,7 @@
 	return nil
 }
 
-func activateDHCPServer (veth string, serverip string) error {
+func activateDHCPServer(veth string, serverip string) error {
 	err := exec.Command("ip", "addr", "add", serverip, "dev", veth).Run()
 	if err != nil {
 		logger.Error("Fail to add ip to %s address: %s", veth, err)
@@ -191,4 +200,4 @@
 	}
 	logger.Info("DHCP Server is successfully activated !")
 	return err
-}
\ No newline at end of file
+}
diff --git a/device/device_olt.go b/device/device_olt.go
index daffcec..5f34a03 100644
--- a/device/device_olt.go
+++ b/device/device_olt.go
@@ -31,8 +31,9 @@
 	GetDevkey() Devkey
 }
 
+// Devkey key for OLT/ONU devices
 type Devkey struct {
-	ID uint32
+	ID     uint32
 	Intfid uint32
 }
 
@@ -47,30 +48,68 @@
 	Name               string
 	InternalState      DeviceState
 	OperState          string
-	Intfs              []intf
+	NniIntfs		   []nniIntf
+	PonIntfs		   []ponIntf
 	HeartbeatSignature uint32
-	mu            *sync.Mutex
+	mu                 *sync.Mutex
 }
 
-type intf struct {
-	Type      string
-	IntfID    uint32
-	OperState string
+// AlarmState informs about the present state of the supported alarms
+type AlarmState uint32
+
+const (
+	// PonLosCleared alarm state for PON-LOS
+	PonLosCleared AlarmState = iota
+	// NniLosCleared alarm state for NNI-LOS
+	NniLosCleared
+	// PonLosRaised alarm state for PON-LOS
+	PonLosRaised
+	// NniLosRaised  for NNI-LOS
+	NniLosRaised
+)
+
+type ponIntf struct {
+	Type       string
+	IntfID     uint32
+	OperState  string
+	AlarmState AlarmState
 }
 
+type nniIntf struct {
+	Type       string
+	IntfID     uint32
+	OperState  string
+	AlarmState AlarmState
+}
+
+// Constants for port types
+const (
+	IntfPon = "pon"
+	IntfNni = "nni"
+)
+
 /* OltState
 OLT_INACTIVE -> OLT_PREACTIVE -> ACTIVE
-    (ActivateOLT)   (Enable)
+        (ActivateOLT)      (Enable)
        <-              <-
 */
 
+// Constants for OLT states
 const (
-	OLT_INACTIVE DeviceState  = iota // OLT/ONUs are not instantiated
-	OLT_PREACTIVE        // Before PacketInDaemon Running
-	OLT_ACTIVE            // After PacketInDaemon Running
+	OLT_INACTIVE  DeviceState = iota // OLT/ONUs are not instantiated
+	OLT_PREACTIVE                    // Before PacketInDaemon Running
+	OLT_ACTIVE                       // After PacketInDaemon Running
 )
 
-// NewOlt creates and return new Olt object
+// OLTAlarmStateToString is used to get alarm state as string
+var OLTAlarmStateToString = map[AlarmState]string{
+	PonLosCleared: "PonLosCleared",
+	NniLosCleared: "NniLosCleared",
+	PonLosRaised:  "PonLosRaised",
+	NniLosRaised:  "NniLosRaised",
+}
+
+// NewOlt initialises the new olt variable with the given values
 func NewOlt(oltid uint32, npon uint32, nnni uint32) *Olt {
 	olt := Olt{}
 	olt.ID = oltid
@@ -81,18 +120,21 @@
 	olt.OperState = "up"
 	olt.Manufacture = "BBSIM"
 	olt.SerialNumber = "BBSIMOLT00" + strconv.FormatInt(int64(oltid), 10)
-	olt.Intfs = make([]intf, olt.NumPonIntf+olt.NumNniIntf)
+	olt.NniIntfs = make([]nniIntf, olt.NumNniIntf)
+	olt.PonIntfs = make([]ponIntf, olt.NumPonIntf)
 	olt.HeartbeatSignature = oltid
 	olt.mu = &sync.Mutex{}
 	for i := uint32(0); i < olt.NumNniIntf; i++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type = "nni"
+		olt.NniIntfs[i].IntfID = i
+		olt.NniIntfs[i].OperState = "up"
+		olt.NniIntfs[i].Type = IntfNni
+		olt.NniIntfs[i].AlarmState = NniLosCleared
 	}
-	for i := uint32(olt.NumNniIntf); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type = "pon"
+	for i := uint32(0); i < olt.NumPonIntf; i++ {
+		olt.PonIntfs[i].IntfID = i
+		olt.PonIntfs[i].OperState = "up"
+		olt.PonIntfs[i].Type = IntfPon
+		olt.PonIntfs[i].AlarmState = PonLosCleared
 	}
 	return &olt
 }
@@ -102,14 +144,16 @@
 	olt.InternalState = OLT_INACTIVE
 	olt.OperState = "up"
 	for i := uint32(0); i < olt.NumNniIntf; i++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type = "nni"
+		olt.NniIntfs[i].IntfID = i
+		olt.NniIntfs[i].OperState = "up"
+		olt.NniIntfs[i].Type = IntfNni
+		olt.NniIntfs[i].AlarmState = NniLosCleared
 	}
-	for i := uint32(olt.NumNniIntf); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type = "pon"
+	for i := uint32(olt.NumNniIntf); i < olt.NumPonIntf; i++ {
+		olt.PonIntfs[i].IntfID = i
+		olt.PonIntfs[i].OperState = "up"
+		olt.PonIntfs[i].Type = IntfPon
+		olt.PonIntfs[i].AlarmState = PonLosCleared
 	}
 }
 
@@ -121,7 +165,7 @@
 }
 
 // GetDevkey returns device key of OLT
-func (olt *Olt) GetDevkey () Devkey {
+func (olt *Olt) GetDevkey() Devkey {
 	return Devkey{ID: olt.ID}
 }
 
@@ -130,4 +174,16 @@
 	olt.mu.Lock()
 	defer olt.mu.Unlock()
 	olt.InternalState = intstate
+}
+
+// UpdateNniPortState updates the status of the nni-port
+func (olt *Olt) UpdateNniPortState(portID uint32, alarmState AlarmState, operState string) {
+	olt.NniIntfs[portID].AlarmState = alarmState
+	olt.NniIntfs[portID].OperState = operState
+}
+
+// UpdatePonPortState updates the status of the pon-port
+func (olt *Olt) UpdatePonPortState(portID uint32, alarmState AlarmState, operState string) {
+	olt.PonIntfs[portID].AlarmState = alarmState
+	olt.PonIntfs[portID].OperState = operState
 }
\ No newline at end of file
diff --git a/device/device_onu.go b/device/device_onu.go
index f2338d7..0add821 100644
--- a/device/device_onu.go
+++ b/device/device_onu.go
@@ -21,18 +21,33 @@
 	"sync"
 
 	"gerrit.opencord.org/voltha-bbsim/common/logger"
-	"gerrit.opencord.org/voltha-bbsim/protos"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
 	log "github.com/sirupsen/logrus"
 )
 
+// Constants for the ONU states
 const (
-	ONU_INACTIVE   DeviceState = iota	//TODO: Each stage name should be more accurate
+	ONU_INACTIVE DeviceState = iota // TODO: Each stage name should be more accurate
 	ONU_ACTIVE
 	ONU_OMCIACTIVE
 	ONU_AUTHENTICATED
+	ONU_LOS_RAISED
+	ONU_OMCI_CHANNEL_LOS_RAISED
+	ONU_LOS_ON_OLT_PON_LOS // TODO give more suitable and crisp name
 	ONU_FREE
 )
 
+// ONUState maps int value of device state to string
+var ONUState = map[DeviceState]string{
+	ONU_INACTIVE:                "ONU_INACTIVE",
+	ONU_ACTIVE:                  "ONU_ACTIVE",
+	ONU_OMCIACTIVE:              "ONU_OMCIACTIVE",
+	ONU_FREE:                    "ONU_FREE",
+	ONU_LOS_RAISED:              "ONU_LOS_RAISED",
+	ONU_OMCI_CHANNEL_LOS_RAISED: "ONU_OMCI_CHANNEL_LOS_RAISED",
+	ONU_LOS_ON_OLT_PON_LOS:      "ONU_LOS_ON_OLT_PON_LOS",
+}
+
 // Onu structure stores information of ONUs
 type Onu struct {
 	InternalState DeviceState
@@ -42,6 +57,7 @@
 	SerialNumber  *openolt.SerialNumber
 	OnuID         uint32
 	GemportID     uint16
+	FlowIDs       []uint32
 	mu            *sync.Mutex
 }
 
@@ -54,9 +70,9 @@
 // NewOnus initializes and returns slice of Onu objects
 func NewOnus(oltid uint32, intfid uint32, nonus uint32, nnni uint32) []*Onu {
 	onus := []*Onu{}
-	for i := 0; i < int(nonus); i++ {
+	for i := 1; i <= int(nonus); i++ {
 		onu := Onu{}
-		onu.InternalState = ONU_FREE
+		onu.InternalState = ONU_FREE // New Onu Initialised with state ONU_FREE
 		onu.mu = &sync.Mutex{}
 		onu.IntfID = intfid
 		onu.OltID = oltid
@@ -94,14 +110,12 @@
 }
 
 // UpdateOnusOpStatus method updates ONU oper status
-func UpdateOnusOpStatus(ponif uint32, onus []*Onu, opstatus string) {
-	for _, onu := range onus {
-		onu.OperState = opstatus
-		logger.WithFields(log.Fields{
-			"onu":           onu.SerialNumber,
-			"pon_interface": ponif,
-		}).Info("ONU discovered.")
-	}
+func UpdateOnusOpStatus(ponif uint32, onu *Onu, opstatus string) {
+	onu.OperState = opstatus
+	logger.WithFields(log.Fields{
+		"onu":           onu.SerialNumber,
+		"pon_interface": ponif,
+	}).Info("ONU OperState Updated")
 }
 
 // UpdateIntState method updates ONU internal state
@@ -112,8 +126,8 @@
 }
 
 // GetDevkey returns ONU device key
-func (onu *Onu) GetDevkey () Devkey {
-	return Devkey{ID: onu.OnuID, Intfid:onu.IntfID}
+func (onu *Onu) GetDevkey() Devkey {
+	return Devkey{ID: onu.OnuID, Intfid: onu.IntfID}
 }
 
 // GetIntState returns ONU internal state
@@ -122,3 +136,17 @@
 	defer onu.mu.Unlock()
 	return onu.InternalState
 }
+
+// DeleteFlowID method search and delete flowID from the onu flowIDs slice
+func (onu *Onu) DeleteFlowID(flowID uint32) {
+	for pos, id := range onu.FlowIDs {
+		if id == flowID {
+			// delete the flowID by shifting all flowIDs by one
+			onu.FlowIDs = append(onu.FlowIDs[:pos], onu.FlowIDs[pos+1:]...)
+			t := make([]uint32, len(onu.FlowIDs))
+			copy(t, onu.FlowIDs)
+			onu.FlowIDs = t
+			break
+		}
+	}
+}
diff --git a/flow/flow.go b/flow/flow.go
new file mode 100644
index 0000000..f32e048
--- /dev/null
+++ b/flow/flow.go
@@ -0,0 +1,100 @@
+/*
+ * 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 flow
+
+import (
+	"gerrit.opencord.org/voltha-bbsim/common/logger"
+	openolt "gerrit.opencord.org/voltha-bbsim/protos"
+)
+
+var flowManager FlowManager
+
+// FlowManager interface for common methods of controller
+type FlowManager interface {
+	AddFlow(flow *openolt.Flow) error
+	DeleteFlow(flow *openolt.Flow) error
+	PortUp(portID uint32) error
+	PortDown(portID uint32) error
+	GetFlow(onuID uint32) ([]*openolt.Flow, error)
+}
+
+// DefaultFlowController empty struct
+type DefaultFlowController struct {
+}
+
+// InitializeFlowManager starts godc controller
+func InitializeFlowManager(OltID uint32) {
+	// Initialize flow controller as per custom implementation
+	logger.Debug("InitializeFlowManager for OLT %d", OltID)
+	flowManager = InitializeDefaultFlowController()
+	return
+}
+
+// AddFlow abstracts actual implementation of flow addition
+func AddFlow(flow *openolt.Flow) error {
+	return flowManager.AddFlow(flow)
+}
+
+// DeleteFlow abstracts actual implementation of flow deletion
+func DeleteFlow(flow *openolt.Flow) error {
+	return flowManager.DeleteFlow(flow)
+}
+
+// PortUp abstracts actual implementation of port up
+func PortUp(portID uint32) error {
+	return flowManager.PortUp(portID)
+}
+
+// PortDown abstracts actual implementation of port down
+func PortDown(portID uint32) error {
+	return flowManager.PortDown(portID)
+}
+
+// InitializeDefaultFlowController method to initialize default controller
+func InitializeDefaultFlowController() FlowManager {
+	logger.Debug("Default controller initialized")
+	return new(DefaultFlowController)
+}
+
+// AddFlow method implemented for DefaultFlowController
+func (fc *DefaultFlowController) AddFlow(flow *openolt.Flow) error {
+	logger.Debug("AddFlow invoked %v", flow)
+	return nil
+}
+
+// DeleteFlow implemented for DefaultFlowController
+func (fc *DefaultFlowController) DeleteFlow(flow *openolt.Flow) error {
+	logger.Debug("DeleteFlow invoked %v", flow)
+	return nil
+}
+
+// GetFlow implemented for DefaultFlowController
+func (fc *DefaultFlowController) GetFlow(onuID uint32) ([]*openolt.Flow, error) {
+	return nil, nil
+}
+
+// PortUp implemented for DefaultFlowController
+func (fc *DefaultFlowController) PortUp(portID uint32) error {
+	logger.Debug("PortUp invoked %d", portID)
+	return nil
+}
+
+// PortDown implemented for DefaultFlowController
+func (fc *DefaultFlowController) PortDown(portID uint32) error {
+	logger.Debug("PortDown invoked %d", portID)
+	return nil
+}