Merge "Changing output format for the BBSim scale pipeline plot"
diff --git a/Makefile b/Makefile
index 036735f..ee2b460 100644
--- a/Makefile
+++ b/Makefile
@@ -14,19 +14,44 @@
 
 VERSION     ?= $(shell cat ./VERSION)
 DIFF		?= $(git diff --shortstat 2> /dev/null | tail -n1)
-GIT_STATUS	?= $(shell [[ $DIFF != "" ]] && echo "Dirty" || echo "Clean")
+GIT_STATUS	?= $(shell [ -z "$DIFF" ] && echo "Dirty" || echo "Clean")
 
 ## Docker related
 DOCKER_TAG  			?= ${VERSION}
 DOCKER_REPOSITORY  		?= ""
 DOCKER_REGISTRY 		?= ""
 DOCKER_RUN_ARGS			?= ""
+DOCKER_PORTS			?= -p 50070:50070 -p 50060:50060 -p 50071:50071 -p 50072:50072 -p 50073:50073
+
+## protobuf related
+VOLTHA_PROTOS			?= $(shell GO111MODULE=on go list -f '{{ .Dir }}' -m github.com/opencord/voltha-protos)
+GOOGLEAPI				?= $(shell GO111MODULE=on go list -f '{{ .Dir }}' -m github.com/grpc-ecosystem/grpc-gateway)
+TOOLS_DIR := tools
+TOOLS_BIN := $(TOOLS_DIR)/bin/
+
+export PATH=$(shell echo $$PATH):$(PWD)/$(TOOLS_BIN)
 
 # Public targets
-
 all: help
 
-protos: api/bbsim/bbsim.pb.go # @HELP Build proto files
+# go installed tools.go
+GO_TOOLS := github.com/golang/protobuf/protoc-gen-go \
+            github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
+            github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger
+
+# tools
+GO_TOOLS_BIN := $(addprefix $(TOOLS_BIN), $(notdir $(GO_TOOLS)))
+GO_TOOLS_VENDOR := $(addprefix vendor/, $(GO_TOOLS))
+
+TEST_PACKAGES := github.com/opencord/bbsim/cmd/... \
+                 github.com/opencord/bbsim/internal/...
+
+setup_tools: $(GO_TOOLS_BIN)
+
+$(GO_TOOLS_BIN): $(GO_TOOLS_VENDOR)
+	GO111MODULE=on GOBIN="$(PWD)/$(TOOLS_BIN)" go install -mod=vendor $(GO_TOOLS)
+
+protos: dep setup_tools api/bbsim/bbsim.pb.go api/bbsim/bbsim.pb.gw.go api/legacy/bbsim.pb.go api/legacy/bbsim.pb.gw.go # @HELP Build proto files
 
 dep: # @HELP Download the dependencies to the vendor folder
 	GO111MODULE=on go mod vendor
@@ -39,7 +64,7 @@
 	docker run --rm -v $(shell pwd):/bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim-builder:${DOCKER_TAG} /bin/sh -c "cd /bbsim; make _build"
 
 test: dep protos fmt # @HELP Execute unit tests
-	GO111MODULE=on go test -v -mod vendor ./... -covermode count -coverprofile ./tests/results/go-test-coverage.out 2>&1 | tee ./tests/results/go-test-results.out
+	GO111MODULE=on go test -v -mod vendor $(TEST_PACKAGES) -covermode count -coverprofile ./tests/results/go-test-coverage.out 2>&1 | tee ./tests/results/go-test-results.out
 	go-junit-report < ./tests/results/go-test-results.out > ./tests/results/go-test-results.xml
 	gocover-cobertura < ./tests/results/go-test-coverage.out > ./tests/results/go-test-coverage.xml
 
@@ -53,15 +78,15 @@
 	docker push ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG}
 
 docker-run: # @HELP Runs the container locally (available options: DOCKER_RUN_ARGS="-pon 2 -onu 2" make docker-run)
-	docker run -d -p 50070:50070 -p 50060:50060 --privileged --rm --name bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG} /app/bbsim ${DOCKER_RUN_ARGS}
+	docker run -d ${DOCKER_PORTS} --privileged --rm --name bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG} /app/bbsim ${DOCKER_RUN_ARGS}
 
 docker-run-dev: # @HELP Runs the container locally (intended for development purposes, not in detached mode)
-	docker run -p 50070:50070 -p 50060:50060 --privileged --rm --name bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG} /app/bbsim ${DOCKER_RUN_ARGS}
+	docker run ${DOCKER_PORTS} --privileged --rm --name bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG} /app/bbsim ${DOCKER_RUN_ARGS}
 
 .PHONY: docs
-docs: # @HELP Generate docs and opens them in the browser
-	pushd docs; make doc_venv; make html; popd
-	open docs/build/html/index.html
+docs: swagger # @HELP Generate docs and opens them in the browser
+	cd docs; make doc_venv; make html; cd -
+	@echo -e "\nBBSim documentation generated in file://${PWD}/docs/build/html/index.html"
 
 # Release related items
 # Generates binaries in $RELEASE_DIR with name $RELEASE_NAME-$RELEASE_OS_ARCH
@@ -79,7 +104,6 @@
 	export GOARCH=$(rel_arch) ;\
 	GO111MODULE=on go build -i -v -mod vendor \
         	-ldflags "-w -X main.buildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
-        		-X main.commitHash=$(shell git log --pretty=format:%H -n 1) \
         		-X main.gitStatus=${GIT_STATUS} \
         		-X main.version=${VERSION}" \
         	-o "$@" ./cmd/bbr
@@ -89,13 +113,13 @@
 	export GOARCH=$(rel_arch) ;\
 	GO111MODULE=on go build -i -v -mod vendor \
         	-ldflags "-w -X main.buildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
-        		-X main.commitHash=$(shell git log --pretty=format:%H -n 1) \
         		-X main.gitStatus=${GIT_STATUS} \
         		-X main.version=${VERSION}" \
         	-o "$@" ./cmd/bbsim
 
 .PHONY: release $(RELEASE_BBR_BINS) $(RELEASE_BBSIM_BINS)
 release: dep protos $(RELEASE_BBR_BINS) $(RELEASE_BBSIM_BINS) # @HELP Creates release ready bynaries for BBSimctl and BBR artifacts
+swagger: docs/swagger/bbsim/bbsim.swagger.json docs/swagger/leagacy/bbsim.swagger.json # @HELP Generate swagger documentation for BBSim API
 
 help: # @HELP Print the command options
 	@echo
@@ -139,16 +163,37 @@
 		./cmd/bbsimctl
 
 api/openolt/openolt.pb.go: api/openolt/openolt.proto
-	@protoc -I . \
-    	-I${GOPATH}/src \
-    	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
+	@protoc -I. \
+    	-I${GOOGLEAPI}/third_party/googleapis \
     	--go_out=plugins=grpc:./ \
     	$<
 
-api/bbsim/bbsim.pb.go: api/bbsim/bbsim.proto
-	@protoc -I . \
-    	-I${GOPATH}/src \
-    	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
+api/bbsim/bbsim.pb.go api/bbsim/bbsim.pb.gw.go: api/bbsim/bbsim.proto api/bbsim/bbsim.yaml
+	@protoc -I. \
+		-I${GOOGLEAPI}/third_party/googleapis \
     	--go_out=plugins=grpc:./ \
+		--grpc-gateway_out=logtostderr=true,grpc_api_configuration=api/bbsim/bbsim.yaml,allow_delete_body=true:./ \
     	$<
 
+api/legacy/bbsim.pb.go api/legacy/bbsim.pb.gw.go: api/legacy/bbsim.proto
+	@protoc -I. \
+		-I${GOOGLEAPI}/third_party/googleapis/ \
+		-I${GOOGLEAPI}/ \
+		-I${VOLTHA_PROTOS}/protos/ \
+    	--go_out=plugins=grpc:./ \
+		--grpc-gateway_out=logtostderr=true,allow_delete_body=true:./ \
+    	$<
+
+docs/swagger/bbsim/bbsim.swagger.json: api/bbsim/bbsim.yaml
+	@protoc -I ./api \
+	-I${GOOGLEAPI}/ \
+	-I${VOLTHA_PROTOS}/protos/ \
+	--swagger_out=logtostderr=true,allow_delete_body=true,grpc_api_configuration=$<:docs/swagger/ \
+	api/bbsim/bbsim.proto
+
+docs/swagger/leagacy/bbsim.swagger.json: api/legacy/bbsim.proto
+	@protoc -I ./api \
+	-I${GOOGLEAPI}/ \
+	-I${VOLTHA_PROTOS}/protos/ \
+	--swagger_out=logtostderr=true,allow_delete_body=true:docs/swagger/ \
+	$<
\ No newline at end of file
diff --git a/api/bbsim/bbsim.yaml b/api/bbsim/bbsim.yaml
new file mode 100644
index 0000000..484ad3c
--- /dev/null
+++ b/api/bbsim/bbsim.yaml
@@ -0,0 +1,30 @@
+# 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.
+
+type: google.api.Service
+config_version: 3
+
+http:
+  rules:
+  - selector: bbsim.BBSim.Version
+    get: "/v1/version"
+  - selector: bbsim.BBSim.GetOlt
+    get: "/v1/olt"
+    additional_bindings:
+      - get: "/v1/olt/status"
+  - selector: bbsim.BBSim.GetONUs
+    get: "/v1/olt/onus"
+  - selector: bbsim.BBSim.GetONU
+    get: "/v1/olt/onus/{SerialNumber}"
+
diff --git a/api/legacy/bbsim.proto b/api/legacy/bbsim.proto
new file mode 100644
index 0000000..fee9bec
--- /dev/null
+++ b/api/legacy/bbsim.proto
@@ -0,0 +1,232 @@
+// 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 legacy;
+
+import "google/api/annotations.proto";
+import "protoc-gen-swagger/options/annotations.proto";
+import "voltha_protos/openolt.proto";
+import "voltha_protos/tech_profile.proto";
+
+option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
+	info: {
+	title: "BBSim API";
+	version: "1.0";
+	contact: {
+		url: "http://opencord.org";
+		};
+	};
+	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 {
+	option deprecated = true;
+	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;
+    repeated uint32 gemports = 6;
+    Tconts tconts = 7;
+}
+
+// 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;
+	uint32 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 serial_number = 2;    // Device serial number
+	string action = 3;           // soft or hard reboot
+}
+
+message Tconts {
+    fixed32 uni_id = 4;
+	fixed32 port_no = 5;
+	repeated tech_profile.TrafficScheduler tconts = 3;
+}
+
+message Flows {
+    repeated openolt.Flow flows = 1;
+}
+
+message Empty {}
+
+service BBSimService {
+
+	// Get current status of OLT
+	rpc OLTStatus(Empty) returns (OLTStatusResponse) {
+		option deprecated = true;
+		option (google.api.http) = {
+			get : "/v0/olt"
+			additional_bindings {get : "/v0/olt/status"}
+		};
+	}
+
+	// Get status of a PON/NNI port
+	rpc PortStatus(PortInfo) returns (Ports) {
+		option deprecated = true;
+		option (google.api.http) = {
+			get : "/v0/olt/ports/{port_type}/{port_id}/status"
+		};
+	}
+
+	// Get status of all or specific ONUs
+	rpc ONUStatus(ONURequest) returns (ONUs) {
+		option deprecated = true;
+		option (google.api.http) = {
+			get : "/v0/olt/onus"
+			additional_bindings { get : "/v0/olt/onus/{onu.onu_serial}" }
+			additional_bindings { get : "/v0/olt/ports/{onu.pon_port_id}/onus/{onu.onu_id}" }
+			additional_bindings { get : "/v0/olt/ports/{onu.pon_port_id}/onus" }
+		};
+	}
+
+	// Single/bulk activate ONU(s) for specific PON port(s)
+	rpc ONUActivate(ONURequest) returns (BBSimResponse) {
+		option deprecated = true;
+		option (google.api.http) = {
+			post : "/v0/olt/onus"
+			body: "onus_batch"
+			additional_bindings { post : "/v0/olt/ports/{onu.pon_port_id}/onus" }
+			additional_bindings { post : "/v0/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 deprecated = true;
+		option (google.api.http) = {
+			delete : "/v0/olt/onus"
+			body: "onus_batch"
+			additional_bindings { delete: "/v0/olt/onus/{onu.onu_serial}" }
+			additional_bindings { delete: "/v0/olt/ports/{onu.pon_port_id}/onus" }
+			additional_bindings { delete: "/v0/olt/ports/{onu.pon_port_id}/onus/{onu.onu_id}" }
+		};
+	}
+
+	// Generate ONU related alarms
+	rpc GenerateONUAlarm(ONUAlarmRequest) returns (BBSimResponse) {
+		option deprecated = true;
+		option (google.api.http) = {
+			post : "/v0/olt/onus/{onu_serial}/alarms/{alarm_type}/{status}"
+		};
+	}
+
+	// Generate OLT related alarms
+	rpc GenerateOLTAlarm(OLTAlarmRequest) returns (BBSimResponse) {
+		option deprecated = true;
+		option (google.api.http) = {
+			post : "/v0/olt/ports/{port_type}/{port_id}/alarms/los/{status}"
+		};
+	}
+
+	// Perform actions on OLT/ONU devices (e.g. reboot)
+	rpc PerformDeviceAction(DeviceAction) returns (BBSimResponse) {
+		option deprecated = true;
+		option (google.api.http) = {
+			patch: "/v0/{device_type}/action/{action}"
+			additional_bindings { patch : "/v0/olt/{device_type}/{serial_number}/action/{action}"}
+		};
+	}
+
+	// Get flows
+	rpc GetFlows(ONUInfo) returns(Flows) {
+		option deprecated = true;
+	    option (google.api.http) = {
+	        get: "/v0/olt/flows"
+	        additional_bindings {get: "/v0/olt/onu/{onu_serial}/flows"}
+	    };
+	}
+}
diff --git a/build/package/Dockerfile b/build/package/Dockerfile
index 73f3cca..86e15bb 100644
--- a/build/package/Dockerfile
+++ b/build/package/Dockerfile
@@ -29,24 +29,21 @@
  && mv /tmp/protoc3/bin/* /usr/local/bin/ \
  && mv /tmp/protoc3/include/* /usr/local/include/ \
  && go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
+ && go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger \
  && go get -v github.com/golang/protobuf/protoc-gen-go
 
 WORKDIR /go/src/github.com/opencord/bbsim
 ENV GO111MODULE=on
 ENV GOPROXY=https://proxy.golang.org
 
-# get dependencies
-COPY go.mod go.sum ./
-COPY vendor ./vendor
-
 # build the protos
 COPY Makefile ./
 COPY api ./api
-RUN make protos
 
 # copy and build
 COPY . ./
-RUN go mod vendor -v  # we can't vendor dependencies unless the code is there
+RUN make dep  # we cannot vendor dependencies unless the code is there
+RUN make protos
 RUN make build-bbsim
 RUN make build-bbsimctl
 
@@ -73,4 +70,4 @@
 RUN chmod a+x /app/bbsim
 RUN chmod a+x /usr/bin/bbsimctl
 RUN bbsimctl completion bash >> $HOME/.bashrc
-CMD [ '/app/bbsim' ]
\ No newline at end of file
+CMD [ '/app/bbsim' ]
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index 53b6ae0..d8cfcf0 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -17,23 +17,30 @@
 package main
 
 import (
+	"context"
+	"net"
+	"net/http"
+	"os"
+	"os/signal"
+	"runtime/pprof"
+	"sync"
+	"syscall"
+
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
 	"github.com/opencord/bbsim/api/bbsim"
+	"github.com/opencord/bbsim/api/legacy"
 	"github.com/opencord/bbsim/internal/bbsim/api"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
 	"github.com/opencord/bbsim/internal/common"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/reflection"
-	"net"
-	"os"
-	"runtime/pprof"
-	"sync"
 )
 
 func startApiServer(channel chan bool, group *sync.WaitGroup) {
 	// TODO make configurable
 	address := "0.0.0.0:50070"
-	log.Debugf("APIServer Listening on: %v", address)
+	log.Debugf("APIServer listening on: %v", address)
 	lis, err := net.Listen("tcp", address)
 	if err != nil {
 		log.Fatalf("APIServer failed to listen: %v", err)
@@ -43,25 +50,84 @@
 
 	reflection.Register(grpcServer)
 
-	wg := sync.WaitGroup{}
-	wg.Add(1)
-
 	go grpcServer.Serve(lis)
+	go startApiRestServer(channel, group, address)
 
-	for {
-		_, ok := <-channel
-		if !ok {
-			// if the olt channel is closed, stop the gRPC server
-			log.Warnf("Stopping API gRPC server")
-			grpcServer.Stop()
-			wg.Done()
-			break
-		}
+	select {
+	case <-channel:
+		// if the api channel is closed, stop the gRPC server
+		grpcServer.Stop()
+		log.Warnf("Stopping API gRPC server")
 	}
 
-	wg.Wait()
 	group.Done()
-	return
+}
+
+// startApiRestServer method starts the REST server (grpc gateway) for BBSim.
+func startApiRestServer(channel chan bool, group *sync.WaitGroup, grpcAddress string) {
+	ctx := context.Background()
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	// TODO make configurable
+	address := "0.0.0.0:50071"
+
+	mux := runtime.NewServeMux()
+	opts := []grpc.DialOption{grpc.WithInsecure()}
+
+	if err := bbsim.RegisterBBSimHandlerFromEndpoint(ctx, mux, grpcAddress, opts); err != nil {
+		log.Errorf("Could not register API server: %v", err)
+		return
+	}
+
+	s := &http.Server{Addr: address, Handler: mux}
+
+	go func() {
+		log.Infof("REST API server listening on %s ...", address)
+		if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+			log.Errorf("Could not start API server: %v", err)
+			return
+		}
+	}()
+
+	select {
+	case <-channel:
+		log.Warnf("Stopping API REST server")
+		s.Shutdown(ctx)
+	}
+
+	group.Done()
+}
+
+// This server aims to provide compatibility with the previous BBSim version. It is deprecated and will be removed in the future.
+func startLegacyApiServer(channel chan bool, group *sync.WaitGroup) {
+	// TODO make configurable
+	grpcAddress := "0.0.0.0:50072"
+	restAddress := "0.0.0.0:50073"
+
+	log.Debugf("Legacy APIServer listening on: %v", grpcAddress)
+	listener, err := net.Listen("tcp", grpcAddress)
+	if err != nil {
+		log.Fatalf("Legacy APIServer failed to listen: %v", err)
+		return
+	}
+	apiserver := grpc.NewServer()
+	legacy.RegisterBBSimServiceServer(apiserver, api.BBSimLegacyServer{})
+
+	go apiserver.Serve(listener)
+	// Start rest gateway for BBSim server
+	go api.StartRestGatewayService(channel, group, grpcAddress, restAddress)
+
+	select {
+	case <-channel:
+		// if the olt channel is closed, stop the gRPC server
+		log.Warnf("Stopping legacy API gRPC server")
+		apiserver.Stop()
+		break
+
+	}
+
+	group.Done()
 }
 
 func main() {
@@ -91,13 +157,26 @@
 	oltDoneChannel := make(chan bool)
 	apiDoneChannel := make(chan bool)
 
+	sigs := make(chan os.Signal, 1)
+	// stop API and OLT servers on SIGTERM
+	signal.Notify(sigs, syscall.SIGTERM)
+
+	go func() {
+		<-sigs
+		// TODO check when these servers should be shutdown
+		close(apiDoneChannel)
+		close(oltDoneChannel)
+	}()
+
 	wg := sync.WaitGroup{}
-	wg.Add(2)
+	wg.Add(5)
 
 	olt := devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, false)
 	go devices.StartOlt(olt, &wg)
 	log.Debugf("Created OLT with id: %d", options.OltID)
 	go startApiServer(apiDoneChannel, &wg)
+	go startLegacyApiServer(apiDoneChannel, &wg)
+
 	log.Debugf("Started APIService")
 
 	wg.Wait()
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 3496da1..2fdbc75 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -11,4 +11,5 @@
 sphinxcontrib-actdiag~=0.8.5
 sphinxcontrib-blockdiag~=1.5.5
 sphinxcontrib-nwdiag~=0.9.5
-sphinxcontrib-seqdiag~=0.8.5
\ No newline at end of file
+sphinxcontrib-seqdiag~=0.8.5
+sphinxcontrib-openapi~=0.5.0
\ No newline at end of file
diff --git a/docs/source/api.rst b/docs/source/api.rst
new file mode 100644
index 0000000..37c653b
--- /dev/null
+++ b/docs/source/api.rst
@@ -0,0 +1,17 @@
+.. _BBSimAPI:
+
+BBSimAPI
+--------
+
+BBSim exposes gRPC and REST APIs for external control of the simulated OLT. The API is defined using the protocol buffer specification in `api/bbsim/bbsim.proto`. 
+By default, the gRPC server is started on port 50070 and the REST server is started on port 50071. The following endpoints are currently defined:
+
+.. openapi:: ../swagger/bbsim/bbsim.swagger.json
+
+Legacy BBSimAPI
+---------------
+
+Additionally, a legacy API is available, defined in `api/legacy/bbsim.proto`. This API is deprecated and will be removed once BBSim reaches feature parity with the legacz version.
+By default, the legacy gRPC server is started on port 50072 and the corresponding REST server is started on port 50073. The following endpoints are currently defined:
+
+.. openapi:: ../swagger/legacy/bbsim.swagger.json
diff --git a/docs/source/conf.py b/docs/source/conf.py
index 0a9ac82..81910a3 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -53,6 +53,7 @@
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
+    'sphinxcontrib.openapi',
     'sphinx.ext.graphviz'
 ]
 
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 184fcfa..9acc4d5 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -15,6 +15,7 @@
    development-dependencies.rst
    bbr.rst
    bbsimctl.rst
+   api.rst
 
 
 Quickstart
diff --git a/docs/swagger/bbsim/.gitignore b/docs/swagger/bbsim/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/docs/swagger/bbsim/.gitignore
diff --git a/docs/swagger/bbsim/bbsim.swagger.json b/docs/swagger/bbsim/bbsim.swagger.json
new file mode 100644
index 0000000..e124c5c
--- /dev/null
+++ b/docs/swagger/bbsim/bbsim.swagger.json
@@ -0,0 +1,252 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "bbsim/bbsim.proto",
+    "version": "version not set"
+  },
+  "schemes": [
+    "http",
+    "https"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/v1/olt": {
+      "get": {
+        "operationId": "GetOlt",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/bbsimOlt"
+            }
+          }
+        },
+        "tags": [
+          "BBSim"
+        ]
+      }
+    },
+    "/v1/olt/onus": {
+      "get": {
+        "operationId": "GetONUs",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/bbsimONUs"
+            }
+          }
+        },
+        "tags": [
+          "BBSim"
+        ]
+      }
+    },
+    "/v1/olt/onus/{SerialNumber}": {
+      "get": {
+        "operationId": "GetONU",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/bbsimONU"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "SerialNumber",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSim"
+        ]
+      }
+    },
+    "/v1/olt/status": {
+      "get": {
+        "operationId": "GetOlt2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/bbsimOlt"
+            }
+          }
+        },
+        "tags": [
+          "BBSim"
+        ]
+      }
+    },
+    "/v1/version": {
+      "get": {
+        "operationId": "Version",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/bbsimVersionNumber"
+            }
+          }
+        },
+        "tags": [
+          "BBSim"
+        ]
+      }
+    }
+  },
+  "definitions": {
+    "bbsimLogLevel": {
+      "type": "object",
+      "properties": {
+        "level": {
+          "type": "string"
+        },
+        "caller": {
+          "type": "boolean",
+          "format": "boolean"
+        }
+      }
+    },
+    "bbsimNNIPort": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "OperState": {
+          "type": "string"
+        }
+      }
+    },
+    "bbsimONU": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "SerialNumber": {
+          "type": "string"
+        },
+        "OperState": {
+          "type": "string"
+        },
+        "InternalState": {
+          "type": "string"
+        },
+        "PonPortID": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "STag": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "CTag": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "HwAddress": {
+          "type": "string"
+        },
+        "PortNo": {
+          "type": "integer",
+          "format": "int32"
+        }
+      }
+    },
+    "bbsimONUs": {
+      "type": "object",
+      "properties": {
+        "items": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/bbsimONU"
+          }
+        }
+      }
+    },
+    "bbsimOlt": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "SerialNumber": {
+          "type": "string"
+        },
+        "OperState": {
+          "type": "string"
+        },
+        "InternalState": {
+          "type": "string"
+        },
+        "NNIPorts": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/bbsimNNIPort"
+          }
+        },
+        "PONPorts": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/bbsimPONPort"
+          }
+        }
+      }
+    },
+    "bbsimPONPort": {
+      "type": "object",
+      "properties": {
+        "ID": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "OperState": {
+          "type": "string"
+        }
+      }
+    },
+    "bbsimResponse": {
+      "type": "object",
+      "properties": {
+        "status_code": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "message": {
+          "type": "string"
+        }
+      }
+    },
+    "bbsimVersionNumber": {
+      "type": "object",
+      "properties": {
+        "version": {
+          "type": "string"
+        },
+        "buildTime": {
+          "type": "string"
+        },
+        "commitHash": {
+          "type": "string"
+        },
+        "gitStatus": {
+          "type": "string"
+        }
+      }
+    }
+  }
+}
diff --git a/docs/swagger/legacy/.gitignore b/docs/swagger/legacy/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/docs/swagger/legacy/.gitignore
diff --git a/docs/swagger/legacy/bbsim.swagger.json b/docs/swagger/legacy/bbsim.swagger.json
new file mode 100644
index 0000000..2873efd
--- /dev/null
+++ b/docs/swagger/legacy/bbsim.swagger.json
@@ -0,0 +1,1648 @@
+{
+  "swagger": "2.0",
+  "info": {
+    "title": "BBSim API",
+    "version": "1.0",
+    "contact": {
+      "url": "http://opencord.org"
+    }
+  },
+  "schemes": [
+    "http"
+  ],
+  "consumes": [
+    "application/json"
+  ],
+  "produces": [
+    "application/json"
+  ],
+  "paths": {
+    "/v0/olt": {
+      "get": {
+        "summary": "Get current status of OLT",
+        "operationId": "OLTStatus",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyOLTStatusResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/flows": {
+      "get": {
+        "summary": "Get flows",
+        "operationId": "GetFlows",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyFlows"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "pon_port_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/onu/{onu_serial}/flows": {
+      "get": {
+        "summary": "Get flows",
+        "operationId": "GetFlows2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyFlows"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu_serial",
+            "description": "ONU serial number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "pon_port_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/onus": {
+      "get": {
+        "summary": "Get status of all or specific ONUs",
+        "operationId": "ONUStatus",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.pon_port_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "delete": {
+        "summary": "Deactivate ONU(s) for specific PON port(s) specified by\na given onu_serial, onu_id, or pon_port_id",
+        "operationId": "ONUDeactivate",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "post": {
+        "summary": "Single/bulk activate ONU(s) for specific PON port(s)",
+        "operationId": "ONUActivate",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "body",
+            "in": "body",
+            "required": true,
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/onus/{onu.onu_serial}": {
+      "get": {
+        "summary": "Get status of all or specific ONUs",
+        "operationId": "ONUStatus2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.pon_port_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "delete": {
+        "summary": "Deactivate ONU(s) for specific PON port(s) specified by\na given onu_serial, onu_id, or pon_port_id",
+        "operationId": "ONUDeactivate2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.pon_port_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/onus/{onu_serial}/alarms/{alarm_type}/{status}": {
+      "post": {
+        "summary": "Generate ONU related alarms",
+        "operationId": "GenerateONUAlarm",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu_serial",
+            "description": "ONU serial number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "alarm_type",
+            "description": "Alarm types are:\n  \"signaldegrade\"\n  \"lossofomcichannel\"\n  \"lossofploam\"",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "status",
+            "description": "\"on\"/\"off\" indicates raised or cleared alarm",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/ports/{onu.pon_port_id}/onus": {
+      "get": {
+        "summary": "Get status of all or specific ONUs",
+        "operationId": "ONUStatus4",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "delete": {
+        "summary": "Deactivate ONU(s) for specific PON port(s) specified by\na given onu_serial, onu_id, or pon_port_id",
+        "operationId": "ONUDeactivate3",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "post": {
+        "summary": "Single/bulk activate ONU(s) for specific PON port(s)",
+        "operationId": "ONUActivate2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/ports/{onu.pon_port_id}/onus/{onu.onu_id}": {
+      "get": {
+        "summary": "Get status of all or specific ONUs",
+        "operationId": "ONUStatus3",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyONUs"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      },
+      "delete": {
+        "summary": "Deactivate ONU(s) for specific PON port(s) specified by\na given onu_serial, onu_id, or pon_port_id",
+        "operationId": "ONUDeactivate4",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.oper_state",
+            "description": "ONU oper state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.onu_state",
+            "description": "ONU internal state.",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "onu.gemports",
+            "in": "query",
+            "required": false,
+            "type": "array",
+            "items": {
+              "type": "integer",
+              "format": "int64"
+            },
+            "collectionFormat": "multi"
+          },
+          {
+            "name": "onu.tconts.uni_id",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.tconts.port_no",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/ports/{onu.pon_port_id}/onus/{onu.onu_serial}": {
+      "post": {
+        "summary": "Single/bulk activate ONU(s) for specific PON port(s)",
+        "operationId": "ONUActivate3",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "onu.pon_port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "onu.onu_serial",
+            "description": "ONU serial number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/ports/{port_type}/{port_id}/alarms/los/{status}": {
+      "post": {
+        "summary": "Generate OLT related alarms",
+        "operationId": "GenerateOLTAlarm",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "port_type",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "status",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/ports/{port_type}/{port_id}/status": {
+      "get": {
+        "summary": "Get status of a PON/NNI port",
+        "operationId": "PortStatus",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyPorts"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "port_type",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "port_id",
+            "in": "path",
+            "required": true,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "pon_port_max_onus",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "pon_port_active_onus",
+            "in": "query",
+            "required": false,
+            "type": "integer",
+            "format": "int64"
+          },
+          {
+            "name": "port_state",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          },
+          {
+            "name": "alarm_state",
+            "in": "query",
+            "required": false,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/status": {
+      "get": {
+        "summary": "Get current status of OLT",
+        "operationId": "OLTStatus2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyOLTStatusResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/olt/{device_type}/{serial_number}/action/{action}": {
+      "patch": {
+        "summary": "Perform actions on OLT/ONU devices (e.g. reboot)",
+        "operationId": "PerformDeviceAction2",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "device_type",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "serial_number",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "action",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    },
+    "/v0/{device_type}/action/{action}": {
+      "patch": {
+        "summary": "Perform actions on OLT/ONU devices (e.g. reboot)",
+        "operationId": "PerformDeviceAction",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/legacyBBSimResponse"
+            }
+          },
+          "404": {
+            "description": "Returned when the resource does not exist.",
+            "schema": {
+              "type": "string",
+              "format": "string"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "device_type",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          },
+          {
+            "name": "action",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "BBSimService"
+        ]
+      }
+    }
+  },
+  "definitions": {
+    "legacyBBSimResponse": {
+      "type": "object",
+      "properties": {
+        "status_msg": {
+          "type": "string"
+        }
+      },
+      "title": "BBSim response message"
+    },
+    "legacyFlows": {
+      "type": "object",
+      "properties": {
+        "flows": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/openoltFlow"
+          }
+        }
+      }
+    },
+    "legacyOLTInfo": {
+      "type": "object",
+      "properties": {
+        "olt_id": {
+          "type": "string",
+          "format": "int64"
+        },
+        "olt_serial": {
+          "type": "string"
+        },
+        "olt_ip": {
+          "type": "string"
+        },
+        "olt_state": {
+          "type": "string"
+        },
+        "olt_vendor": {
+          "type": "string"
+        }
+      },
+      "title": "OLT information"
+    },
+    "legacyOLTStatusResponse": {
+      "type": "object",
+      "properties": {
+        "olt": {
+          "$ref": "#/definitions/legacyOLTInfo"
+        },
+        "ports": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/legacyPortInfo"
+          }
+        }
+      },
+      "title": "BBSim status"
+    },
+    "legacyONUInfo": {
+      "type": "object",
+      "properties": {
+        "onu_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pon_port_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "onu_serial": {
+          "type": "string",
+          "title": "ONU serial number"
+        },
+        "oper_state": {
+          "type": "string",
+          "title": "ONU oper state"
+        },
+        "onu_state": {
+          "type": "string",
+          "title": "ONU internal state"
+        },
+        "gemports": {
+          "type": "array",
+          "items": {
+            "type": "integer",
+            "format": "int64"
+          }
+        },
+        "tconts": {
+          "$ref": "#/definitions/legacyTconts"
+        }
+      },
+      "title": "ONU information"
+    },
+    "legacyONUs": {
+      "type": "object",
+      "properties": {
+        "onus": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/legacyONUInfo"
+          }
+        }
+      },
+      "title": "Bulk ONU operations"
+    },
+    "legacyPortInfo": {
+      "type": "object",
+      "properties": {
+        "port_type": {
+          "type": "string"
+        },
+        "port_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pon_port_max_onus": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pon_port_active_onus": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "port_state": {
+          "type": "string"
+        },
+        "alarm_state": {
+          "type": "string"
+        }
+      },
+      "title": "Port information"
+    },
+    "legacyPorts": {
+      "type": "object",
+      "properties": {
+        "ports": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/legacyPortInfo"
+          }
+        }
+      },
+      "title": "Bulk port information"
+    },
+    "legacyTconts": {
+      "type": "object",
+      "properties": {
+        "uni_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "port_no": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "tconts": {
+          "type": "array",
+          "items": {
+            "$ref": "#/definitions/tech_profileTrafficScheduler"
+          }
+        }
+      }
+    },
+    "openoltAction": {
+      "type": "object",
+      "properties": {
+        "cmd": {
+          "$ref": "#/definitions/openoltActionCmd"
+        },
+        "o_vid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "o_pbits": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "o_tpid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_vid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_pbits": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_tpid": {
+          "type": "integer",
+          "format": "int64"
+        }
+      }
+    },
+    "openoltActionCmd": {
+      "type": "object",
+      "properties": {
+        "add_outer_tag": {
+          "type": "boolean",
+          "format": "boolean"
+        },
+        "remove_outer_tag": {
+          "type": "boolean",
+          "format": "boolean"
+        },
+        "trap_to_host": {
+          "type": "boolean",
+          "format": "boolean"
+        }
+      }
+    },
+    "openoltClassifier": {
+      "type": "object",
+      "properties": {
+        "o_tpid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "o_vid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_tpid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_vid": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "o_pbits": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "i_pbits": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "eth_type": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "dst_mac": {
+          "type": "string",
+          "format": "byte"
+        },
+        "src_mac": {
+          "type": "string",
+          "format": "byte"
+        },
+        "ip_proto": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "dst_ip": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "src_ip": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "src_port": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "dst_port": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pkt_tag_type": {
+          "type": "string"
+        }
+      }
+    },
+    "openoltFlow": {
+      "type": "object",
+      "properties": {
+        "access_intf_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "onu_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "uni_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "flow_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "flow_type": {
+          "type": "string"
+        },
+        "alloc_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "network_intf_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "gemport_id": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "classifier": {
+          "$ref": "#/definitions/openoltClassifier"
+        },
+        "action": {
+          "$ref": "#/definitions/openoltAction"
+        },
+        "priority": {
+          "type": "integer",
+          "format": "int32"
+        },
+        "cookie": {
+          "type": "string",
+          "format": "uint64"
+        },
+        "port_no": {
+          "type": "integer",
+          "format": "int64"
+        }
+      }
+    },
+    "tech_profileAdditionalBW": {
+      "type": "string",
+      "enum": [
+        "AdditionalBW_None",
+        "AdditionalBW_NA",
+        "AdditionalBW_BestEffort",
+        "AdditionalBW_Auto"
+      ],
+      "default": "AdditionalBW_None"
+    },
+    "tech_profileDirection": {
+      "type": "string",
+      "enum": [
+        "UPSTREAM",
+        "DOWNSTREAM",
+        "BIDIRECTIONAL"
+      ],
+      "default": "UPSTREAM"
+    },
+    "tech_profileInferredAdditionBWIndication": {
+      "type": "string",
+      "enum": [
+        "InferredAdditionBWIndication_None",
+        "InferredAdditionBWIndication_Assured",
+        "InferredAdditionBWIndication_BestEffort"
+      ],
+      "default": "InferredAdditionBWIndication_None"
+    },
+    "tech_profileSchedulerConfig": {
+      "type": "object",
+      "properties": {
+        "direction": {
+          "$ref": "#/definitions/tech_profileDirection"
+        },
+        "additional_bw": {
+          "$ref": "#/definitions/tech_profileAdditionalBW"
+        },
+        "priority": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "weight": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "sched_policy": {
+          "$ref": "#/definitions/tech_profileSchedulingPolicy"
+        }
+      }
+    },
+    "tech_profileSchedulingPolicy": {
+      "type": "string",
+      "enum": [
+        "WRR",
+        "StrictPriority",
+        "Hybrid"
+      ],
+      "default": "WRR"
+    },
+    "tech_profileTrafficScheduler": {
+      "type": "object",
+      "properties": {
+        "direction": {
+          "$ref": "#/definitions/tech_profileDirection"
+        },
+        "alloc_id": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "scheduler": {
+          "$ref": "#/definitions/tech_profileSchedulerConfig"
+        },
+        "traffic_shaping_info": {
+          "$ref": "#/definitions/tech_profileTrafficShapingInfo"
+        }
+      }
+    },
+    "tech_profileTrafficShapingInfo": {
+      "type": "object",
+      "properties": {
+        "cir": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "cbs": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pir": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "pbs": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "gir": {
+          "type": "integer",
+          "format": "int64"
+        },
+        "add_bw_ind": {
+          "$ref": "#/definitions/tech_profileInferredAdditionBWIndication"
+        }
+      }
+    }
+  }
+}
diff --git a/go.mod b/go.mod
index c3882e0..0e175f9 100644
--- a/go.mod
+++ b/go.mod
@@ -6,9 +6,9 @@
 	github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect
 	github.com/cboling/omci v0.1.0
 	github.com/deckarep/golang-set v1.7.1 // indirect
-	github.com/fullstorydev/grpcurl v1.3.2 // indirect
 	github.com/golang/protobuf v1.3.2
 	github.com/google/gopacket v1.1.17
+	github.com/grpc-ecosystem/grpc-gateway v1.11.3
 	github.com/jessevdk/go-flags v1.4.0
 	github.com/jhump/protoreflect v1.5.0
 	github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 // indirect
@@ -20,6 +20,8 @@
 	github.com/sirupsen/logrus v1.4.2
 	github.com/stretchr/testify v1.4.0 // indirect
 	github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
+	golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 // indirect
+	google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610
 	google.golang.org/grpc v1.22.1
 	gopkg.in/yaml.v2 v2.2.2
 	gotest.tools v2.2.0+incompatible
diff --git a/go.sum b/go.sum
index e0ebc88..84816cc 100644
--- a/go.sum
+++ b/go.sum
@@ -6,11 +6,13 @@
 github.com/cboling/omci v0.1.0/go.mod h1:qE+T+qTEh/U1UaMidFdMv1eDOJ45WTKTBp2QmEvsWGQ=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
 github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
-github.com/fullstorydev/grpcurl v1.3.2 h1:cJKWsBYMocdxXQvgbnhtLG810SL5MhKT4K7BagxRih8=
-github.com/fullstorydev/grpcurl v1.3.2/go.mod h1:kvk8xPCXOrwVd9zYdjy+xSOT4YWm6kyth4Y9NMfBns4=
+github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -21,63 +23,88 @@
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
 github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
+github.com/grpc-ecosystem/grpc-gateway v1.11.3 h1:h8+NsYENhxNTuq+dobk3+ODoJtwY4Fu0WQXsxJfL8aM=
+github.com/grpc-ecosystem/grpc-gateway v1.11.3/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
 github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
 github.com/jhump/protoreflect v1.5.0 h1:NgpVT+dX71c8hZnxHof2M7QDK7QtohIJ7DYycjnkyfc=
 github.com/jhump/protoreflect v1.5.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/looplab/fsm v0.1.0 h1:Qte7Zdn/5hBNbXzP7yxVU4OIFHWXBovyTT2LaBTyC20=
 github.com/looplab/fsm v0.1.0/go.mod h1:m2VaOfDHxqXBBMgc26m6yUOwkFn8H2AlJDE+jd/uafI=
 github.com/opencord/cordctl v0.0.0-20190909161711-01e9c1f04bf4 h1:Odib2px8tyALzdbyztAAqdxmpmQ/pJahJ7uz8kN/rvk=
 github.com/opencord/cordctl v0.0.0-20190909161711-01e9c1f04bf4/go.mod h1:/+3S0pwQUy7HeKnH0KfKp5W6hmh/LdZzuZTNT/m7vA4=
-github.com/opencord/omci-sim v0.0.0-20190717165025-5ff7bb17f1e9 h1:qUbCkEFUDEtOBeG6JfI2oMD6dmUa/zJldvBR59RhRdM=
-github.com/opencord/omci-sim v0.0.0-20190717165025-5ff7bb17f1e9/go.mod h1:W10NTwE0xK9Kh++wrflXPfkknMCaNKkb7Io/ihO4y3k=
-github.com/opencord/omci-sim v0.0.0-20190826212842-203314beba7e h1:LHHUbAUR0LyFEHxl1KhMbSTZMDs4HvDngwIpmvvFE6U=
-github.com/opencord/omci-sim v0.0.0-20190826212842-203314beba7e/go.mod h1:ToOkj7hkHgoet9XQDadKMhYqgA7qItZsi2j1Pk/mX6Y=
 github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252 h1:CMRqdJmtqku04ImHZW5NtdRlc6RRcdxLOn5Ep/b9HBg=
 github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252/go.mod h1:ToOkj7hkHgoet9XQDadKMhYqgA7qItZsi2j1Pk/mX6Y=
 github.com/opencord/voltha-protos v0.0.0-20190813191205-792553b747df h1:j/gaZts38ij2uVVikbXGqlm6n3hts1s0zWzUnBI96C4=
 github.com/opencord/voltha-protos v0.0.0-20190813191205-792553b747df/go.mod h1:MDGL9ai3XOPbiZ0tA8U7k4twK/T/P0Hh4gtjNxNk/qY=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
-github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
 github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
+golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
 golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610 h1:Ygq9/SRJX9+dU0WCIICM8RkWvDw03lvB77hrhJnpxfU=
+google.golang.org/genproto v0.0.0-20190716160619-c506a9f90610/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
 google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
diff --git a/internal/bbsim/api/grpc_api_server.go b/internal/bbsim/api/grpc_api_server.go
index 0c209f8..9df725b 100644
--- a/internal/bbsim/api/grpc_api_server.go
+++ b/internal/bbsim/api/grpc_api_server.go
@@ -18,6 +18,7 @@
 
 import (
 	"context"
+
 	"github.com/opencord/bbsim/api/bbsim"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
 	"github.com/opencord/bbsim/internal/common"
@@ -39,7 +40,7 @@
 }
 
 func (s BBSimServer) Version(ctx context.Context, req *bbsim.Empty) (*bbsim.VersionNumber, error) {
-	// TODO add a flag to specofy whether the tree was clean at this commit or not
+	// TODO add a flag to specify whether the tree was clean at this commit or not
 	return &bbsim.VersionNumber{
 		Version:    version,
 		BuildTime:  buildTime,
diff --git a/internal/bbsim/api/grpc_api_server_legacy.go b/internal/bbsim/api/grpc_api_server_legacy.go
new file mode 100644
index 0000000..0e9b67b
--- /dev/null
+++ b/internal/bbsim/api/grpc_api_server_legacy.go
@@ -0,0 +1,209 @@
+/*
+ * 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 api
+
+import (
+	"context"
+	"net/http"
+	"sync"
+
+	"github.com/grpc-ecosystem/grpc-gateway/runtime"
+	"github.com/opencord/bbsim/api/legacy"
+	"github.com/opencord/bbsim/internal/bbsim/devices"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+type BBSimLegacyServer struct {
+}
+
+// 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 the OLT status.
+func (s BBSimLegacyServer) OLTStatus(ctx context.Context, in *legacy.Empty) (*legacy.OLTStatusResponse, error) {
+	logger.Trace("OLTStatus request received")
+
+	olt := devices.GetOLT()
+	oltInfo := &legacy.OLTStatusResponse{
+		Olt: &legacy.OLTInfo{
+			OltId:     int64(olt.ID),
+			OltVendor: "BBSIM",
+			OltSerial: olt.SerialNumber,
+			// OltIp:     getOltIP().String(),  // TODO
+			OltState: olt.OperState.Current(),
+		},
+	}
+
+	for _, nni := range olt.Nnis {
+		nniPortInfo := &legacy.PortInfo{
+			PortType:  "nni",
+			PortId:    uint32(nni.ID),
+			PortState: nni.OperState.Current(),
+		}
+		oltInfo.Ports = append(oltInfo.Ports, nniPortInfo)
+	}
+
+	for _, pon := range olt.Pons {
+		ponPortInfo := &legacy.PortInfo{
+			PortType:          "pon",
+			PortId:            uint32(pon.ID),
+			PonPortMaxOnus:    uint32(olt.NumOnuPerPon),
+			PonPortActiveOnus: uint32(olt.NumPon),
+			PortState:         pon.OperState.Current(),
+		}
+		oltInfo.Ports = append(oltInfo.Ports, ponPortInfo)
+	}
+
+	return oltInfo, nil
+}
+
+// PortStatus method returns Port status.
+func (s BBSimLegacyServer) PortStatus(ctx context.Context, in *legacy.PortInfo) (*legacy.Ports, error) {
+	logger.Trace("PortStatus() invoked")
+	ports := &legacy.Ports{}
+	switch portType := in.GetPortType(); portType {
+	case "pon":
+		portInfo, err := s.fetchPortDetail(in.PortId, portType)
+		if err != nil {
+			return ports, err
+		}
+		ports.Ports = append(ports.Ports, portInfo)
+	case "nni":
+		portInfo, _ := s.fetchPortDetail(in.PortId, portType)
+		ports.Ports = append(ports.Ports, portInfo)
+	default:
+		return &legacy.Ports{}, status.Errorf(codes.InvalidArgument, "Invalid port type")
+	}
+
+	return ports, nil
+}
+
+// ONUStatus method returns ONU status.
+func (s BBSimLegacyServer) ONUStatus(ctx context.Context, in *legacy.ONURequest) (*legacy.ONUs, error) {
+	logger.Trace("ONUStatus request received")
+	onuInfo := &legacy.ONUs{}
+
+	if in.GetOnu() != nil {
+		logger.Debugf("Received single ONU: %+v, %d\n", in.GetOnu().OnuId, in.GetOnu().PonPortId)
+		return s.handleONUStatusRequest(in.GetOnu())
+	}
+
+	logger.Debug("Received all ONUs status request")
+
+	// Get status of all ONUs
+	olt := devices.GetOLT()
+	for _, p := range olt.Pons {
+		for _, o := range p.Onus {
+			onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(o))
+		}
+	}
+
+	return onuInfo, nil
+}
+
+// ONUActivate method handles ONU activate requests from user.
+func (s BBSimLegacyServer) ONUActivate(ctx context.Context, in *legacy.ONURequest) (*legacy.BBSimResponse, error) {
+	logger.Trace("ONUActivate request received")
+	logger.Error("Not implemented")
+
+	return &legacy.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// ONUDeactivate method handles ONU deactivation request.
+func (s BBSimLegacyServer) ONUDeactivate(ctx context.Context, in *legacy.ONURequest) (*legacy.BBSimResponse, error) {
+	logger.Info("ONUDeactivate request received")
+	logger.Error("Not implemented")
+
+	return &legacy.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// GenerateONUAlarm RPC generates alarm for the onu
+func (s BBSimLegacyServer) GenerateONUAlarm(ctx context.Context, in *legacy.ONUAlarmRequest) (*legacy.BBSimResponse, error) {
+	logger.Trace("GenerateONUAlarms() invoked")
+	logger.Error("Not implemented")
+
+	return nil, nil
+}
+
+// GenerateOLTAlarm RPC generates alarm for the OLT
+func (s BBSimLegacyServer) GenerateOLTAlarm(ctx context.Context, in *legacy.OLTAlarmRequest) (*legacy.BBSimResponse, error) {
+	logger.Trace("GenerateOLTAlarm() invoked")
+	logger.Error("Not implemented")
+
+	return &legacy.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// PerformDeviceAction rpc take the device request and performs OLT and ONU hard and soft reboot
+func (s BBSimLegacyServer) PerformDeviceAction(ctx context.Context, in *legacy.DeviceAction) (*legacy.BBSimResponse, error) {
+	logger.Trace("PerformDeviceAction() invoked")
+	logger.Error("Not implemented")
+
+	return &legacy.BBSimResponse{StatusMsg: RequestAccepted}, nil
+}
+
+// GetFlows returns all flows or flows for specified ONU
+func (s BBSimLegacyServer) GetFlows(ctx context.Context, in *legacy.ONUInfo) (*legacy.Flows, error) {
+	logger.Info("GetFlow request received")
+	logger.Error("Not implemented")
+
+	return &legacy.Flows{}, nil
+}
+
+// StartRestGatewayService method starts REST server for BBSim.
+func StartRestGatewayService(channel chan bool, group *sync.WaitGroup, grpcAddress string, hostandport string) {
+	ctx := context.Background()
+	ctx, cancel := context.WithCancel(ctx)
+	defer cancel()
+
+	mux := runtime.NewServeMux()
+	opts := []grpc.DialOption{grpc.WithInsecure()}
+
+	// Register REST endpoints
+	err := legacy.RegisterBBSimServiceHandlerFromEndpoint(ctx, mux, grpcAddress, opts)
+	if err != nil {
+		logger.Error("%v", err)
+		return
+	}
+
+	s := &http.Server{Addr: hostandport, Handler: mux}
+
+	go func() {
+		logger.Infof("legacy REST API server listening on %s ...", hostandport)
+		if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
+			logger.Errorf("Could not start legacy API server: %v", err)
+			return
+		}
+	}()
+
+	select {
+	case <-channel:
+		logger.Warnf("Stopping legacy API REST server")
+		s.Shutdown(ctx)
+		group.Done()
+	}
+	return
+}
diff --git a/internal/bbsim/api/legacy_api_handler.go b/internal/bbsim/api/legacy_api_handler.go
new file mode 100644
index 0000000..c56e963
--- /dev/null
+++ b/internal/bbsim/api/legacy_api_handler.go
@@ -0,0 +1,169 @@
+/*
+ * 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 api
+
+import (
+	"encoding/hex"
+	"errors"
+	"net"
+	"strconv"
+
+	"github.com/opencord/bbsim/api/legacy"
+	api "github.com/opencord/bbsim/api/legacy"
+	"github.com/opencord/bbsim/internal/bbsim/devices"
+	"github.com/opencord/voltha-protos/go/openolt"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+// Constants for reboot delays
+const (
+	OltRebootDelay     = 40
+	OnuSoftRebootDelay = 10
+	OnuHardRebootDelay = 30
+)
+
+// handleONUStatusRequest process ONU status request
+func (s BBSimLegacyServer) handleONUStatusRequest(in *api.ONUInfo) (*api.ONUs, error) {
+	logger.Trace("handleONUStatusRequest() invoked")
+	onuInfo := &api.ONUs{}
+	olt := devices.GetOLT()
+
+	if in.OnuSerial != "" { // Get status of a single ONU by SerialNumber
+		onu, err := olt.FindOnuBySn(in.OnuSerial)
+		if err != nil {
+			logger.Errorf("ONU error: %+v", err)
+			return onuInfo, status.Errorf(codes.NotFound, "Unable to retrieve ONU %s", in.OnuSerial)
+		}
+		onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+	} else {
+		if in.OnuId != 0 { // Get status of a single ONU by ONU-ID
+			onu, err := olt.FindOnuById(in.PonPortId, in.OnuId)
+			if err != nil {
+				logger.Errorf("ONU error: %+v", err)
+				return onuInfo, status.Errorf(codes.NotFound, "Unable to retrieve ONU with ID %d", in.OnuId)
+			}
+
+			onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(onu))
+		} else { // Get status of all ONUs
+			for _, p := range olt.Pons {
+				for _, o := range p.Onus {
+					onuInfo.Onus = append(onuInfo.Onus, copyONUInfo(o))
+				}
+			}
+		}
+	}
+
+	return onuInfo, nil
+}
+
+func copyONUInfo(onu *devices.Onu) *api.ONUInfo {
+	onuData := &api.ONUInfo{
+		OnuId:     onu.ID,
+		PonPortId: onu.PonPortID,
+		OnuSerial: onu.Sn(),
+		OnuState:  onu.InternalState.Current(),
+		OperState: onu.OperState.Current(),
+	}
+
+	return onuData
+}
+
+func (s BBSimLegacyServer) fetchPortDetail(intfID uint32, portType string) (*api.PortInfo, error) {
+	logger.Tracef("fetchPortDetail() invoked %s-%d", portType, intfID)
+	olt := devices.GetOLT()
+
+	switch portType {
+	case "pon":
+		for _, pon := range olt.Pons {
+			if pon.ID == intfID {
+				ponPortInfo := &api.PortInfo{
+					PortType:          "pon",
+					PortId:            uint32(pon.ID),
+					PonPortMaxOnus:    uint32(olt.NumOnuPerPon),
+					PonPortActiveOnus: uint32(olt.NumPon),
+					PortState:         pon.OperState.Current(),
+				}
+				return ponPortInfo, nil
+			}
+		}
+	case "nni":
+		for _, nni := range olt.Nnis {
+			if nni.ID == intfID {
+				nniPortInfo := &legacy.PortInfo{
+					PortType:  "nni",
+					PortId:    uint32(nni.ID),
+					PortState: nni.OperState.Current(),
+				}
+				return nniPortInfo, nil
+			}
+		}
+	}
+
+	portInfo := &api.PortInfo{}
+	return portInfo, status.Errorf(codes.NotFound, "Invalid port %s-%d", portType, intfID)
+}
+
+func getOpenoltSerialNumber(SerialNumber string) (*openolt.SerialNumber, error) {
+	var VendorIDLength = 4
+	var SerialNumberLength = 12
+
+	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
+}
+
+// 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
+}
+
+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/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index 3a36287..ad01f2f 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -74,34 +74,43 @@
 // sendNniPacket will send a packet out of the NNI interface.
 // We will send upstream only DHCP packets and drop anything else
 func sendNniPacket(packet gopacket.Packet) error {
-	if isDhcp := packetHandlers.IsDhcpPacket(packet); !isDhcp {
+	isDhcp := packetHandlers.IsDhcpPacket(packet)
+	isLldp := packetHandlers.IsLldpPacket(packet)
+
+	if isDhcp == false && isLldp == false {
 		nniLogger.WithFields(log.Fields{
 			"packet": packet,
 		}).Trace("Dropping NNI packet as it's not DHCP")
+		return nil
 	}
 
-	packet, err := packetHandlers.PopDoubleTag(packet)
-	if err != nil {
-		nniLogger.WithFields(log.Fields{
-			"packet": packet,
-		}).Errorf("Can't remove double tags from packet: %v", err)
-		return err
-	}
+	if isDhcp {
+		packet, err := packetHandlers.PopDoubleTag(packet)
+		if err != nil {
+			nniLogger.WithFields(log.Fields{
+				"packet": packet,
+			}).Errorf("Can't remove double tags from packet: %v", err)
+			return err
+		}
 
-	handle, err := getVethHandler(nniVeth)
-	if err != nil {
-		return err
-	}
+		handle, err := getVethHandler(nniVeth)
+		if err != nil {
+			return err
+		}
 
-	err = handle.WritePacketData(packet.Data())
-	if err != nil {
-		nniLogger.WithFields(log.Fields{
-			"packet": packet,
-		}).Errorf("Failed to send packet out of the NNI: %s", err)
-		return err
-	}
+		err = handle.WritePacketData(packet.Data())
+		if err != nil {
+			nniLogger.WithFields(log.Fields{
+				"packet": packet,
+			}).Errorf("Failed to send packet out of the NNI: %s", err)
+			return err
+		}
 
-	nniLogger.Infof("Sent packet out of NNI")
+		nniLogger.Infof("Sent packet out of NNI")
+	} else if isLldp {
+		// TODO rework this when BBSim supports data-plane packets
+		nniLogger.Trace("Received LLDP Packet, ignoring it")
+	}
 	return nil
 }
 
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index bfaa5fd..2a31eb2 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -20,6 +20,9 @@
 	"context"
 	"errors"
 	"fmt"
+	"net"
+	"sync"
+
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	"github.com/looplab/fsm"
@@ -30,8 +33,6 @@
 	"github.com/opencord/voltha-protos/go/tech_profile"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
-	"net"
-	"sync"
 )
 
 var oltLogger = log.WithFields(log.Fields{
@@ -436,7 +437,7 @@
 
 // returns an ONU with a given Serial Number
 func (o OltDevice) FindOnuBySn(serialNumber string) (*Onu, error) {
-	// TODO this function can be a perfoormance bottlenec when we have many ONUs,
+	// TODO this function can be a performance bottleneck when we have many ONUs,
 	// memoizing it will remove the bottleneck
 	for _, pon := range o.Pons {
 		for _, onu := range pon.Onus {
@@ -451,7 +452,7 @@
 
 // returns an ONU with a given interface/Onu Id
 func (o OltDevice) FindOnuById(intfId uint32, onuId uint32) (*Onu, error) {
-	// TODO this function can be a performance bottlenec when we have many ONUs,
+	// TODO this function can be a performance bottleneck when we have many ONUs,
 	// memoizing it will remove the bottleneck
 	for _, pon := range o.Pons {
 		if pon.ID == intfId {
@@ -467,7 +468,7 @@
 
 // returns an ONU with a given Mac Address
 func (o OltDevice) FindOnuByMacAddress(mac net.HardwareAddr) (*Onu, error) {
-	// TODO this function can be a perfoormance bottlenec when we have many ONUs,
+	// TODO this function can be a performance bottleneck when we have many ONUs,
 	// memoizing it will remove the bottleneck
 	for _, pon := range o.Pons {
 		for _, onu := range pon.Onus {
@@ -631,6 +632,7 @@
 	devinfo.FlowIdStart = 1
 	devinfo.FlowIdEnd = 16383
 	devinfo.DeviceSerialNumber = o.SerialNumber
+	devinfo.DeviceId = net.HardwareAddr{0xA, 0xA, 0xA, 0xA, 0xA, byte(o.ID)}.String()
 
 	return devinfo, nil
 }
diff --git a/internal/bbsim/devices/pon.go b/internal/bbsim/devices/pon.go
index f27510f..ac1bdb7 100644
--- a/internal/bbsim/devices/pon.go
+++ b/internal/bbsim/devices/pon.go
@@ -20,6 +20,7 @@
 	"bytes"
 	"errors"
 	"fmt"
+
 	"github.com/looplab/fsm"
 	"github.com/opencord/voltha-protos/go/openolt"
 )
diff --git a/internal/bbsim/packetHandlers/filters.go b/internal/bbsim/packetHandlers/filters.go
index b43b677..9ada88c 100644
--- a/internal/bbsim/packetHandlers/filters.go
+++ b/internal/bbsim/packetHandlers/filters.go
@@ -30,6 +30,13 @@
 	return false
 }
 
+func IsLldpPacket(pkt gopacket.Packet) bool {
+	if layer := pkt.Layer(layers.LayerTypeLinkLayerDiscovery); layer != nil {
+		return true
+	}
+	return false
+}
+
 // return true if the packet is coming in the OLT from the NNI port
 // it uses the ack to check if the source is the one we assigned to the
 // dhcp server
diff --git a/internal/bbsim/packetHandlers/filters_test.go b/internal/bbsim/packetHandlers/filters_test.go
index 6064e68..8d5f96a 100644
--- a/internal/bbsim/packetHandlers/filters_test.go
+++ b/internal/bbsim/packetHandlers/filters_test.go
@@ -62,6 +62,50 @@
 	assert.Equal(t, res, false)
 }
 
+func Test_IsLldpPacket_True(t *testing.T) {
+	layer := &layers.LinkLayerDiscovery{
+		PortID: layers.LLDPPortID{
+			ID:      []byte{1, 2, 3, 4},
+			Subtype: layers.LLDPPortIDSubtypeMACAddr,
+		},
+		ChassisID: layers.LLDPChassisID{
+			ID:      []byte{1, 2, 3, 4},
+			Subtype: layers.LLDPChassisIDSubTypeMACAddr,
+		},
+	}
+
+	buffer := gopacket.NewSerializeBuffer()
+	opts := gopacket.SerializeOptions{FixLengths: true}
+	err := gopacket.SerializeLayers(buffer, opts, layer)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	packet := gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeLinkLayerDiscovery, gopacket.DecodeOptions{})
+
+	res := packetHandlers.IsLldpPacket(packet)
+	assert.Equal(t, res, true)
+}
+
+func Test_IsLldpPacket_False(t *testing.T) {
+	eth := &layers.Ethernet{
+		DstMAC: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x15, 0x16},
+		SrcMAC: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x15, 0x17},
+	}
+
+	buffer := gopacket.NewSerializeBuffer()
+	opts := gopacket.SerializeOptions{FixLengths: true}
+	err := gopacket.SerializeLayers(buffer, opts, eth)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	ethernetPkt := gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.DecodeOptions{})
+
+	res := packetHandlers.IsLldpPacket(ethernetPkt)
+	assert.Equal(t, res, false)
+}
+
 func Test_IsIncomingPacket_True(t *testing.T) {
 	eth := &layers.IPv4{
 		SrcIP: net.ParseIP("192.168.254.1"),
diff --git a/tools/.gitignore b/tools/.gitignore
new file mode 100644
index 0000000..08c9828
--- /dev/null
+++ b/tools/.gitignore
@@ -0,0 +1,3 @@
+*
+!.gitignore
+!main.go
diff --git a/tools/main.go b/tools/main.go
new file mode 100644
index 0000000..b387490
--- /dev/null
+++ b/tools/main.go
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+// +build tools
+
+package tools
+
+import (
+	// protocol buffer compiler plugins
+	_ "github.com/golang/protobuf/protoc-gen-go"
+	_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
+	_ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger"
+)