VOL-291 : PON simulator refactoring for cluster integration
- Added ponsim build target in Makefile
- Added new option to vcore to select comm type with ponsim
- Modified all proto files to include destination go package
Amendments:
- Clean up based on review comments
- Properly close GRPC connections in ponsim_olt adapter
- Added voltha namespace to some k8s templates
Change-Id: I2f349fa7b3550a8a8cc8fc676cc896f33fbb9372
diff --git a/Makefile b/Makefile
index 1cba5c3..5d56ebd 100644
--- a/Makefile
+++ b/Makefile
@@ -79,6 +79,7 @@
grafana \
onos \
unum \
+ ponsim \
tester \
config-push \
j2 \
@@ -135,7 +136,7 @@
FETCH_IMAGE_LIST = $(shell echo $(FETCH_BUILD_IMAGE_LIST) $(FETCH_COMPOSE_IMAGE_LIST) $(FETCH_K8S_IMAGE_LIST) | tr ' ' '\n' | sort -u)
-.PHONY: $(DIRS) $(DIRS_CLEAN) $(DIRS_FLAKE8) flake8 base voltha ofagent netconf shovel onos dashd cli portainer grafana nginx consul envoy go-builder envoyd tools opennms logstash unum start stop tag push pull
+.PHONY: $(DIRS) $(DIRS_CLEAN) $(DIRS_FLAKE8) flake8 base voltha ofagent netconf shovel onos dashd cli portainer grafana nginx consul envoy go-builder envoyd tools opennms logstash unum ponsim start stop tag push pull
# This should to be the first and default target in this Makefile
help:
@@ -170,6 +171,7 @@
@echo "nginx : Build the nginx docker container"
@echo "consul : Build the consul docker container"
@echo "unum : Build the unum docker container"
+ @echo "ponsim : Build the ponsim docker container"
@echo "j2 : Build the Jinja2 template container"
@echo "test_runner : Build a container from which tests are run"
@echo "start : Start VOLTHA on the current system"
@@ -219,7 +221,7 @@
prod-containers: base voltha ofagent netconf shovel onos dashd cli grafana consul tools envoy fluentd unum j2
-containers: base voltha ofagent netconf shovel onos tester config-push dashd cli portainer grafana nginx consul tools envoy fluentd unum j2 test_runner
+containers: base voltha ofagent netconf shovel onos tester config-push dashd cli portainer grafana nginx consul tools envoy fluentd unum ponsim j2 test_runner
base:
docker build $(DOCKER_BUILD_ARGS) -t ${REGISTRY}${REPOSITORY}voltha-base:${TAG} -f docker/Dockerfile.base .
@@ -321,6 +323,9 @@
logstash:
docker build $(DOCKER_BUILD_ARGS) -t ${REGISTRY}${REPOSITORY}voltha-logstash:${TAG} -f docker/Dockerfile.logstash .
+ponsim:
+ docker build $(DOCKER_BUILD_ARGS) -t ${REGISTRY}${REPOSITORY}voltha-ponsim:${TAG} -f docker/Dockerfile.ponsim .
+
j2:
docker build $(DOCKER_BUILD_ARGS) -t ${REGISTRY}${REPOSITORY}voltha-j2:${TAG} -f docker/Dockerfile.j2 docker
diff --git a/docker/Dockerfile.ponsim b/docker/Dockerfile.ponsim
new file mode 100644
index 0000000..b1ae5fe
--- /dev/null
+++ b/docker/Dockerfile.ponsim
@@ -0,0 +1,51 @@
+# -------------
+# Build stage
+
+FROM golang:alpine AS build-env
+
+# Install required packages
+RUN apk add --no-cache wget git libpcap-dev make build-base protobuf protobuf-dev
+
+# Prepare directory structure
+RUN ["mkdir", "-p", "/src/pki", "/src/protos"]
+RUN ["mkdir", "-p", "$GOPATH/src", "$GOPATH/pkg", "$GOPATH/bin"]
+RUN ["mkdir", "-p", "$GOPATH/src/github.com/opencord/voltha/protos/go"]
+
+# Copy files
+ADD ponsim/v2 $GOPATH/src/github.com/opencord/voltha/ponsim/v2
+ADD ponsim/v2 /src
+ADD pki /src/pki
+
+# Copy required proto files
+# ... VOLTHA protos
+ADD voltha/protos/*.proto /src/protos/
+# ... BAL protos
+ADD voltha/adapters/asfvolt16_olt/protos/*.proto /src/protos/
+# ... PONSIM protos
+ADD ponsim/v2/protos/*.proto /src/protos/
+
+# Install golang protobuf and pcap support
+RUN go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
+RUN go get -u github.com/golang/protobuf/protoc-gen-go
+RUN go get -u github.com/google/gopacket/pcap
+
+# Compile protobuf files
+RUN sh /src/scripts/build_protos.sh /src/protos
+
+# Build ponsim
+RUN cd /src && go get -d ./... && go build -o ponsim
+
+# -------------
+# Final stage
+
+FROM alpine
+
+# Install required packages
+RUN apk add --no-cache libpcap-dev
+WORKDIR /app
+
+# Copy required files
+COPY --from=build-env /src/ponsim /app/
+COPY --from=build-env /src/pki /app/pki
+
+ENV VOLTHA_BASE /app
diff --git a/k8s/freeradius.yml b/k8s/freeradius.yml
index 55eb926..baa1615 100644
--- a/k8s/freeradius.yml
+++ b/k8s/freeradius.yml
@@ -2,6 +2,7 @@
kind: Service
metadata:
name: freeradius
+ namespace: voltha
labels:
name: freeradius
spec:
@@ -25,6 +26,7 @@
kind: Deployment
metadata:
name: freeradius
+ namespace: voltha
spec:
replicas: 1
template:
diff --git a/k8s/olt.yml b/k8s/olt.yml
index 9093c3b..88925ec 100644
--- a/k8s/olt.yml
+++ b/k8s/olt.yml
@@ -2,6 +2,7 @@
kind: Service
metadata:
name: olt
+ namespace: voltha
labels:
name: olt
spec:
@@ -16,6 +17,7 @@
kind: Deployment
metadata:
name: olt
+ namespace: voltha
spec:
replicas: 1
template:
diff --git a/k8s/onos.yml b/k8s/onos.yml
index 5843a3a..f1aeb00 100644
--- a/k8s/onos.yml
+++ b/k8s/onos.yml
@@ -2,6 +2,7 @@
kind: Service
metadata:
name: onos
+ namespace: voltha
labels:
name: onos
spec:
@@ -22,6 +23,7 @@
kind: Deployment
metadata:
name: onos
+ namespace: voltha
spec:
replicas: 1
template:
diff --git a/k8s/onu.yml b/k8s/onu.yml
index cfd3763..2d262ff 100644
--- a/k8s/onu.yml
+++ b/k8s/onu.yml
@@ -2,6 +2,7 @@
kind: Service
metadata:
name: onu
+ namespace: voltha
labels:
name: onu
spec:
@@ -16,6 +17,7 @@
kind: Deployment
metadata:
name: onu
+ namespace: voltha
spec:
replicas: 3
template:
diff --git a/k8s/rg.yml b/k8s/rg.yml
index 27dd346..074a46f 100644
--- a/k8s/rg.yml
+++ b/k8s/rg.yml
@@ -2,6 +2,7 @@
kind: Deployment
metadata:
name: rg
+ namespace: voltha
spec:
replicas: 1
template:
diff --git a/ponsim/v2/README.md b/ponsim/v2/README.md
new file mode 100644
index 0000000..fe50243
--- /dev/null
+++ b/ponsim/v2/README.md
@@ -0,0 +1,318 @@
+# 1. Overview
+
+The PON simulator was re-written for the purpose of easily integrating it in a cluster environment.
+
+Here are some differences with the legacy PONSIM implementation:
+* The OLT and ONU instances are deployed as independent entities.
+* Both OLT and ONU are scalable containers.
+* OLT-ONU and VOLTHA-OLT communication is done via GRPC
+
+# 2. Directory structure
+
+```
+./common - Contains utilities used within the project
+./core - Contains the main component for handling the OLT/ONU services
+./grpc - Contains the GRPC server implementation along with the necessary NBI and SBI handlers
+./misc - Contains scripts and required protobuf files
+```
+
+# 3. Requirements
+
+# Golang Installation
+
+If you plan on running the simulator locally, i.e. not in a container, you will need to first
+install setup Golang on your system. Install using existing packages for your operating system
+or issue the following commands (Linux).
+
+```
+cd /tmp
+wget https://storage.googleapis.com/golang/go1.9.3.linux-amd64.tar.gz
+tar -C /usr/local -xzf /tmp/go1.9.3.linux-amd64.tar.gz
+rm -f /tmp/go1.9.3.linux-amd64.tar.gz
+mkdir ~/go
+```
+
+Edit your profile (e.g. .bashrc) and add the following configuration
+
+```
+export GOROOT=/usr/local/go
+export GOPATH=~/go
+export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
+```
+
+# 4. Build the PON simulator
+
+## Container
+
+The PON simulator container can be built by issuing the following command.
+
+```
+make ponsim
+```
+
+## Local
+
+In order to run the PON simulator as a standalone application, you need to do some manual setups
+
+### Protos
+
+The required protobuf files are built as part of the docker build process. If you intend to run
+the simulator in a non-containerized way, you will need to build the protobuf files manually.
+
+```
+mkdir -p ponsim/v2/protos
+cp voltha/protos/*.proto ponsim/v2/protos
+cp voltha/adapters/asfvolt16_olt/protos/*.proto ponsim/v2/protos
+cp ponsim/v2/misc/protos/*.proto ponsim/v2/protos
+
+sh ponsim/v2/misc/scripts/build_protos.sh ponsim/v2/protos
+
+```
+
+### PON simulator executable (optional)
+
+You can optionally build the PON simulator and make it available through your GOPATH.
+
+```
+go get -u github.com/opencord/voltha/ponsim/v2
+
+go build -o $GOPATH/bin/ponsim $GOPATH/src/github.com/opencord/voltha/ponsim/v2/ponsim.go
+```
+
+
+# 5. PON Simulator Usage
+
+```
+Usage of ./ponsim:
+ -alarm_freq int
+ Frequency of simulated alarms (in seconds) (default 60)
+ -alarm_sim
+ Enable generation of simulated alarms
+ -api_type string
+ Type of API used to communicate with devices (PONSIM or BAL) (default "PONSIM")
+ -device_type string
+ Type of device to simulate (OLT or ONU) (default "OLT")
+ -external_if string
+ External Communication Interface for read/write network traffic (default "eth2")
+ -grpc_addr string
+ Address used to establish GRPC server connection
+ -grpc_port int
+ Port used to establish GRPC server connection (default 50060)
+ -internal_if string
+ Internal Communication Interface for read/write network traffic (default "eth1")
+ -name string
+ Name of the PON device (default "PON")
+ -no_banner
+ Omit startup banner log lines
+ -onus int
+ Number of ONUs to simulate (default 1)
+ -parent_addr string
+ Address of OLT to connect to (default "olt")
+ -parent_port int
+ Port of OLT to connect to (default 50060)
+ -promiscuous
+ Enable promiscuous mode on network interfaces
+ -quiet
+ Suppress debug and info logs
+ -vcore_endpoint string
+ Voltha core endpoint address (default "vcore")
+ -verbose
+ Enable verbose logging
+```
+
+# 6. Run in local mode (no container)
+
+## Create the necessary docker networks
+
+```
+docker network create -o "com.docker.network.bridge.name"="ponsim_wan" \
+ --subnet=172.31.31.0/24 ponsim_wan
+
+docker network create -o "com.docker.network.bridge.name"="ponsim_internal" \
+ --subnet=172.32.32.0/24 ponsim_internal
+```
+
+Allow multicast traffic to flow through the ponsim_wan network
+
+```
+echo 8 > /sys/class/net/ponsim_wan/bridge/group_fwd_mask
+```
+
+## Start VOLTHA
+
+```
+docker-compose -f compose/docker-compose-system-test.yml up -d
+docker-compose -f compose/docker-compose-auth-test.yml -p auth up -d
+```
+
+## OLT
+
+```
+ponsim -device_type OLT \
+ -internal_if <network to voltha> \
+ -external_if <internal network> \
+ -vcore_endpoint <ip of vcore instance> \
+ -onus 10
+```
+
+Example:
+
+```
+# Run as root
+sudo su
+
+ponsim -device_type OLT \
+ -internal_if ponmgmt \
+ -external_if ponsim_internal \
+ -vcore_endpoint 172.30.30.3 \
+ -onus 10
+```
+
+
+## ONU
+
+```
+ponsim -device_type ONU \
+ -external_if <network to world> \
+ -internal_if <internal network> \
+ -grpc_port 50061 \
+ -parent_addr localhost
+```
+
+Example:
+
+```
+# Run as root
+sudo su
+
+ponsim -device_type ONU \
+ -external_if ponsim_wan \
+ -internal_if ponsim_internal \
+ -grpc_port 50061 \
+ -parent_addr localhost
+```
+
+## Create PONSIM adapter
+
+Log into the VOLTHA CLI and provision an OLT instance.
+
+```
+ssh -p 5022 voltha@localhost
+
+preprovision_olt -t ponsim_olt -H 172.17.0.1:50060
+enable
+```
+
+## RG
+
+Run the RG tester
+
+```
+docker run --net=ponsim_wan --rm --name RG -it cord/tester bash
+```
+
+Execute the EAPOL authentication
+
+```
+/sbin/wpa_supplicant -Dwired -ieth0 -c /etc/wpa_supplicant/wpa_supplicant.conf
+```
+
+
+# 7. Run in a Kubernetes cluster
+
+Note: The following instructions are just a reference and may be incomplete.
+
+
+## Install networking components
+
+Install the Weave network package
+
+```
+kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
+```
+
+Install the CNI Genie package (Required to support multiple network interfaces in a container)
+
+```
+kubectl apply -f https://raw.githubusercontent.com/Huawei-PaaS/CNI-Genie/master/conf/1.8/genie.yaml
+```
+
+Configure PON management network template (on each host).
+
+```
+# Run as root
+sudo su
+
+cat <<EOF >> /etc/cni/net.d/10-pon0.conf
+{
+ "name": "pon0",
+ "type": "bridge",
+ "bridge": "pon0",
+ "isGateway": true,
+ "ipMask": true,
+ "ipam": {
+ "type": "host-local",
+ "subnet": "10.22.0.0/16",
+ "routes": [
+ { "dst": "0.0.0.0/0" }
+ ]
+ }
+}
+EOF
+```
+
+## Start Voltha Components
+
+```
+cd k8s
+
+kubectl create configmap freeradius-config --from-file data/clients.conf --from-file data/users
+
+kubectl apply -f consul.yml
+kubectl apply -f zookeeper.yml
+kubectl apply -f kafka.yml
+kubectl apply -f envoy_for_consul.yml
+kubectl apply -f vcore_for_consul.yml
+kubectl apply -f ofagent.yml
+kubectl apply -f vcli.yml
+kubectl apply -f onos.yml
+kubectl apply -f freeradius.yml
+```
+
+## Start PONSIM
+
+From the main directory, execute the following command:
+
+```
+cd k8s
+
+kubectl apply -f olt.yml
+
+# The ONU configuration will setup a bridge on the host to ensure communication with the RG
+kubectl apply -f onu.yml
+
+# Setup bridge to allow multicast traffic (must be done on each host running an ONU)
+echo 8 > /sys/class/net/pon0/bridge/group_fwd_mask
+```
+
+## Create PONSIM adapter
+
+```
+ssh -p 5022 voltha@<ip of cli>
+
+preprovision_olt -t ponsim_olt -H olt:50060
+enable
+```
+
+## Start RG
+
+```
+kubectl apply -f rg.yml
+
+# Enter the RG container
+kubectl exec <rg container id> -ti bash
+
+# Execute some test (e.g. EAPOL authentication)
+wpa_supplicant -i eth0 -Dwired -c /etc/wpa_supplicant/wpa_supplicant.conf
+
+```
diff --git a/ponsim/v2/common/flow_sort.go b/ponsim/v2/common/flow_sort.go
new file mode 100644
index 0000000..f62f7ca
--- /dev/null
+++ b/ponsim/v2/common/flow_sort.go
@@ -0,0 +1,17 @@
+package common
+
+import (
+ "github.com/opencord/voltha/protos/go/openflow_13"
+)
+
+type SortByPriority []*openflow_13.OfpFlowStats
+
+func (s SortByPriority) Len() int {
+ return len(s)
+}
+func (s SortByPriority) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+func (s SortByPriority) Less(i, j int) bool {
+ return s[i].Priority < s[j].Priority
+}
diff --git a/ponsim/v2/common/interval_handler.go b/ponsim/v2/common/interval_handler.go
new file mode 100644
index 0000000..973865e
--- /dev/null
+++ b/ponsim/v2/common/interval_handler.go
@@ -0,0 +1,164 @@
+package common
+
+import (
+ "github.com/sirupsen/logrus"
+ "sync"
+ "time"
+)
+
+/*
+ IntervalHandler is used to run a routine at regular intervals and provide all the necessary
+ utilities to manage the execution.
+*/
+
+type IntervalHandler struct {
+ // Interval period in between each execution (in seconds?)
+ Interval int
+ // function to execute after each interval
+ function func()
+ // Channel listening to execution events
+ execute chan _ExecutionState
+ // Channel listening for a termination event
+ terminate chan struct{}
+ // Current execution state of the handler
+ state _ExecutionState
+ wg sync.WaitGroup
+}
+
+// Define execution state constants
+type _ExecutionState uint8
+
+const (
+ STARTED _ExecutionState = iota
+ STOPPED
+ PAUSED
+ RESUMED
+)
+
+// Execute state string equivalents
+var _ExecutionStateEnum = []string{
+ "STARTED",
+ "STOPPED",
+ "PAUSED",
+ "RESUMED",
+}
+
+func (s _ExecutionState) String() string {
+ return _ExecutionStateEnum[s]
+}
+
+/*
+NewIntervalHandler instantiates a new interval based function execution handler
+*/
+func NewIntervalHandler(interval int, function func()) *IntervalHandler {
+ handler := &IntervalHandler{
+ Interval: interval,
+ function: function,
+ }
+
+ handler.execute = make(chan _ExecutionState)
+ handler.terminate = make(chan struct{})
+ handler.state = STOPPED
+
+ return handler
+}
+
+/*
+_Execute is a routine running concurrently and listening to execution events
+*/
+func (h *IntervalHandler) _Execute() {
+ defer h.wg.Done()
+ for {
+ select {
+ case h.state = <-h.execute:
+ Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Debug("Processing execution state")
+ switch h.state {
+ case STARTED:
+ case PAUSED:
+ case RESUMED:
+ h.state = STARTED
+ case STOPPED:
+ fallthrough
+ default:
+ h.terminate <- struct{}{}
+ }
+
+ case <-h.terminate:
+ return
+
+ default:
+ if h.state == STARTED {
+ h.function()
+ time.Sleep(time.Duration(h.Interval) * time.Second)
+ } else {
+ // TODO: replace hardcoded delay with a configurable parameter
+ time.Sleep(1 * time.Second)
+ }
+ }
+ }
+}
+
+/*
+Start initiates the interval based function execution
+*/
+func (h *IntervalHandler) Start() {
+ Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Info("Starting interval handler")
+
+ if h.execute == nil {
+ return
+ }
+ if h.state == STOPPED {
+ go h._Execute()
+ h.execute <- STARTED
+ }
+}
+
+/*
+Pause interrupts the interval based function execution
+*/
+func (h *IntervalHandler) Pause() {
+ Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Info("Pausing interval handler")
+
+ if h.execute == nil || h.state == STOPPED {
+ return
+ }
+ if h.state == STARTED {
+ h.execute <- PAUSED
+ }
+}
+
+/*
+Resume continues the interval based function execution
+*/
+func (h *IntervalHandler) Resume() {
+ Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Info("Resuming interval handler")
+
+ if h.execute == nil || h.state == STOPPED {
+ return
+ }
+ if h.state == PAUSED {
+ h.execute <- RESUMED
+ }
+}
+
+/*
+Stop terminates the interval based function execution
+*/
+func (h *IntervalHandler) Stop() {
+ Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Info("Stopping interval handler")
+
+ if h.execute == nil || h.state == STOPPED {
+ return
+ }
+ h.execute <- STOPPED
+}
diff --git a/ponsim/v2/common/interval_handler_test.go b/ponsim/v2/common/interval_handler_test.go
new file mode 100644
index 0000000..e1f060f
--- /dev/null
+++ b/ponsim/v2/common/interval_handler_test.go
@@ -0,0 +1,72 @@
+package common
+
+import (
+ "fmt"
+ "testing"
+ "time"
+)
+
+var (
+ handler *IntervalHandler
+ iteration int = 0
+ interval int = 2
+)
+
+func RepeatMessage() {
+ fmt.Printf("Ran the function %d times\n", iteration)
+ iteration += 1
+}
+
+func TestNewIntervalHandler(t *testing.T) {
+ handler = NewIntervalHandler(interval, RepeatMessage)
+
+ if handler.state != STOPPED {
+ t.Error("The handler should be in STOPPED state", handler.state)
+ }
+ if handler.Interval != interval {
+ t.Error("The handler interval doesn't match the configured value", handler.Interval)
+ }
+ if handler.function == nil {
+ t.Error("The handler does not have function configured function", handler.function)
+ }
+}
+
+func TestIntervalHandler_Start(t *testing.T) {
+ handler.Start()
+
+ time.Sleep(5 * time.Second)
+
+ if handler.state != STARTED {
+ t.Error("The handler should be in STARTED state", handler.state)
+ }
+}
+
+func TestIntervalHandler_Pause(t *testing.T) {
+ handler.Pause()
+
+ if handler.state != PAUSED {
+ t.Error("The handler should be in PAUSED state", handler.state)
+ }
+
+ time.Sleep(5 * time.Second)
+}
+
+func TestIntervalHandler_Resume(t *testing.T) {
+ handler.Resume()
+
+ time.Sleep(5 * time.Second)
+
+ if handler.state != STARTED {
+ t.Error("The handler should be in STARTED state", handler.state)
+ }
+}
+
+func TestIntervalHandler_Stop(t *testing.T) {
+ handler.Stop()
+
+ if handler.state != STOPPED {
+ t.Error("The handler should be in STOPPED state", handler.state)
+ }
+
+ time.Sleep(5 * time.Second)
+}
diff --git a/ponsim/v2/common/logger.go b/ponsim/v2/common/logger.go
new file mode 100644
index 0000000..7749dd2
--- /dev/null
+++ b/ponsim/v2/common/logger.go
@@ -0,0 +1,81 @@
+package common
+
+import (
+ "github.com/evalphobia/logrus_fluent"
+ "github.com/sirupsen/logrus"
+ "net"
+ "strconv"
+ "sync"
+)
+
+type logManager struct {
+ *logrus.Logger
+}
+
+// Singleton instance
+var mgrInstance *logManager
+var once sync.Once
+
+func (mgr *logManager) SetFluentd(fluentdHost string) {
+ var hook *logrus_fluent.FluentHook
+ var err error
+ var host string
+ var portStr string
+ var port int
+
+ if host, portStr, err = net.SplitHostPort(fluentdHost); err != nil {
+ mgr.WithFields(logrus.Fields{
+ "error": err.Error(),
+ }).Error("Failed to retrieve host/port information")
+ return
+ }
+
+ if port, err = strconv.Atoi(portStr); err != nil {
+ mgr.WithFields(logrus.Fields{
+ "error": err.Error(),
+ }).Error("Failed to convert port to integer")
+ return
+ }
+
+ if hook, err = logrus_fluent.NewWithConfig(
+ logrus_fluent.Config{
+ Host: host,
+ Port: port,
+ }); err != nil {
+ mgr.WithFields(logrus.Fields{
+ "error": err.Error(),
+ }).Error("Failed to enable Fluentd hook")
+ return
+ }
+
+ hook.SetTag("ponsim")
+
+ hook.SetLevels([]logrus.Level{
+ logrus.DebugLevel,
+ })
+
+ mgr.AddHook(hook)
+
+ mgr.WithFields(logrus.Fields{
+ "hook": hook,
+ }).Info("Added fluentd hook")
+}
+
+/**
+ * Get instance
+ *
+ * It should get initialized only once
+ */
+func Logger() *logManager {
+ once.Do(func() {
+
+ _logger := logrus.New()
+ _logger.Formatter = new(logrus.JSONFormatter)
+ _logger.Level = logrus.DebugLevel
+ //_logger.Out =
+
+ mgrInstance = &logManager{_logger}
+ })
+
+ return mgrInstance
+}
diff --git a/ponsim/v2/common/net_utils.go b/ponsim/v2/common/net_utils.go
new file mode 100644
index 0000000..c37c4ef
--- /dev/null
+++ b/ponsim/v2/common/net_utils.go
@@ -0,0 +1,107 @@
+package common
+
+import (
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/sirupsen/logrus"
+ "net"
+)
+
+func GetInterfaceIP(ifName string) string {
+ var err error
+ var netIf *net.Interface
+ var netAddrs []net.Addr
+ var netIp net.IP
+ var ipAddr string
+
+ if netIf, err = net.InterfaceByName(ifName); err == nil {
+ if netAddrs, err = netIf.Addrs(); err == nil {
+ for _, addr := range netAddrs {
+ Logger().WithFields(logrus.Fields{
+ "type": addr.Network(),
+ }).Debug("Address network type")
+ switch v := addr.(type) {
+ case *net.IPNet:
+ netIp = v.IP
+ case *net.IPAddr:
+ netIp = v.IP
+ }
+ if netIp == nil || netIp.IsLoopback() {
+ continue
+ }
+ netIp = netIp.To4()
+ if netIp == nil {
+ continue // not an ipv4 address
+ }
+ ipAddr = netIp.String()
+ break
+ }
+ }
+ }
+
+ return ipAddr
+}
+func GetHostIP(hostName string) string {
+ var err error
+ var ipAddrs []string
+ var ipAddr string
+
+ if ipAddrs, err = net.LookupHost(hostName); err == nil {
+ for _, ip := range ipAddrs {
+ if addr := net.ParseIP(ip); err == nil {
+ Logger().WithFields(logrus.Fields{
+ "ip": addr,
+ }).Debug("Host address")
+ if addr == nil /*|| addr.IsLoopback()*/ {
+ continue
+ }
+ ipAddr = ip
+ break
+ }
+ }
+ }
+
+ return ipAddr
+}
+func GetMacAddress(ifName string) net.HardwareAddr {
+ var err error
+ var netIf *net.Interface
+ var hwAddr net.HardwareAddr
+
+ if netIf, err = net.InterfaceByName(ifName); err == nil {
+ hwAddr = netIf.HardwareAddr
+ }
+
+ return hwAddr
+}
+
+func GetEthernetLayer(frame gopacket.Packet) *layers.Ethernet {
+ eth := &layers.Ethernet{}
+ if ethLayer := frame.Layer(layers.LayerTypeEthernet); ethLayer != nil {
+ eth, _ = ethLayer.(*layers.Ethernet)
+ }
+ return eth
+}
+func GetDot1QLayer(frame gopacket.Packet) *layers.Dot1Q {
+ var dot1q *layers.Dot1Q
+ //dot1q := &layers.Dot1Q{}
+ if dot1qLayer := frame.Layer(layers.LayerTypeDot1Q); dot1qLayer != nil {
+ dot1q, _ = dot1qLayer.(*layers.Dot1Q)
+ }
+ return dot1q
+}
+
+func GetIpLayer(frame gopacket.Packet) *layers.IPv4 {
+ ip := &layers.IPv4{}
+ if ipLayer := frame.Layer(layers.LayerTypeIPv4); ipLayer != nil {
+ ip, _ = ipLayer.(*layers.IPv4)
+ }
+ return ip
+}
+func GetUdpLayer(frame gopacket.Packet) *layers.UDP {
+ udp := &layers.UDP{}
+ if udpLayer := frame.Layer(layers.LayerTypeUDP); udpLayer != nil {
+ udp, _ = udpLayer.(*layers.UDP)
+ }
+ return udp
+}
diff --git a/ponsim/v2/core/ponsim_alarm.go b/ponsim/v2/core/ponsim_alarm.go
new file mode 100644
index 0000000..e99b84d
--- /dev/null
+++ b/ponsim/v2/core/ponsim_alarm.go
@@ -0,0 +1,175 @@
+package core
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/voltha"
+ "github.com/sirupsen/logrus"
+ "math/rand"
+ "net"
+ "time"
+)
+
+// TODO: user-defined values? min/max intervals, vlan?
+
+const (
+ minInterval = 20
+ maxInterval = 60
+ vlandId = 4000
+ localhost = "127.0.0.1"
+ ttl = 64
+ ipVersion = 4
+)
+
+type Alarm struct {
+ Severity int `json:"severity"`
+ Type int `json:"type"`
+ Category int `json:"category"`
+ State int `json:"state"`
+ TimeStamp int `json:"ts"`
+ Description string `json:"description"`
+}
+
+/*
+PonSimAlarm is the structure responsible for the handling of alarms
+*/
+type PonSimAlarm struct {
+ forwardFunction func(int, gopacket.Packet)
+ dstInterface string
+ dstEndpoint string
+}
+
+/*
+NewPonSimAlarm instantiates a new alarm handling structure
+*/
+func NewPonSimAlarm(dstInterface string, dstEndpoint string, function func(int, gopacket.Packet)) *PonSimAlarm {
+ psa := &PonSimAlarm{dstInterface: dstInterface, dstEndpoint: dstEndpoint, forwardFunction: function}
+ return psa
+}
+
+/*
+prepareAlarm constructs an alarm object with random field values.
+*/
+func (a *PonSimAlarm) prepareAlarm() *Alarm {
+ alarm_severity := rand.Intn(len(voltha.AlarmEventSeverity_AlarmEventSeverity_value))
+ alarm_type := rand.Intn(len(voltha.AlarmEventType_AlarmEventType_value))
+ alarm_category := rand.Intn(len(voltha.AlarmEventCategory_AlarmEventCategory_value))
+ alarm_state := int(voltha.AlarmEventState_RAISED)
+ alarm_ts := time.Now().UTC().Second()
+ alarm_description := fmt.Sprintf("%s.%s alarm",
+ voltha.AlarmEventType_AlarmEventType_name[int32(alarm_type)],
+ voltha.AlarmEventCategory_AlarmEventCategory_name[int32(alarm_category)],
+ )
+
+ alarm := &Alarm{
+ Severity: alarm_severity,
+ Type: alarm_type,
+ Category: alarm_category,
+ State: alarm_state,
+ TimeStamp: alarm_ts,
+ Description: alarm_description,
+ }
+
+ return alarm
+}
+
+/*
+sendAlarm constructs and forwards the alarm to the network
+*/
+func (a *PonSimAlarm) sendAlarm(alarm *Alarm) {
+ // Ethernet layer is configured as a broadcast packet
+ ethLayer := &layers.Ethernet{
+ SrcMAC: common.GetMacAddress(a.dstInterface),
+ DstMAC: layers.EthernetBroadcast,
+ EthernetType: layers.EthernetTypeDot1Q,
+ }
+
+ // Need to encapsulate in VLAN so that voltha captures the packet
+ dot1qLayer := &layers.Dot1Q{
+ Type: layers.EthernetTypeIPv4,
+ VLANIdentifier: vlandId,
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "Alarm": a,
+ "srcIp": common.GetInterfaceIP(a.dstInterface),
+ "dstIp": common.GetHostIP(a.dstEndpoint),
+ }).Info("SRC/DST IP addresses")
+
+ // IP layer needs the following attributes at a minimum in order to have
+ // a properly formed packet
+ ipLayer := &layers.IPv4{
+ SrcIP: net.ParseIP(common.GetInterfaceIP(a.dstInterface)),
+ DstIP: net.ParseIP(common.GetHostIP(a.dstEndpoint)),
+ //SrcIP: net.ParseIP(localhost),
+ //DstIP: net.ParseIP(localhost),
+ Version: ipVersion,
+ TTL: ttl,
+ Protocol: layers.IPProtocolTCP,
+ }
+
+ // TCP layer does not require anything special
+ // except than providing the IP layer so that the checksum can be
+ // properly calculated
+ tcpLayer := &layers.TCP{}
+ tcpLayer.SetNetworkLayerForChecksum(ipLayer)
+
+ // Convert the alarm to bytes to include it as the packet payload
+ rawData, _ := json.Marshal(alarm)
+
+ // Construct the packet
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{
+ FixLengths: true,
+ ComputeChecksums: true,
+ }
+ gopacket.SerializeLayers(buffer, options,
+ ethLayer,
+ dot1qLayer,
+ ipLayer,
+ tcpLayer,
+ gopacket.Payload(rawData),
+ )
+ frame := gopacket.NewPacket(
+ buffer.Bytes(),
+ layers.LayerTypeEthernet,
+ gopacket.Default,
+ )
+
+ // Forward the packetized alarm to the network
+ a.forwardFunction(0, frame)
+
+ common.Logger().WithFields(logrus.Fields{
+ "Alarm": alarm,
+ "Frame": frame.Dump(),
+ }).Debug("Sent alarm")
+}
+
+/*
+raiseAlarm submits an alarm object with a RAISED state
+*/
+func (a *PonSimAlarm) raiseAlarm(alarm *Alarm) {
+ alarm.State = int(voltha.AlarmEventState_RAISED)
+ a.sendAlarm(alarm)
+}
+
+/*
+clearAlarm submits an alarm object with a CLEARED state
+*/
+func (a *PonSimAlarm) clearAlarm(alarm *Alarm) {
+ alarm.State = int(voltha.AlarmEventState_CLEARED)
+ a.sendAlarm(alarm)
+}
+
+/*
+GenerateAlarm simulates RAISE and CLEAR alarm events with a random delay in between each state.
+*/
+func (a *PonSimAlarm) GenerateAlarm() {
+ alarm := a.prepareAlarm()
+ a.raiseAlarm(alarm)
+ time.Sleep(time.Duration(rand.Intn(maxInterval-minInterval)+minInterval) * time.Second)
+ a.clearAlarm(alarm)
+}
diff --git a/ponsim/v2/core/ponsim_api_type.go b/ponsim/v2/core/ponsim_api_type.go
new file mode 100644
index 0000000..f178c9b
--- /dev/null
+++ b/ponsim/v2/core/ponsim_api_type.go
@@ -0,0 +1,17 @@
+package core
+
+type PonSimApiType uint8
+
+const (
+ PONSIM PonSimApiType = iota
+ BAL
+)
+
+var enum_ponsim_api_types = []string{
+ "PONSIM",
+ "BAL",
+}
+
+func (t PonSimApiType) String() string {
+ return enum_ponsim_api_types[t]
+}
diff --git a/ponsim/v2/core/ponsim_device.go b/ponsim/v2/core/ponsim_device.go
new file mode 100644
index 0000000..c13d003
--- /dev/null
+++ b/ponsim/v2/core/ponsim_device.go
@@ -0,0 +1,796 @@
+package core
+
+import (
+ "context"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/google/gopacket/pcap"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/openflow_13"
+ "github.com/sirupsen/logrus"
+ "net"
+ "sort"
+)
+
+// TODO: Pass-in the certificate information as a structure parameter
+// TODO: Add certification information
+
+type PonSimDevice struct {
+ Name string `json:name`
+ Port int32 `json:port`
+ Address string `json:address`
+ ExternalIf string `json:external_if`
+ InternalIf string `json:internal_if`
+ Promiscuous bool `json:promiscuous`
+ SnapshotLen int32 `json:snapshot_len`
+ AlarmsOn bool `json:alarm_on`
+ AlarmsFreq int `json:alarm_freq`
+ Counter *PonSimMetricCounter `json:counter`
+
+ //*grpc.GrpcSecurity
+
+ flows []*openflow_13.OfpFlowStats `json:-`
+ ingressHandler *pcap.Handle `json:-`
+ egressHandler *pcap.Handle `json:-`
+ links map[int]map[int]interface{} `json:-`
+}
+
+const (
+ UDP_DST = 1
+ UDP_SRC = 2
+ IPV4_DST = 4
+ VLAN_PCP = 8
+ VLAN_VID = 16
+ IP_PROTO = 32
+ ETH_TYPE = 64
+ IN_PORT = 128
+)
+
+/*
+Start performs common setup operations for a ponsim device
+*/
+func (o *PonSimDevice) Start(ctx context.Context) {
+}
+
+/*
+Stop performs common cleanup operations for a ponsim device
+*/
+func (o *PonSimDevice) Stop(ctx context.Context) {
+}
+
+/*
+GetAddress returns the IP/FQDN for the device
+*/
+func (o *PonSimDevice) GetAddress() string {
+ return o.Address
+}
+
+/*
+GetPort return the port assigned to the device
+*/
+func (o *PonSimDevice) GetPort() int32 {
+ return o.Port
+}
+
+/*
+Forward is responsible of processing incoming data, filtering it and redirecting to the
+intended destination
+*/
+func (o *PonSimDevice) Forward(
+ ctx context.Context,
+ port int,
+ frame gopacket.Packet,
+) error {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Debug("Forwarding packet")
+
+ var err error
+
+ o.Counter.CountRxFrame(port, len(common.GetEthernetLayer(frame).Payload))
+
+ if egressPort, egressFrame := o.processFrame(ctx, port, frame); egressFrame != nil {
+ forwarded := 0
+ links := o.links[int(egressPort)]
+
+ o.Counter.CountTxFrame(int(egressPort), len(common.GetEthernetLayer(egressFrame).Payload))
+
+ for _, link := range links {
+ forwarded += 1
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "egressPort": port,
+ "egressFrame": egressFrame,
+ }).Debug("Forwarding packet to link")
+
+ link.(func(int, gopacket.Packet))(int(egressPort), egressFrame)
+ }
+ if forwarded == 0 {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Warn("Nothing was forwarded")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": egressPort,
+ "frame": egressFrame,
+ }).Error("Failed to properly process frame")
+ }
+
+ return err
+}
+
+/*
+connectNetworkInterfaces opens network interfaces for reading and/or writing packets
+*/
+func (o *PonSimDevice) connectNetworkInterfaces() {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Opening network interfaces")
+
+ var err error
+ if o.ingressHandler, err = pcap.OpenLive(
+ o.ExternalIf, o.SnapshotLen, o.Promiscuous, pcap.BlockForever,
+ ); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.ExternalIf,
+ "error": err.Error(),
+ }).Fatal("Unable to open Ingress interface")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.ExternalIf,
+ }).Info("Opened Ingress interface")
+ }
+
+ if o.egressHandler, err = pcap.OpenLive(
+ o.InternalIf, o.SnapshotLen, o.Promiscuous, pcap.BlockForever,
+ ); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.InternalIf,
+ "error": err.Error(),
+ }).Fatal("Unable to open egress interface")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.InternalIf,
+ }).Info("Opened egress interface")
+ }
+}
+
+/*
+AddLink assigns a functional operation to a device endpoint
+
+The functional operation is called whenever a packet has been processed
+and the endpoint has been identified as the outgoing interface
+*/
+func (o *PonSimDevice) AddLink(
+ port int,
+ index int,
+ function interface{},
+) error {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "index": index,
+ }).Debug("Linking port to functional operation")
+
+ if o.links == nil {
+ o.links = make(map[int]map[int]interface{})
+ }
+ if _, ok := o.links[port]; !ok {
+ o.links[port] = make(map[int]interface{})
+ }
+ o.links[port][index] = function
+
+ return nil
+}
+
+/*
+RemoveLink will remove reference a functional operation for a given port and index
+*/
+func (o *PonSimDevice) RemoveLink(
+ port int,
+ index int,
+) error {
+ if _, hasPort := o.links[port]; hasPort {
+ if _, hasIndex := o.links[port][index]; hasIndex {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "index": index,
+ }).Debug("Removing link functional operation")
+
+ delete(o.links[port], index)
+
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "index": index,
+ }).Warn("No such index for link functional operation")
+
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "index": index,
+ }).Warn("No such port for functional operation")
+ }
+
+ return nil
+}
+
+/*
+InstallFlows assigns flows to the device in order of priority
+*/
+func (o *PonSimDevice) InstallFlows(
+ ctx context.Context,
+ flows []*openflow_13.OfpFlowStats,
+) error {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flows": flows,
+ }).Debug("Installing flows")
+
+ o.flows = flows
+ sort.Sort(common.SortByPriority(o.flows))
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Installed sorted flows")
+
+ return nil
+}
+
+/*
+processFrame is responsible for matching or discarding a frame based on the configured flows
+*/
+func (o *PonSimDevice) processFrame(
+ ctx context.Context,
+ port int,
+ frame gopacket.Packet,
+) (uint32, gopacket.Packet) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Debug("Processing frame")
+
+ var err error
+ var matchedMask int = 0
+ var currentMask int
+ var highestPriority uint32 = 0
+ var matchedFlow *openflow_13.OfpFlowStats = nil
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Looping through flows")
+
+ for _, flow := range o.flows {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ }).Debug("Checking flow")
+
+ if matchedFlow != nil && flow.Priority < highestPriority {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "matchedFlow": matchedFlow,
+ "priority": highestPriority,
+ }).Debug("Flow has already been matched")
+ break
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "matchedFlow": matchedFlow,
+ "priority": flow.Priority,
+ "highestPriority": highestPriority,
+ }).Debug("Flow OR Priority requirements not met")
+ }
+
+ highestPriority = flow.Priority
+ if currentMask, err = o.isMatch(ctx, flow, port, frame); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "port": port,
+ "frame": frame,
+ "error": err.Error(),
+ }).Error("Problem while matching flow")
+
+ } else if currentMask > matchedMask {
+ matchedMask = currentMask
+ matchedFlow = flow
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "matchedFlow": flow,
+ "port": port,
+ "frame": frame,
+ "matchedMask": matchedMask,
+ }).Debug("Flow matches")
+ }
+ }
+
+ if matchedFlow != nil {
+ egressPort, egressFrame := o.processActions(ctx, matchedFlow, frame)
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "egressPort": egressPort,
+ "egressFrame": egressFrame,
+ }).Debug("Processed actions to matched flow")
+
+ return egressPort, egressFrame
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ "matchedMask": matchedMask,
+ }).Warn("Flow was not successfully matched")
+ }
+
+ return 0, nil
+}
+
+/*
+isMatch traverses the criteria of a flow and identify all matching elements of a frame (if any)
+*/
+func (o *PonSimDevice) isMatch(
+ ctx context.Context,
+ flow *openflow_13.OfpFlowStats,
+ port int,
+ frame gopacket.Packet,
+) (int, error) {
+ matchedMask := 0
+
+ for _, ofbfield := range flow.Match.OxmFields {
+ if ofbfield.GetOxmClass() == openflow_13.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+ switch ofbfield.GetOfbField().Type {
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IN_PORT:
+ if ofbfield.GetOfbField().GetPort() != uint32(port) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetPort(),
+ "actual": port,
+ }).Warn("Port does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetPort(),
+ "actual": port,
+ }).Debug("Port matches")
+ }
+ matchedMask |= IN_PORT
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_ETH_TYPE:
+ cmpType := uint32(common.GetEthernetLayer(frame).EthernetType)
+ if dot1q := common.GetDot1QLayer(frame); dot1q != nil {
+ cmpType = uint32(dot1q.Type)
+ }
+ if ofbfield.GetOfbField().GetEthType() != cmpType {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": layers.EthernetType(ofbfield.GetOfbField().GetEthType()),
+ "actual": common.GetEthernetLayer(frame).EthernetType,
+ }).Warn("Frame type does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": layers.EthernetType(ofbfield.GetOfbField().GetEthType()),
+ "actual": common.GetEthernetLayer(frame).EthernetType,
+ }).Debug("Frame type matches")
+ }
+ matchedMask |= ETH_TYPE
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IP_PROTO:
+ if ofbfield.GetOfbField().GetIpProto() != uint32(common.GetIpLayer(frame).Protocol) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetIpProto(),
+ "actual": common.GetIpLayer(frame).Protocol,
+ }).Warn("IP protocol does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetIpProto(),
+ "actual": common.GetIpLayer(frame).Protocol,
+ }).Debug("IP protocol matches")
+ }
+ matchedMask |= IP_PROTO
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID:
+ expectedVlan := ofbfield.GetOfbField().GetVlanVid()
+ dot1q := common.GetDot1QLayer(frame)
+
+ if (expectedVlan&4096 == 0) != (dot1q == nil) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expectedVlan": expectedVlan,
+ "vlanBitwise": expectedVlan & 4096,
+ "dot1q": dot1q,
+ }).Warn("VLAN condition not met")
+ return 0, nil
+ }
+ if dot1q != nil {
+ if uint32(dot1q.VLANIdentifier) != (expectedVlan & 4095) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": expectedVlan,
+ "actual": uint32(dot1q.VLANIdentifier),
+ }).Warn("VLAN VID does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": expectedVlan,
+ "actual": uint32(dot1q.VLANIdentifier),
+ }).Debug("VLAN VID matches")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ }).Warn("VLAN VID missing. Not dot1q encapsulation")
+ }
+ matchedMask |= VLAN_VID
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP:
+ if ofbfield.GetOfbField().GetVlanPcp() != uint32(common.GetDot1QLayer(frame).Priority) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetVlanPcp(),
+ "actual": uint32(common.GetDot1QLayer(frame).Priority),
+ }).Warn("VLAN priority does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetVlanPcp(),
+ "actual": uint32(common.GetDot1QLayer(frame).Priority),
+ }).Debug("VLAN priority matches")
+ }
+ matchedMask |= VLAN_PCP
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_IPV4_DST:
+ dstIpRaw := ofbfield.GetOfbField().GetIpv4Dst()
+ dstIp := net.IPv4(
+ byte((dstIpRaw>>24)&0xFF),
+ byte((dstIpRaw>>16)&0xFF),
+ byte((dstIpRaw>>8)&0xFF),
+ byte(dstIpRaw&0xFF))
+
+ if !dstIp.Equal(common.GetIpLayer(frame).DstIP) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": dstIp,
+ "actual": common.GetIpLayer(frame).DstIP,
+ }).Warn("IPv4 destination does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": dstIp,
+ "actual": common.GetIpLayer(frame).DstIP,
+ }).Debug("IPv4 destination matches")
+
+ }
+ matchedMask |= IPV4_DST
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_UDP_SRC:
+ if ofbfield.GetOfbField().GetUdpSrc() != uint32(common.GetUdpLayer(frame).SrcPort) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetUdpSrc(),
+ "actual": common.GetUdpLayer(frame).SrcPort,
+ }).Warn("UDP source port does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetUdpSrc(),
+ "actual": common.GetUdpLayer(frame).SrcPort,
+ }).Debug("UDP source port matches")
+ }
+ matchedMask |= UDP_SRC
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_UDP_DST:
+ if ofbfield.GetOfbField().GetUdpDst() != uint32(common.GetUdpLayer(frame).DstPort) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetUdpDst(),
+ "actual": common.GetUdpLayer(frame).DstPort,
+ }).Warn("UDP destination port does not match")
+ return 0, nil
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "expected": ofbfield.GetOfbField().GetUdpDst(),
+ "actual": common.GetUdpLayer(frame).DstPort,
+ }).Debug("UDP destination port does matches")
+ }
+ matchedMask |= UDP_DST
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_METADATA:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ }).Warn("Skipping metadata")
+ continue
+
+ default:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "type": ofbfield.GetOfbField().Type,
+ }).Warn("Field type not implemented")
+ }
+ }
+ }
+ return matchedMask, nil
+}
+
+/*
+processActions applies transformation instructions to a frame that met all the flow criteria
+*/
+func (o *PonSimDevice) processActions(
+ ctx context.Context,
+ flow *openflow_13.OfpFlowStats,
+ frame gopacket.Packet,
+) (uint32, gopacket.Packet) {
+ var egressPort uint32
+ var retFrame gopacket.Packet = frame
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Info("Processing actions")
+
+ for _, instruction := range flow.Instructions {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "instruction": instruction,
+ }).Debug("Processing actions - Instruction entry")
+ if instruction.Type == uint32(openflow_13.OfpInstructionType_OFPIT_APPLY_ACTIONS) {
+ for _, action := range instruction.GetActions().GetActions() {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "action": action,
+ "actionType": action.Type,
+ }).Debug("Processing actions - Action entry")
+
+ switch action.Type {
+ case openflow_13.OfpActionType_OFPAT_OUTPUT:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Debug("Processing action OFPAT output")
+ egressPort = action.GetOutput().Port
+
+ case openflow_13.OfpActionType_OFPAT_POP_VLAN:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Debug("Processing action OFPAT POP VLAN")
+ if shim := common.GetDot1QLayer(retFrame); shim != nil {
+ if eth := common.GetEthernetLayer(retFrame); eth != nil {
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: eth.SrcMAC,
+ DstMAC: eth.DstMAC,
+ EthernetType: shim.Type,
+ }
+ buffer := gopacket.NewSerializeBuffer()
+ gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
+ ethernetLayer,
+ gopacket.Payload(shim.Payload),
+ )
+ retFrame = gopacket.NewPacket(
+ buffer.Bytes(),
+ layers.LayerTypeEthernet,
+ gopacket.Default,
+ )
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("No ETH found while processing POP VLAN action")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("No DOT1Q found while processing POP VLAN action")
+ }
+ case openflow_13.OfpActionType_OFPAT_PUSH_VLAN:
+ if eth := common.GetEthernetLayer(retFrame); eth != nil {
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: eth.SrcMAC,
+ DstMAC: eth.DstMAC,
+ EthernetType: layers.EthernetType(action.GetPush().GetEthertype()),
+ }
+ dot1qLayer := &layers.Dot1Q{
+ Type: eth.EthernetType,
+ }
+
+ buffer := gopacket.NewSerializeBuffer()
+ gopacket.SerializeLayers(
+ buffer,
+ gopacket.SerializeOptions{
+ FixLengths: false,
+ },
+ ethernetLayer,
+ dot1qLayer,
+ gopacket.Payload(eth.Payload),
+ )
+ retFrame = gopacket.NewPacket(
+ buffer.Bytes(),
+ layers.LayerTypeEthernet,
+ gopacket.Default,
+ )
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("No ETH found while processing PUSH VLAN action")
+ }
+ case openflow_13.OfpActionType_OFPAT_SET_FIELD:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Debug("Processing action OFPAT SET FIELD")
+ if action.GetSetField().GetField().GetOxmClass() ==
+ openflow_13.OfpOxmClass_OFPXMC_OPENFLOW_BASIC {
+ field := action.GetSetField().GetField().GetOfbField()
+
+ switch field.Type {
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_VID:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Debug("Processing action OFPAT SET FIELD - VLAN VID")
+ if shim := common.GetDot1QLayer(retFrame); shim != nil {
+ eth := common.GetEthernetLayer(retFrame)
+ buffer := gopacket.NewSerializeBuffer()
+
+ var dot1qLayer *layers.Dot1Q
+ var ethernetLayer *layers.Ethernet
+ ethernetLayer = &layers.Ethernet{
+ SrcMAC: eth.SrcMAC,
+ DstMAC: eth.DstMAC,
+ EthernetType: eth.EthernetType,
+ }
+
+ dot1qLayer = &layers.Dot1Q{
+ Type: shim.Type,
+ VLANIdentifier: uint16(field.GetVlanVid() & 4095),
+ }
+
+ gopacket.SerializeLayers(
+ buffer,
+ gopacket.SerializeOptions{},
+ ethernetLayer,
+ dot1qLayer,
+ gopacket.Payload(shim.LayerPayload()),
+ )
+ retFrame = gopacket.NewPacket(
+ buffer.Bytes(),
+ layers.LayerTypeEthernet,
+ gopacket.Default,
+ )
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "frameDump": retFrame.Dump(),
+ "vlanVid": shim.VLANIdentifier,
+ }).Info("Setting DOT1Q VLAN VID")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("No DOT1Q found while setting VLAN VID")
+ }
+
+ case openflow_13.OxmOfbFieldTypes_OFPXMT_OFB_VLAN_PCP:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Debug("Processing action OFPAT SET FIELD - VLAN PCP")
+ if shim := common.GetDot1QLayer(retFrame); shim != nil {
+ shim.Priority = uint8(field.GetVlanPcp())
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "priority": shim.Priority,
+ }).Info("Setting DOT1Q VLAN PCP")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("No DOT1Q found while setting VLAN PCP")
+ }
+ default:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "type": field.Type,
+ }).Warn("Set field not implemented for this type")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ }).Warn("Field not of type OF-BASIC")
+ }
+ default:
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "frame": retFrame,
+ "type": action.Type,
+ }).Warn("Action type not implemented")
+ }
+ }
+ }
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "flow": flow,
+ "egressPort": egressPort,
+ "retFrame": retFrame,
+ }).Debug("Processed actions")
+
+ return egressPort, retFrame
+}
diff --git a/ponsim/v2/core/ponsim_device_state.go b/ponsim/v2/core/ponsim_device_state.go
new file mode 100644
index 0000000..a5805a2
--- /dev/null
+++ b/ponsim/v2/core/ponsim_device_state.go
@@ -0,0 +1,22 @@
+package core
+
+type PonSimDeviceState uint8
+
+const (
+ DISCONNECTED_FROM_PON PonSimDeviceState = iota
+ CONNECTED_TO_PON
+ REGISTERED_WITH_OLT
+ CONNECTED_IO_INTERFACE
+)
+
+// Execute state string equivalents
+var PonSimDeviceStateEnum = []string{
+ "DISCONNECTED_FROM_PON",
+ "CONNECTED_TO_PON",
+ "REGISTERED_WITH_OLT",
+ "CONNECTED_IO_INTERFACE",
+}
+
+func (s PonSimDeviceState) String() string {
+ return PonSimDeviceStateEnum[s]
+}
diff --git a/ponsim/v2/core/ponsim_device_type.go b/ponsim/v2/core/ponsim_device_type.go
new file mode 100644
index 0000000..306440c
--- /dev/null
+++ b/ponsim/v2/core/ponsim_device_type.go
@@ -0,0 +1,17 @@
+package core
+
+type PonSimDeviceType uint8
+
+const (
+ OLT PonSimDeviceType = iota
+ ONU
+)
+
+var enum_ponsim_device_types = []string{
+ "OLT",
+ "ONU",
+}
+
+func (t PonSimDeviceType) String() string {
+ return enum_ponsim_device_types[t]
+}
diff --git a/ponsim/v2/core/ponsim_interface.go b/ponsim/v2/core/ponsim_interface.go
new file mode 100644
index 0000000..d73d2ca
--- /dev/null
+++ b/ponsim/v2/core/ponsim_interface.go
@@ -0,0 +1,18 @@
+package core
+
+import (
+ "context"
+ "github.com/google/gopacket"
+)
+
+type PonSimInterface interface {
+ Start(context.Context)
+
+ Stop(context.Context)
+
+ GetAddress() string
+
+ GetPort() int32
+
+ Forward(context.Context, int, gopacket.Packet) error
+}
diff --git a/ponsim/v2/core/ponsim_metric.go b/ponsim/v2/core/ponsim_metric.go
new file mode 100644
index 0000000..d28c420
--- /dev/null
+++ b/ponsim/v2/core/ponsim_metric.go
@@ -0,0 +1,220 @@
+package core
+
+import (
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/voltha"
+ "github.com/sirupsen/logrus"
+)
+
+/*
+metricCounter holds details for a specific metric
+*/
+type metricCounter struct {
+ Name string
+ Value [2]int // [PON,NNI] values
+ Min int
+ Max int
+}
+
+/*
+Create a new MetricCounter instance for TX packets
+*/
+func newTxMetricCounter(name txMetricCounterType, min int, max int) *metricCounter {
+ return &metricCounter{Name: name.String(), Min: min, Max: max}
+}
+
+/*
+Create a new MetricCounter instance for RX packets
+*/
+func newRxMetricCounter(name rxMetricCounterType, min int, max int) *metricCounter {
+ return &metricCounter{Name: name.String(), Min: min, Max: max}
+}
+
+/*
+Define TX constants
+*/
+type txMetricCounterType uint8
+
+const (
+ tx_64_pkts txMetricCounterType = iota
+ tx_65_127_pkts
+ tx_128_255_pkts
+ tx_256_511_pkts
+ tx_512_1023_pkts
+ tx_1024_1518_pkts
+ tx_1519_9k_pkts
+)
+
+/*
+TX packet constants string equivalents
+*/
+var txMetricCounterEnum = []string{
+ "tx_64_pkts",
+ "tx_65_127_pkts",
+ "tx_128_255_pkts",
+ "tx_256_511_pkts",
+ "tx_512_1023_pkts",
+ "tx_1024_1518_pkts",
+ "tx_1519_9k_pkts",
+}
+
+func (t txMetricCounterType) String() string {
+ return txMetricCounterEnum[t]
+}
+
+/*
+Define RX constants
+*/
+type rxMetricCounterType uint8
+
+const (
+ rx_64_pkts rxMetricCounterType = iota
+ rx_65_127_pkts
+ rx_128_255_pkts
+ rx_256_511_pkts
+ rx_512_1023_pkts
+ rx_1024_1518_pkts
+ rx_1519_9k_pkts
+)
+
+/*
+RX packet constants string equivalents
+*/
+var rxMetricCounterEnum = []string{
+ "rx_64_pkts",
+ "rx_65_127_pkts",
+ "rx_128_255_pkts",
+ "rx_256_511_pkts",
+ "rx_512_1023_pkts",
+ "rx_1024_1518_pkts",
+ "rx_1519_9k_pkts",
+}
+
+func (t rxMetricCounterType) String() string {
+ return rxMetricCounterEnum[t]
+}
+
+/*
+
+ */
+type PonSimMetricCounter struct {
+ Name string
+ TxCounters map[txMetricCounterType]*metricCounter
+ RxCounters map[rxMetricCounterType]*metricCounter
+}
+
+/*
+NewPonSimMetricCounter instantiates new metric counters for a PON device
+*/
+func NewPonSimMetricCounter(name string) *PonSimMetricCounter {
+ counter := &PonSimMetricCounter{Name: name}
+
+ counter.TxCounters = map[txMetricCounterType]*metricCounter{
+ tx_64_pkts: newTxMetricCounter(tx_64_pkts, 1, 64),
+ tx_65_127_pkts: newTxMetricCounter(tx_65_127_pkts, 65, 127),
+ tx_128_255_pkts: newTxMetricCounter(tx_128_255_pkts, 128, 255),
+ tx_256_511_pkts: newTxMetricCounter(tx_256_511_pkts, 256, 511),
+ tx_512_1023_pkts: newTxMetricCounter(tx_512_1023_pkts, 512, 1023),
+ tx_1024_1518_pkts: newTxMetricCounter(tx_1024_1518_pkts, 1024, 1518),
+ tx_1519_9k_pkts: newTxMetricCounter(tx_1519_9k_pkts, 1519, 9216),
+ }
+ counter.RxCounters = map[rxMetricCounterType]*metricCounter{
+ rx_64_pkts: newRxMetricCounter(rx_64_pkts, 1, 64),
+ rx_65_127_pkts: newRxMetricCounter(rx_65_127_pkts, 65, 127),
+ rx_128_255_pkts: newRxMetricCounter(rx_128_255_pkts, 128, 255),
+ rx_256_511_pkts: newRxMetricCounter(rx_256_511_pkts, 256, 511),
+ rx_512_1023_pkts: newRxMetricCounter(rx_512_1023_pkts, 512, 1023),
+ rx_1024_1518_pkts: newRxMetricCounter(rx_1024_1518_pkts, 1024, 1518),
+ rx_1519_9k_pkts: newRxMetricCounter(rx_1519_9k_pkts, 1519, 9216),
+ }
+
+ return counter
+}
+
+/*
+CountRxFrame increments the receive count for a specific packet size metric
+*/
+func (mc *PonSimMetricCounter) CountRxFrame(port int, size int) {
+ for k, v := range mc.RxCounters {
+ if size >= v.Min && size <= v.Max {
+ mc.RxCounters[k].Value[port-1] += 1
+ }
+ }
+}
+
+/*
+CountTxFrame increments the transmit count for a specific packet size metric
+*/
+func (mc *PonSimMetricCounter) CountTxFrame(port int, size int) {
+ for k, v := range mc.TxCounters {
+ if size >= v.Min && size <= v.Max {
+ mc.TxCounters[k].Value[port-1] += 1
+ }
+ }
+}
+
+/*
+LogCounts logs the current counts for all RX/TX packets
+*/
+func (mc *PonSimMetricCounter) LogCounts() {
+ common.Logger().WithFields(logrus.Fields{
+ "counters": mc.RxCounters,
+ }).Info("RX Metrics")
+ common.Logger().WithFields(logrus.Fields{
+ "counters": mc.TxCounters,
+ }).Info("TX Metrics")
+}
+
+/*
+MakeProto collects all RX/TX metrics with which it constructs a GRPC proto metrics structure
+*/
+func (mc *PonSimMetricCounter) MakeProto() *voltha.PonSimMetrics {
+ simMetrics := &voltha.PonSimMetrics{Device: mc.Name}
+ ponMetrics := &voltha.PonSimPortMetrics{PortName: "pon"}
+ nniMetrics := &voltha.PonSimPortMetrics{PortName: "nni"}
+
+ // Collect RX metrics
+ for _, c := range mc.RxCounters {
+ // PON values
+ ponMetrics.Packets = append(
+ ponMetrics.Packets,
+ &voltha.PonSimPacketCounter{
+ Name: c.Name,
+ Value: int64(c.Value[0]),
+ },
+ )
+ // NNI values
+ nniMetrics.Packets = append(
+ nniMetrics.Packets,
+ &voltha.PonSimPacketCounter{
+ Name: c.Name,
+ Value: int64(c.Value[1]),
+ },
+ )
+ }
+ // Collect TX metrics
+ for _, c := range mc.TxCounters {
+ // PON values
+ ponMetrics.Packets = append(
+ ponMetrics.Packets,
+ &voltha.PonSimPacketCounter{
+ Name: c.Name,
+ Value: int64(c.Value[0]),
+ },
+ )
+ // NNI values
+ nniMetrics.Packets = append(
+ nniMetrics.Packets,
+ &voltha.PonSimPacketCounter{
+ Name: c.Name,
+ Value: int64(c.Value[1]),
+ },
+ )
+ }
+
+ // Populate GRPC proto structure
+ simMetrics.Metrics = append(simMetrics.Metrics, ponMetrics)
+ simMetrics.Metrics = append(simMetrics.Metrics, nniMetrics)
+
+ return simMetrics
+}
diff --git a/ponsim/v2/core/ponsim_olt.go b/ponsim/v2/core/ponsim_olt.go
new file mode 100644
index 0000000..2485a67
--- /dev/null
+++ b/ponsim/v2/core/ponsim_olt.go
@@ -0,0 +1,403 @@
+package core
+
+import (
+ "context"
+ "crypto/tls"
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/google/gopacket"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/ponsim"
+ "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/connectivity"
+ "google.golang.org/grpc/credentials"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// TODO: Pass-in the certificate information as a structure parameter
+
+/*
+PonSimOltDevice is the structure responsible for the handling of an OLT device
+*/
+type PonSimOltDevice struct {
+ PonSimDevice `json:pon_device`
+ VCoreEndpoint string `json:vcore_ep`
+ MaxOnuCount int `json:max_onu`
+ Onus map[int32]*OnuRegistree `json:onu_registrees`
+ outgoing chan []byte
+
+ counterLoop *common.IntervalHandler
+ alarmLoop *common.IntervalHandler
+}
+
+/*
+
+ */
+type OnuRegistree struct {
+ Device *PonSimOnuDevice `json:onu_device`
+ Conn *grpc.ClientConn `json:grpc_conn`
+ Client ponsim.PonSimCommonClient `json:client`
+ Stream ponsim.PonSimCommon_ProcessDataClient `json:stream`
+}
+
+const (
+ BASE_PORT_NUMBER = 128
+)
+
+/*
+NewPonSimOltDevice instantiates a new OLT device structure
+*/
+func NewPonSimOltDevice(device PonSimDevice) *PonSimOltDevice {
+ olt := &PonSimOltDevice{PonSimDevice: device}
+ return olt
+}
+
+/*
+forwardToONU defines a EGRESS function to forward a packet to a specific ONU
+*/
+func (o *PonSimOltDevice) forwardToONU(onuPort int32) func(int, gopacket.Packet) {
+ return func(port int, frame gopacket.Packet) {
+ ipAddress := common.GetInterfaceIP(o.ExternalIf)
+ incoming := &ponsim.IncomingData{
+ Id: "EGRESS.OLT." + ipAddress,
+ Address: ipAddress,
+ Port: int32(port),
+ Payload: frame.Data(),
+ }
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Debug("Forwarding to ONU")
+
+ // Forward packet to ONU
+ if err := o.GetOnu(onuPort).Stream.Send(incoming); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "frameDump": frame.Dump(),
+ "incoming": incoming,
+ "error": err.Error(),
+ }).Error("A problem occurred while forwarding to ONU")
+ }
+
+ }
+}
+
+/*
+forwardToLAN defines an INGRESS function to forward a packet to VOLTHA
+*/
+func (o *PonSimOltDevice) forwardToLAN() func(int, gopacket.Packet) {
+ return func(port int, frame gopacket.Packet) {
+ common.Logger().WithFields(logrus.Fields{
+ "frame": frame.Dump(),
+ }).Info("Sending packet")
+
+ select {
+ case o.outgoing <- frame.Data():
+ common.Logger().WithFields(logrus.Fields{
+ "frame": frame.Dump(),
+ }).Info("Sent packet")
+ default:
+ common.Logger().WithFields(logrus.Fields{
+ "frame": frame.Dump(),
+ }).Warn("Unable to send packet")
+ }
+ }
+}
+
+/*
+Start performs setup operations for an OLT device
+*/
+func (o *PonSimOltDevice) Start(ctx context.Context) {
+ common.Logger().Info("Starting OLT device...")
+ o.PonSimDevice.Start(ctx)
+
+ // Open network interfaces for listening
+ o.connectNetworkInterfaces()
+
+ o.outgoing = make(chan []byte, 1)
+
+ // Add INGRESS operation
+ o.AddLink(2, 0, o.forwardToLAN())
+
+ // Start PM counter logging
+ o.counterLoop = common.NewIntervalHandler(90, o.Counter.LogCounts)
+ o.counterLoop.Start()
+
+ // Start alarm simulation
+ if o.AlarmsOn {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Starting alarm simulation")
+
+ alarms := NewPonSimAlarm(o.InternalIf, o.VCoreEndpoint, o.forwardToLAN())
+ o.alarmLoop = common.NewIntervalHandler(o.AlarmsFreq, alarms.GenerateAlarm)
+ o.alarmLoop.Start()
+ }
+}
+
+/*
+Stop performs cleanup operations for an OLT device
+*/
+func (o *PonSimOltDevice) Stop(ctx context.Context) {
+ common.Logger().Info("Stopping OLT device...")
+
+ // Stop PM counters loop
+ o.counterLoop.Stop()
+ o.counterLoop = nil
+
+ // Stop alarm simulation
+ if o.AlarmsOn {
+ o.alarmLoop.Stop()
+ }
+ o.alarmLoop = nil
+
+ o.ingressHandler.Close()
+ o.egressHandler.Close()
+
+ o.PonSimDevice.Stop(ctx)
+}
+
+/*
+ConnectToRemoteOnu establishes communication to a remote ONU device
+*/
+func (o *PonSimOltDevice) ConnectToRemoteOnu(onu *OnuRegistree) error {
+ var err error
+
+ host := strings.Join([]string{
+ onu.Device.Address,
+ strconv.Itoa(int(onu.Device.Port)),
+ }, ":")
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "host": host,
+ }).Debug("Formatting host address")
+
+ // TODO: make it secure
+ ta := credentials.NewTLS(&tls.Config{
+ //Certificates: []tls.Certificate{peerCert},
+ //RootCAs: caCertPool,
+ InsecureSkipVerify: true,
+ })
+
+ // GRPC communication needs to be secured
+ if onu.Conn, err = grpc.DialContext(
+ context.Background(),
+ host,
+ grpc.WithTransportCredentials(ta),
+ ); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "error": err.Error(),
+ }).Error("Problem with client connection")
+ }
+
+ return err
+}
+
+/*
+Listen waits for incoming EGRESS data on the internal interface
+*/
+func (o *PonSimOltDevice) Listen(ctx context.Context, port int32) {
+ var reply *empty.Empty
+ var err error
+
+ // Establish a GRPC connection with the ONU
+ onu := o.GetOnu(port)
+
+ common.Logger().WithFields(logrus.Fields{
+ "onu": onu,
+ }).Debug("Connecting to remote ONU")
+
+ if onu.Client = ponsim.NewPonSimCommonClient(onu.Conn); onu.Client == nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Error("Problem establishing client connection to ONU")
+ o.RemoveOnu(ctx, port)
+ return
+ }
+
+ // Prepare stream to ONU to forward incoming data as needed
+ if onu.Stream, err = onu.Client.ProcessData(ctx); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Error("Problem establishing stream to ONU")
+ o.RemoveOnu(ctx, port)
+ return
+ }
+
+ defer o.egressHandler.Close()
+ packetSource := gopacket.NewPacketSource(o.egressHandler, o.egressHandler.LinkType())
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.InternalIf,
+ }).Debug("Listening to incoming EGRESS data")
+
+ // Wait for incoming EGRESS data
+ for packet := range packetSource.Packets() {
+ if dot1q := common.GetDot1QLayer(packet); dot1q != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "packet": packet,
+ }).Debug("Received EGRESS packet")
+
+ o.Forward(ctx, 2, packet)
+ }
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("No more packets to process")
+
+ if reply, err = onu.Stream.CloseAndRecv(); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "error": err.Error(),
+ }).Error("A problem occurred while closing client stream")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "reply": reply,
+ }).Warn("Client stream closed")
+ }
+}
+
+/*
+GetOnus returns the list of registered ONU devices
+*/
+func (o *PonSimOltDevice) GetOnus() map[int32]*OnuRegistree {
+ if o.Onus == nil {
+ o.Onus = make(map[int32]*OnuRegistree)
+ }
+
+ return o.Onus
+}
+
+/*
+GetOnu return a specific registered ONU
+*/
+func (o *PonSimOltDevice) GetOnu(index int32) *OnuRegistree {
+ var onu *OnuRegistree
+ var ok bool
+
+ if onu, ok = (o.GetOnus())[index]; ok {
+ return onu
+ }
+
+ return nil
+}
+
+func (o *PonSimOltDevice) GetOutgoing() chan []byte {
+ return o.outgoing
+}
+
+/*
+nextAvailablePort returns a port that is not already used by a registered ONU
+*/
+func (o *PonSimOltDevice) nextAvailablePort() int32 {
+ var port int32 = BASE_PORT_NUMBER
+
+ if len(o.GetOnus()) < o.MaxOnuCount {
+ for {
+ if o.GetOnu(port) != nil {
+ // port is already used
+ port += 1
+ } else {
+ // port is available... use it
+ return port
+ }
+ }
+ } else {
+ // OLT has reached its max number of ONUs
+ return -1
+ }
+}
+
+/*
+AddOnu registers an ONU device and sets up all required monitoring and connections
+*/
+func (o *PonSimOltDevice) AddOnu(onu *PonSimOnuDevice) (int32, error) {
+ var portNum int32
+ ctx := context.Background()
+
+ if portNum = o.nextAvailablePort(); portNum != -1 {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": portNum,
+ "onu": onu,
+ }).Info("Adding ONU")
+
+ registree := &OnuRegistree{Device: onu}
+
+ // Setup GRPC communication and check if it succeeded
+ if err := o.ConnectToRemoteOnu(registree); err == nil {
+ o.GetOnus()[portNum] = registree
+
+ o.AddLink(1, int(portNum), o.forwardToONU(portNum))
+ go o.MonitorOnu(ctx, portNum)
+ go o.Listen(ctx, portNum)
+ }
+
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Warn("ONU Map is full")
+ }
+
+ return int32(portNum), nil
+}
+
+/*
+RemoveOnu removes the reference to a registered ONU
+*/
+func (o *PonSimOltDevice) RemoveOnu(ctx context.Context, onuIndex int32) error {
+ onu := o.GetOnu(onuIndex)
+ if err := onu.Conn.Close(); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "onu": onu.Device,
+ "onuIndex": onuIndex,
+ }).Error("Problem closing connection to ONU")
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "onu": onu,
+ "onuIndex": onuIndex,
+ }).Info("Removing ONU")
+
+ delete(o.Onus, onuIndex)
+
+ // Remove link entries for this ONU
+ o.RemoveLink(1, int(onuIndex))
+
+ return nil
+}
+
+/*
+MonitorOnu verifies the connection status of a specific ONU and cleans up as necessary
+*/
+func (o *PonSimOltDevice) MonitorOnu(ctx context.Context, onuIndex int32) {
+ for {
+ if o.GetOnu(onuIndex) != nil {
+ if conn := o.GetOnu(onuIndex).Conn; conn.GetState() == connectivity.Ready {
+ // Wait for any change to occur
+ conn.WaitForStateChange(ctx, conn.GetState())
+ // We lost communication with the ONU ... remove it
+ o.RemoveOnu(ctx, onuIndex)
+ return
+ }
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "ctx": ctx,
+ "onuIndex": onuIndex,
+ }).Debug("ONU is not ready")
+ time.Sleep(1 * time.Second)
+ } else {
+ return
+ }
+ }
+}
diff --git a/ponsim/v2/core/ponsim_onu.go b/ponsim/v2/core/ponsim_onu.go
new file mode 100644
index 0000000..f58d473
--- /dev/null
+++ b/ponsim/v2/core/ponsim_onu.go
@@ -0,0 +1,388 @@
+package core
+
+import (
+ "context"
+ "crypto/tls"
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/google/gopacket"
+ "github.com/google/uuid"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/ponsim"
+ "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+)
+
+// TODO: Cleanup GRPC security config
+// TODO: Pass-in the certificate information as a structure parameter
+
+/*
+PonSimOnuDevice is the structure responsible for the handling of an ONU device
+*/
+type PonSimOnuDevice struct {
+ PonSimDevice
+
+ ParentAddress string
+ ParentPort int32
+ AssignedPort int32
+ Conn *grpc.ClientConn
+
+ oltClient ponsim.PonSimCommonClient
+ stream ponsim.PonSimCommon_ProcessDataClient
+ monitor chan PonSimDeviceState
+ state PonSimDeviceState
+}
+
+/*
+NewPonSimOnuDevice instantiates a new ONU device structure
+*/
+func NewPonSimOnuDevice(device PonSimDevice) *PonSimOnuDevice {
+ onu := &PonSimOnuDevice{PonSimDevice: device}
+
+ return onu
+}
+
+/*
+forwardToOLT defines a INGRESS function to forward a packet to the parent OLT
+*/
+func (o *PonSimOnuDevice) forwardToOLT() func(int, gopacket.Packet) {
+ return func(port int, frame gopacket.Packet) {
+ ipAddress := common.GetInterfaceIP(o.InternalIf)
+ incoming := &ponsim.IncomingData{
+ Id: "INGRESS.ONU." + ipAddress,
+ Address: ipAddress,
+ Port: int32(port),
+ Payload: frame.Data(),
+ }
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ "frameDump": frame.Dump(),
+ "incoming": incoming,
+ }).Debug("Forwarding to OLT")
+
+ // Forward packet to OLT
+ if err := o.stream.Send(incoming); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frameDump": frame.Dump(),
+ "incoming": incoming,
+ }).Fatal("A problem occurred while forwarding to OLT")
+ }
+ }
+}
+
+/*
+forwardToWAN defines a EGRESS function to forward a packet to the world
+*/
+func (o *PonSimOnuDevice) forwardToWAN() func(int, gopacket.Packet) {
+ return func(port int, frame gopacket.Packet) {
+ var err error
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Debug("Forwarding packet to world")
+ if err = o.ingressHandler.WritePacketData(frame.Data()); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Fatal("Problem while forwarding packet to world")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "port": port,
+ "frame": frame,
+ }).Debug("Forwarded packet to world")
+ }
+ }
+}
+
+/*
+Start performs setup operations for an ONU device
+*/
+func (o *PonSimOnuDevice) Start(ctx context.Context) {
+ // Initialize the parent
+ o.PonSimDevice.Start(ctx)
+
+ // Setup flow behaviours
+ // ONU -> OLT
+ o.AddLink(1, 0, o.forwardToOLT())
+ // ONU -> World
+ o.AddLink(2, 0, o.forwardToWAN())
+
+ go o.MonitorConnection(ctx)
+}
+
+/*
+Stop performs cleanup operations for an ONU device
+*/
+func (o *PonSimOnuDevice) Stop(ctx context.Context) {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Stopping ONU")
+
+ o.RemoveLink(1, 0)
+ o.RemoveLink(2, 0)
+
+ o.PonSimDevice.Stop(ctx)
+}
+
+/*
+Listen waits for incoming INGRESS data on the external interface
+*/
+func (o *PonSimOnuDevice) Listen(ctx context.Context) {
+ var reply *empty.Empty
+ var err error
+
+ if o.oltClient = ponsim.NewPonSimCommonClient(o.Conn); o.oltClient == nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Fatal("Problem establishing client connection to OLT")
+ panic("Problem establishing client connection to OLT")
+ }
+
+ // Establish GRPC connection with OLT
+ if o.stream, err = o.oltClient.ProcessData(ctx); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "error": err.Error(),
+ }).Fatal("Problem establishing stream")
+ panic(err)
+ }
+
+ defer o.ingressHandler.Close()
+ packetSource := gopacket.NewPacketSource(o.ingressHandler, o.ingressHandler.LinkType())
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "interface": o.ExternalIf,
+ }).Debug("Listening to incoming ONU data")
+
+ for packet := range packetSource.Packets() {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "packet": packet,
+ }).Debug("Received INGRESS packet")
+
+ o.Forward(ctx, 2, packet)
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("No more packets to process")
+
+ if reply, err = o.stream.CloseAndRecv(); err != nil {
+ common.Logger().Fatal("A problem occurred while closing Ingress stream", err.Error())
+ } else {
+ common.Logger().Info("Ingress stream closed", reply)
+ }
+}
+
+/*
+Register sends a registration request to the remote OLT
+*/
+func (o *PonSimOnuDevice) Register(ctx context.Context) error {
+ var err error
+ var rreq *ponsim.RegistrationRequest
+ var rrep *ponsim.RegistrationReply
+ var client ponsim.PonSimOltClient
+
+ if o.Conn != nil {
+ if client = ponsim.NewPonSimOltClient(o.Conn); client != nil {
+ rreq = &ponsim.RegistrationRequest{
+ Id: uuid.New().String(),
+ Address: common.GetInterfaceIP(o.InternalIf),
+ Port: o.Port,
+ }
+ common.Logger().Printf("Request details %+v\n", rreq)
+
+ // TODO: Loop registration until an OLT becomes available??
+
+ rrep, err = client.Register(ctx, rreq)
+ if err != nil {
+ common.Logger().Printf("Problem with registration", err.Error())
+ } else {
+ // Save OLT address details
+ o.ParentAddress = rrep.GetParentAddress()
+ o.ParentPort = rrep.GetParentPort()
+ o.AssignedPort = rrep.GetAssignedPort()
+
+ common.Logger().Printf("Registration details - %+v\n", rrep)
+
+ o.monitor <- REGISTERED_WITH_OLT
+ }
+
+ } else {
+ common.Logger().Info("Client is NIL")
+ }
+ }
+
+ return err
+}
+
+/*
+MonitorConnection verifies the communication with the OLT
+*/
+func (o *PonSimOnuDevice) MonitorConnection(ctx context.Context) {
+ for {
+ if o.state == DISCONNECTED_FROM_PON {
+ // Establish communication with OLT
+ o.Connect(ctx)
+ }
+
+ if o.state == CONNECTED_TO_PON {
+ // Just stay idle while the ONU-OLT connection is up
+ o.Conn.WaitForStateChange(ctx, o.Conn.GetState())
+
+ // The ONU-OLT connection was lost... need to cleanup
+ o.Disconnect(ctx)
+ }
+
+ time.Sleep(1 * time.Second)
+ }
+}
+
+/*
+Connect sets up communication and monitoring with remote OLT
+*/
+func (o *PonSimOnuDevice) Connect(ctx context.Context) {
+ o.monitor = make(chan PonSimDeviceState, 1)
+
+ // Define a waitgroup to block the current routine until
+ // a CONNECTED state is reached
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+
+ go o.MonitorState(ctx, &wg)
+
+ o.ConnectToRemoteOlt()
+
+ // Wait until we establish a connection to the remote PON
+ wg.Wait()
+}
+
+/*
+Disconnect tears down communication and monitoring with remote OLT
+*/
+func (o *PonSimOnuDevice) Disconnect(ctx context.Context) {
+ if o.egressHandler != nil {
+ o.egressHandler.Close()
+ o.egressHandler = nil
+ }
+
+ if o.Conn != nil {
+ o.Conn.Close()
+ o.Conn = nil
+ }
+
+ if o.monitor != nil {
+ close(o.monitor)
+ o.monitor = nil
+ o.state = DISCONNECTED_FROM_PON
+ }
+}
+
+/*
+MonitorState follows the progress of the OLT connection
+*/
+func (o *PonSimOnuDevice) MonitorState(ctx context.Context, wg *sync.WaitGroup) {
+ // Start a concurrent routine to handle ONU state changes
+ var ok bool
+ for {
+ select {
+ case o.state, ok = <-o.monitor:
+ if ok {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "state": o.state,
+ }).Info("Received monitoring state")
+
+ switch o.state {
+ case CONNECTED_TO_PON:
+ // We have successfully connected to the OLT
+ // proceed with registration
+ wg.Done()
+
+ if err := o.Register(ctx); err != nil {
+ o.Disconnect(ctx)
+ }
+
+ case DISCONNECTED_FROM_PON:
+ // Connection to remote OLT was lost... exit
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Warn("Exiting due to disconnection")
+ return
+
+ case REGISTERED_WITH_OLT:
+ // Start listening on network interfaces
+ o.connectNetworkInterfaces()
+ o.monitor <- CONNECTED_IO_INTERFACE
+
+ case CONNECTED_IO_INTERFACE:
+ // Start listening on local interfaces
+ go o.Listen(ctx)
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Warn("Monitoring channel has closed")
+ return
+ }
+ case <-ctx.Done():
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Warn("Received a cancellation notification")
+
+ return
+ }
+ }
+}
+
+/*
+ConnectToRemoteOlt establishes GRPC communication with the remote OLT
+*/
+func (o *PonSimOnuDevice) ConnectToRemoteOlt() {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Debug("Connecting to remote device")
+
+ var err error
+
+ host := strings.Join([]string{
+ o.ParentAddress,
+ strconv.Itoa(int(o.ParentPort)),
+ }, ":")
+
+ // TODO: make it secure
+ // GRPC communication needs to be secured
+ ta := credentials.NewTLS(&tls.Config{
+ //Certificates: []tls.Certificate{peerCert},
+ //RootCAs: caCertPool,
+ InsecureSkipVerify: true,
+ })
+
+ if o.Conn, err = grpc.DialContext(
+ context.Background(), host, grpc.WithTransportCredentials(ta), grpc.WithBlock(),
+ ); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ "error": err.Error(),
+ }).Error("Problem establishing connection")
+ } else {
+ // We are now connected
+ // time to move on
+ common.Logger().WithFields(logrus.Fields{
+ "device": o,
+ }).Info("Connected to OLT")
+ }
+
+ o.monitor <- CONNECTED_TO_PON
+}
diff --git a/ponsim/v2/core/xponsim_device.go b/ponsim/v2/core/xponsim_device.go
new file mode 100644
index 0000000..aa7d64a
--- /dev/null
+++ b/ponsim/v2/core/xponsim_device.go
@@ -0,0 +1,215 @@
+package core
+
+import (
+ "context"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/bbf_fiber"
+ "github.com/opencord/voltha/protos/go/voltha"
+ "github.com/sirupsen/logrus"
+)
+
+type XPonSimDevice struct {
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) Start(ctx context.Context) {
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) Stop(ctx context.Context) {
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) CreateInterface(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("create-interface-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) UpdateInterface(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("update-interface-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) RemoveInterface(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("remove-interface-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) CreateTcont(ctx context.Context,
+ config *bbf_fiber.TcontsConfigData,
+ profile *bbf_fiber.TrafficDescriptorProfileData,
+) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "tcont_config_data": config,
+ "traffic_descriptor_profile_config_data": profile,
+ }).Info("create-tcont-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) UpdateTcont(
+ ctx context.Context,
+ config *bbf_fiber.TcontsConfigData,
+ profile *bbf_fiber.TrafficDescriptorProfileData,
+) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "tcont_config_data": config,
+ "traffic_descriptor_profile_config_data": profile,
+ }).Info("update-tcont-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) RemoveTcont(
+ ctx context.Context,
+ config *bbf_fiber.TcontsConfigData,
+ profile *bbf_fiber.TrafficDescriptorProfileData,
+) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "tcont_config_data": config,
+ "traffic_descriptor_profile_config_data": profile,
+ }).Info("remove-tcont-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) CreateGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("create-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) UpdateGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("update-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) RemoveGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("remove-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) CreateMulticastGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("create-multicast-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) UpdateMulticastGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("update-multicast-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) RemoveMulticastGemport(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("remove-multicast-gemport-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) CreateMulticastDistributionSet(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("create-multicast-distribution-set-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) UpdateMulticastDistributionSet(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("update-multicast-distribution-set-request")
+}
+
+/*
+
+ */
+func (d *XPonSimDevice) RemoveMulticastDistributionSet(ctx context.Context, config *voltha.InterfaceConfig) {
+ common.Logger().WithFields(logrus.Fields{
+ "context": ctx,
+ "device": d,
+ "interface_type": config.GetInterfaceType(),
+ "data": config,
+ }).Info("remove-multicast-distribution-set-request")
+}
diff --git a/ponsim/v2/grpc/grpc_security.go b/ponsim/v2/grpc/grpc_security.go
new file mode 100644
index 0000000..6dab468
--- /dev/null
+++ b/ponsim/v2/grpc/grpc_security.go
@@ -0,0 +1,7 @@
+package grpc
+
+type GrpcSecurity struct {
+ KeyFile string
+ CertFile string
+ CaFile string
+}
diff --git a/ponsim/v2/grpc/grpc_server.go b/ponsim/v2/grpc/grpc_server.go
new file mode 100644
index 0000000..aaa8a4d
--- /dev/null
+++ b/ponsim/v2/grpc/grpc_server.go
@@ -0,0 +1,162 @@
+package grpc
+
+import (
+ "net"
+
+ "context"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/ponsim/v2/grpc/nbi"
+ "github.com/opencord/voltha/ponsim/v2/grpc/sbi"
+ "github.com/opencord/voltha/protos/go/bal"
+ "github.com/opencord/voltha/protos/go/ponsim"
+ "github.com/opencord/voltha/protos/go/voltha"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+ "strconv"
+ "strings"
+)
+
+type GrpcServer struct {
+ gs *grpc.Server
+ address string
+ port int32
+ secure bool
+ services []func(*grpc.Server)
+
+ *GrpcSecurity
+}
+
+/*
+Instantiate a GRPC server data structure
+*/
+func NewGrpcServer(
+ address string,
+ port int32,
+ certs *GrpcSecurity,
+ secure bool,
+) *GrpcServer {
+ server := &GrpcServer{
+ address: address,
+ port: port,
+ secure: secure,
+ GrpcSecurity: certs,
+ }
+ return server
+}
+
+/*
+Start prepares the GRPC server and starts servicing requests
+*/
+func (s *GrpcServer) Start(ctx context.Context) {
+ host := strings.Join([]string{
+ s.address,
+ strconv.Itoa(int(s.port)),
+ }, ":")
+
+ lis, err := net.Listen("tcp", host)
+ if err != nil {
+ common.Logger().Fatalf("failed to listen: %v", err)
+ }
+
+ if s.secure {
+ creds, err := credentials.NewServerTLSFromFile(s.CertFile, s.KeyFile)
+ if err != nil {
+ common.Logger().Fatalf("could not load TLS keys: %s", err)
+ }
+ s.gs = grpc.NewServer(grpc.Creds(creds))
+
+ } else {
+ common.Logger().Println("In DEFAULT\n")
+ s.gs = grpc.NewServer()
+ }
+
+ // Register all required services
+ for _, service := range s.services {
+ service(s.gs)
+ }
+
+ if err := s.gs.Serve(lis); err != nil {
+ common.Logger().Fatalf("failed to serve: %v\n", err)
+ }
+}
+
+/*
+Stop servicing GRPC requests
+*/
+func (s *GrpcServer) Stop() {
+ s.gs.Stop()
+}
+
+/*
+AddService appends a generic service request function
+*/
+func (s *GrpcServer) AddService(
+ registerFunction func(*grpc.Server, interface{}),
+ handler interface{},
+) {
+ s.services = append(s.services, func(gs *grpc.Server) { registerFunction(gs, handler) })
+}
+
+/*
+AddPonSimService appends service request functions for PonSim devices
+*/
+func (s *GrpcServer) AddPonSimService(device core.PonSimInterface) {
+ s.services = append(
+ s.services,
+ func(gs *grpc.Server) {
+ voltha.RegisterPonSimServer(gs, nbi.NewPonSimHandler(device))
+ },
+ )
+}
+
+/*
+AddCommonService appends service request functions common to all PonSim devices
+*/
+func (s *GrpcServer) AddCommonService(device core.PonSimInterface) {
+ s.services = append(
+ s.services,
+ func(gs *grpc.Server) {
+ ponsim.RegisterPonSimCommonServer(gs, sbi.NewPonSimCommonHandler(device))
+ },
+ )
+}
+
+/*
+AddOltService appends service request functions specific to OLT devices
+*/
+func (s *GrpcServer) AddOltService(device core.PonSimInterface) {
+ s.services = append(
+ s.services,
+ func(gs *grpc.Server) {
+ ponsim.RegisterPonSimOltServer(
+ gs,
+ sbi.NewPonSimOltHandler(device.(*core.PonSimOltDevice)),
+ )
+ },
+ )
+}
+
+/*
+AddXPonService appends service request functions specific to XPonSim
+*/
+func (s *GrpcServer) AddXPonService() {
+ s.services = append(
+ s.services,
+ func(gs *grpc.Server) {
+ voltha.RegisterXPonSimServer(gs, nbi.NewXPonSimHandler())
+ },
+ )
+}
+
+/*
+AddBalService appends service request functions specific to BAL
+*/
+func (s *GrpcServer) AddBalService() {
+ s.services = append(
+ s.services,
+ func(gs *grpc.Server) {
+ bal.RegisterBalServer(gs, nbi.NewBalHandler())
+ },
+ )
+}
diff --git a/ponsim/v2/grpc/nbi/bal_handler.go b/ponsim/v2/grpc/nbi/bal_handler.go
new file mode 100644
index 0000000..5cbafb3
--- /dev/null
+++ b/ponsim/v2/grpc/nbi/bal_handler.go
@@ -0,0 +1,82 @@
+package nbi
+
+import (
+ "context"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/protos/go/bal"
+)
+
+// TODO: fix BAL function parameters and returns
+
+type BalHandler struct {
+}
+
+func NewBalHandler() *BalHandler {
+ var handler *BalHandler
+ handler = &BalHandler{}
+ return handler
+}
+
+func (handler *BalHandler) BalApiInit(
+ ctx context.Context,
+ request *bal.BalInit,
+) (*bal.BalErr, error) {
+ common.Logger().Info("BalApiInit Called", ctx, request)
+ return &bal.BalErr{Err: bal.BalErrno_BAL_ERR_OK}, nil
+}
+
+func (handler *BalHandler) BalApiFinish(
+ ctx context.Context,
+ request *bal.BalCfg,
+) (*bal.BalErr, error) {
+ common.Logger().Info("BalApiFinish Called", ctx, request)
+ return &bal.BalErr{Err: bal.BalErrno_BAL_ERR_OK}, nil
+}
+
+func (handler *BalHandler) BalCfgSet(
+ ctx context.Context,
+ request *bal.BalCfg,
+) (*bal.BalErr, error) {
+ common.Logger().Info("BalCfgSet Called", ctx, request)
+ return &bal.BalErr{Err: bal.BalErrno_BAL_ERR_OK}, nil
+}
+
+func (handler *BalHandler) BalCfgClear(
+ ctx context.Context,
+ request *bal.BalKey,
+) (*bal.BalErr, error) {
+ common.Logger().Info("BalCfgClear Called", ctx, request)
+ return &bal.BalErr{Err: bal.BalErrno_BAL_ERR_OK}, nil
+}
+
+func (handler *BalHandler) BalCfgGet(
+ ctx context.Context,
+ request *bal.BalKey,
+) (*bal.BalCfg, error) {
+ common.Logger().Info("BalCfgGet Called", ctx, request)
+ return &bal.BalCfg{}, nil
+}
+
+func (handler *BalHandler) BalApiReboot(
+ ctx context.Context,
+ request *bal.BalReboot,
+) (*bal.BalErr, error) {
+ common.Logger().Info("BalApiReboot Called", ctx, request)
+ return &bal.BalErr{Err: bal.BalErrno_BAL_ERR_OK}, nil
+}
+
+func (handler *BalHandler) BalApiHeartbeat(
+ ctx context.Context,
+ request *bal.BalHeartbeat,
+) (*bal.BalRebootState, error) {
+ common.Logger().Info("BalApiHeartbeat Called", ctx, request)
+ return &bal.BalRebootState{}, nil
+}
+
+func (handler *BalHandler) BalCfgStatGet(
+ ctx context.Context,
+ request *bal.BalInterfaceKey,
+) (*bal.BalInterfaceStat, error) {
+ common.Logger().Info("BalCfgStatGet Called", ctx, request)
+ return &bal.BalInterfaceStat{}, nil
+}
diff --git a/ponsim/v2/grpc/nbi/ponsim_handler.go b/ponsim/v2/grpc/nbi/ponsim_handler.go
new file mode 100644
index 0000000..763f4bc
--- /dev/null
+++ b/ponsim/v2/grpc/nbi/ponsim_handler.go
@@ -0,0 +1,327 @@
+package nbi
+
+import (
+ "context"
+ "crypto/tls"
+ "errors"
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/protos/go/voltha"
+ "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+ "google.golang.org/grpc/credentials"
+ "strconv"
+ "strings"
+)
+
+// TODO: Cleanup GRPC security config
+// TODO: Pass-in the certificate information as a structure parameter
+
+type PonSimHandler struct {
+ device core.PonSimInterface
+}
+
+/*
+NewPonSimHandler instantiates a handler for a PonSim device
+*/
+func NewPonSimHandler(device core.PonSimInterface) *PonSimHandler {
+ var handler *PonSimHandler
+ handler = &PonSimHandler{device: device}
+ return handler
+}
+
+/*
+SendFrame handles and forwards EGRESS packets (i.e. VOLTHA to OLT)
+*/
+func (handler *PonSimHandler) SendFrame(ctx context.Context, data *voltha.PonSimFrame) (*empty.Empty, error) {
+ frame := gopacket.NewPacket(data.Payload, layers.LayerTypeEthernet, gopacket.Default)
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "frame": frame.Dump(),
+ }).Info("Constructed frame")
+
+ handler.device.Forward(context.Background(), 2, frame)
+
+ out := new(empty.Empty)
+ return out, nil
+}
+
+/*
+ReceiveFrames handles a stream of INGRESS packets (i.e. OLT to VOLTHA)
+*/
+func (handler *PonSimHandler) ReceiveFrames(empty *empty.Empty, stream voltha.PonSim_ReceiveFramesServer) error {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Info("start-receiving-frames")
+
+ if _, ok := (handler.device).(*core.PonSimOltDevice); ok {
+ var data []byte
+ var ok bool
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "device": (handler.device).(*core.PonSimOltDevice),
+ }).Info("receiving-frames-from-olt-device")
+
+ for {
+ select {
+ case data, ok = <-(handler.device).(*core.PonSimOltDevice).GetOutgoing():
+ if ok {
+ frame := gopacket.NewPacket(data, layers.LayerTypeEthernet, gopacket.Default)
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "frame": frame,
+ }).Info("Received incoming data")
+
+ frameBytes := &voltha.PonSimFrame{Id: handler.device.GetAddress(), Payload: data}
+ if err := stream.Send(frameBytes); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "frame": frame,
+ "error": err,
+ }).Error("Failed to send incoming data")
+ return err
+ }
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "frame": frame,
+ }).Info("Sent incoming data")
+
+ } else {
+ return errors.New("incoming data channel has closed")
+ }
+ }
+ }
+
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Error("Not handling an OLT device")
+ }
+
+ return nil
+}
+
+/*
+GetDeviceInfo returns information of a PonSim device (OLT or ONU)
+*/
+func (handler *PonSimHandler) GetDeviceInfo(
+ ctx context.Context,
+ empty *empty.Empty,
+) (*voltha.PonSimDeviceInfo, error) {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Info("Getting device information")
+
+ var out *voltha.PonSimDeviceInfo
+
+ // Check which device type we're currently handling
+ if _, ok := (handler.device).(*core.PonSimOltDevice); ok {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Debug("Handling OLT device")
+ keys := make([]int32, 0, len((handler.device).(*core.PonSimOltDevice).GetOnus()))
+ for k := range (handler.device).(*core.PonSimOltDevice).GetOnus() {
+ keys = append(keys, k)
+ }
+ out = &voltha.PonSimDeviceInfo{NniPort: 0, UniPorts: []int32(keys)}
+
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Debug("Handling ONU/OTHER device")
+
+ out = &voltha.PonSimDeviceInfo{}
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "result": out,
+ }).Info("Device information")
+
+ return out, nil
+}
+
+/*
+UpdateFlowTable populates and cleans up the flows for a PonSim device
+*/
+func (handler *PonSimHandler) UpdateFlowTable(
+ ctx context.Context,
+ table *voltha.FlowTable,
+) (*empty.Empty, error) {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "table": table,
+ }).Info("Updating flows")
+
+ if _, ok := (handler.device).(*core.PonSimOltDevice); ok {
+ if table.Port == 0 {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "port": table.Port,
+ }).Debug("Updating OLT flows")
+
+ if err := (handler.device).(*core.PonSimOltDevice).InstallFlows(ctx, table.Flows); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "error": err.Error(),
+ "flows": table.Flows,
+ }).Error("Problem updating flows on OLT")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Debug("Updated OLT flows")
+ }
+
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "port": table.Port,
+ }).Debug("Updating ONU flows")
+
+ if child, ok := (handler.device).(*core.PonSimOltDevice).GetOnus()[table.Port]; ok {
+ // TODO: make it secure
+ ta := credentials.NewTLS(&tls.Config{
+ InsecureSkipVerify: true,
+ })
+
+ host := strings.Join([]string{
+ child.Device.Address,
+ strconv.Itoa(int(child.Device.Port)),
+ }, ":")
+
+ conn, err := grpc.Dial(
+ host,
+ grpc.WithTransportCredentials(ta),
+ )
+ if err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "error": err.Error(),
+ }).Error("GRPC Connection problem")
+ }
+ defer conn.Close()
+ client := voltha.NewPonSimClient(conn)
+
+ if _, err = client.UpdateFlowTable(ctx, table); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "host": host,
+ "error": err.Error(),
+ }).Error("Problem forwarding update request to ONU")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "port": table.Port,
+ }).Warn("Unable to find ONU")
+ }
+
+ }
+ } else if _, ok := (handler.device).(*core.PonSimOnuDevice); ok {
+ if err := (handler.device).(*core.PonSimOnuDevice).InstallFlows(ctx, table.Flows); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "error": err.Error(),
+ "flows": table.Flows,
+ }).Error("Problem updating flows on ONU")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Debug("Updated ONU flows")
+ }
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "port": table.Port,
+ }).Warn("Unknown device")
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "table": table,
+ }).Info("Updated flows")
+
+ out := new(empty.Empty)
+ return out, nil
+}
+
+/*
+GetStats retrieves statistics for a PonSim device
+*/
+func (handler *PonSimHandler) GetStats(
+ ctx context.Context,
+ empty *empty.Empty,
+) (*voltha.PonSimMetrics, error) {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Info("Retrieving stats")
+
+ var metrics *voltha.PonSimMetrics = new(voltha.PonSimMetrics)
+
+ if olt, ok := (handler.device).(*core.PonSimOltDevice); ok {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "olt": olt,
+ }).Debug("Retrieving stats for OLT")
+
+ // Get stats for current device
+
+ // Loop through each onus to get stats from those as well?
+ // send grpc request to each onu
+ for _, child := range (handler.device).(*core.PonSimOltDevice).GetOnus() {
+ // TODO: make it secure
+ ta := credentials.NewTLS(&tls.Config{
+ InsecureSkipVerify: true,
+ })
+
+ host := strings.Join([]string{child.Device.Address, strconv.Itoa(int(child.Device.Port))}, ":")
+ conn, err := grpc.Dial(
+ host,
+ grpc.WithTransportCredentials(ta),
+ )
+ if err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "error": err.Error(),
+ }).Error("GRPC Connection problem")
+ }
+ defer conn.Close()
+ client := voltha.NewPonSimClient(conn)
+
+ if _, err = client.GetStats(ctx, empty); err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "host": host,
+ "error": err.Error(),
+ }).Error("Problem forwarding stats request to ONU")
+ }
+ }
+ metrics = (handler.device).(*core.PonSimOltDevice).Counter.MakeProto()
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "metrics": metrics,
+ }).Debug("OLT Metrics")
+
+ } else if onu, ok := (handler.device).(*core.PonSimOnuDevice); ok {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ "onu": onu,
+ }).Debug("Retrieving stats for ONU")
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Warn("Unknown device")
+ }
+
+ common.Logger().WithFields(logrus.Fields{
+ "handler": handler,
+ }).Info("Retrieved stats")
+
+ return metrics, nil
+}
diff --git a/ponsim/v2/grpc/nbi/xponsim_handler.go b/ponsim/v2/grpc/nbi/xponsim_handler.go
new file mode 100644
index 0000000..fbc63eb
--- /dev/null
+++ b/ponsim/v2/grpc/nbi/xponsim_handler.go
@@ -0,0 +1,124 @@
+package nbi
+
+import (
+ "context"
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/protos/go/voltha"
+)
+
+type XPonSimHandler struct {
+ device *core.XPonSimDevice
+}
+
+func NewXPonSimHandler() *XPonSimHandler {
+ var handler *XPonSimHandler
+ handler = &XPonSimHandler{}
+ return handler
+}
+
+func (handler *XPonSimHandler) CreateInterface(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.CreateInterface(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) UpdateInterface(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.UpdateInterface(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) RemoveInterface(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.RemoveInterface(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) CreateTcont(
+ ctx context.Context,
+ config *voltha.TcontInterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.CreateTcont(ctx, config.TcontsConfigData, config.TrafficDescriptorProfileConfigData)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) UpdateTcont(
+ ctx context.Context,
+ config *voltha.TcontInterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.UpdateTcont(ctx, config.TcontsConfigData, config.TrafficDescriptorProfileConfigData)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) RemoveTcont(
+ ctx context.Context,
+ config *voltha.TcontInterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.RemoveTcont(ctx, config.TcontsConfigData, config.TrafficDescriptorProfileConfigData)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) CreateGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.CreateGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) UpdateGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.UpdateGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) RemoveGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.RemoveGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) CreateMulticastGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.CreateMulticastGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) UpdateMulticastGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.UpdateMulticastGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) RemoveMulticastGemport(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.RemoveMulticastGemport(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) CreateMulticastDistributionSet(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.CreateMulticastDistributionSet(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) UpdateMulticastDistributionSet(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.UpdateMulticastDistributionSet(ctx, config)
+ return &empty.Empty{}, nil
+}
+func (handler *XPonSimHandler) RemoveMulticastDistributionSet(
+ ctx context.Context,
+ config *voltha.InterfaceConfig,
+) (*empty.Empty, error) {
+ handler.device.RemoveMulticastDistributionSet(ctx, config)
+ return &empty.Empty{}, nil
+}
diff --git a/ponsim/v2/grpc/sbi/common_handler.go b/ponsim/v2/grpc/sbi/common_handler.go
new file mode 100644
index 0000000..4c59af6
--- /dev/null
+++ b/ponsim/v2/grpc/sbi/common_handler.go
@@ -0,0 +1,72 @@
+package sbi
+
+import (
+ "context"
+ "github.com/golang/protobuf/ptypes/empty"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/protos/go/ponsim"
+ "github.com/sirupsen/logrus"
+ "io"
+)
+
+type PonSimCommonHandler struct {
+ device core.PonSimInterface
+}
+
+/*
+NewPonSimCommonHandler instantiates a handler for common GRPC servicing methods
+*/
+func NewPonSimCommonHandler(device core.PonSimInterface) *PonSimCommonHandler {
+ var handler *PonSimCommonHandler
+
+ handler = &PonSimCommonHandler{device: device}
+
+ return handler
+}
+
+/*
+ProcessData handles and forwards streaming INGRESS/EGRESS packets
+*/
+func (h *PonSimCommonHandler) ProcessData(stream ponsim.PonSimCommon_ProcessDataServer) error {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Debug("Processing data")
+
+ var err error
+ var data *ponsim.IncomingData
+
+ for {
+
+ if data, err = stream.Recv(); err == io.EOF {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Warn("Streaming channel was closed")
+ return stream.SendAndClose(&empty.Empty{})
+ } else if err != nil {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ "error": err.Error(),
+ }).Warn("Error occurred with stream")
+ return err
+ }
+
+ frame := gopacket.NewPacket(data.Payload, layers.LayerTypeEthernet, gopacket.Default)
+
+ h.device.Forward(
+ context.Background(),
+ int(data.Port),
+ frame,
+ )
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ "frame": frame,
+ "port": data.Port,
+ }).Debug("Retrieved and forwarded packet")
+
+ }
+
+ return nil
+}
diff --git a/ponsim/v2/grpc/sbi/olt_handler.go b/ponsim/v2/grpc/sbi/olt_handler.go
new file mode 100644
index 0000000..bb058ca
--- /dev/null
+++ b/ponsim/v2/grpc/sbi/olt_handler.go
@@ -0,0 +1,62 @@
+package sbi
+
+import (
+ "context"
+ "github.com/google/uuid"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/protos/go/ponsim"
+ "github.com/sirupsen/logrus"
+)
+
+type PonSimOltHandler struct {
+ olt *core.PonSimOltDevice
+}
+
+func NewPonSimOltHandler(olt *core.PonSimOltDevice) *PonSimOltHandler {
+ var handler *PonSimOltHandler
+
+ handler = &PonSimOltHandler{olt: olt}
+
+ return handler
+}
+
+func (h *PonSimOltHandler) Register(
+ ctx context.Context,
+ request *ponsim.RegistrationRequest,
+) (*ponsim.RegistrationReply, error) {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ }).Info("Registering device")
+
+ onu := &core.PonSimOnuDevice{
+ PonSimDevice: core.PonSimDevice{
+ Address: request.Address, Port: request.Port, //GrpcSecurity: h.olt.GrpcSecurity,
+ }}
+
+ if assignedPort, err := h.olt.AddOnu(onu); assignedPort == -1 || err != nil {
+ return &ponsim.RegistrationReply{
+ Id: uuid.New().String(),
+ Status: ponsim.RegistrationReply_FAILED,
+ StatusMessage: "Failed to register ONU",
+ ParentAddress: common.GetInterfaceIP(h.olt.ExternalIf),
+ ParentPort: h.olt.Port,
+ AssignedPort: assignedPort,
+ }, err
+ } else {
+ common.Logger().WithFields(logrus.Fields{
+ "handler": h,
+ "onus": h.olt.GetOnus(),
+ }).Debug("ONU Added")
+
+ return &ponsim.RegistrationReply{
+ Id: uuid.New().String(),
+ Status: ponsim.RegistrationReply_REGISTERED,
+ StatusMessage: "Successfully registered ONU",
+ ParentAddress: common.GetInterfaceIP(h.olt.ExternalIf),
+ ParentPort: h.olt.Port,
+ AssignedPort: assignedPort,
+ }, nil
+
+ }
+}
diff --git a/ponsim/v2/ponsim.go b/ponsim/v2/ponsim.go
new file mode 100644
index 0000000..fad0972
--- /dev/null
+++ b/ponsim/v2/ponsim.go
@@ -0,0 +1,289 @@
+package main
+
+import (
+ "context"
+ "flag"
+ "fmt"
+ "github.com/opencord/voltha/ponsim/v2/common"
+ "github.com/opencord/voltha/ponsim/v2/core"
+ "github.com/opencord/voltha/ponsim/v2/grpc"
+ "log"
+ "os"
+ "os/signal"
+ "path"
+)
+
+// TODO: Cleanup logs
+
+const (
+ default_name = "PON"
+ default_grpc_port = 50060
+ default_grpc_addr = ""
+ default_device_type = "OLT"
+ default_api_type = "PONSIM"
+ default_internal_if = "eth0"
+ default_external_if = "eth1"
+ default_onus = 1
+ default_alarm_sim = false
+ default_alarm_freq = 60
+ default_quiet = false
+ default_verbose = false
+ default_no_banner = false
+ default_parent_addr = "olt"
+ default_parent_port = 50060
+ default_vcore_endpoint = "vcore"
+ default_fluentd_host = ""
+
+ default_snapshot_len = 65535
+ default_promiscuous = false
+
+ default_voltha_key = "pki/voltha.key"
+ default_voltha_cert = "pki/voltha.crt"
+ default_voltha_ca = "pki/voltha-CA.pem"
+)
+
+var (
+ voltha_base = os.Getenv("VOLTHA_BASE")
+ certs *grpc.GrpcSecurity
+
+ name string = default_name + "_" + device_type
+ grpc_port int = default_grpc_port
+ grpc_addr string = default_grpc_addr
+ device_type string = default_device_type
+ api_type string = default_api_type
+ internal_if string = default_internal_if
+ external_if string = default_external_if
+ onus int = default_onus
+ alarm_sim bool = default_alarm_sim
+ alarm_freq int = default_alarm_freq
+ quiet bool = default_quiet
+ verbose bool = default_verbose
+ no_banner bool = default_no_banner
+ voltha_key string = default_voltha_key
+ voltha_cert string = default_voltha_cert
+ voltha_ca string = default_voltha_ca
+ parent_addr string = default_parent_addr
+ parent_port int = default_parent_port
+ vcore_endpoint string = default_vcore_endpoint
+ fluentd_host string = default_fluentd_host
+
+ snapshot_len int32 = default_snapshot_len
+ promiscuous bool = default_promiscuous
+)
+
+func init() {
+ parseArgs()
+
+ // Enable fluentd support
+ if fluentd_host != "" {
+ common.Logger().SetFluentd(fluentd_host)
+ }
+
+ // Print banner unless no_banner is specified
+ if !no_banner {
+ printBanner()
+ }
+}
+
+func parseArgs() {
+ var help string
+
+ help = fmt.Sprintf("Name of the PON device")
+ flag.StringVar(&grpc_addr, "name", default_name, help)
+
+ help = fmt.Sprintf("Address used to establish GRPC server connection")
+ flag.StringVar(&grpc_addr, "grpc_addr", default_grpc_addr, help)
+
+ help = fmt.Sprintf("Port used to establish GRPC server connection")
+ flag.IntVar(&grpc_port, "grpc_port", default_grpc_port, help)
+
+ help = fmt.Sprintf("Type of device to simulate (OLT or ONU)")
+ flag.StringVar(&device_type, "device_type", default_device_type, help)
+
+ help = fmt.Sprintf("Type of API used to communicate with devices (PONSIM or BAL)")
+ flag.StringVar(&api_type, "api_type", default_api_type, help)
+
+ help = fmt.Sprintf("Internal Communication Interface for read/write network traffic")
+ flag.StringVar(&internal_if, "internal_if", default_internal_if, help)
+
+ help = fmt.Sprintf("External Communication Interface for read/write network traffic")
+ flag.StringVar(&external_if, "external_if", default_external_if, help)
+
+ help = fmt.Sprintf("Enable promiscuous mode on network interfaces")
+ flag.BoolVar(&promiscuous, "promiscuous", default_promiscuous, help)
+
+ help = fmt.Sprintf("Number of ONUs to simulate")
+ flag.IntVar(&onus, "onus", default_onus, help)
+
+ help = fmt.Sprintf("Suppress debug and info logs")
+ flag.BoolVar(&quiet, "quiet", default_quiet, help)
+
+ help = fmt.Sprintf("Enable verbose logging")
+ flag.BoolVar(&verbose, "verbose", default_verbose, help)
+
+ help = fmt.Sprintf("Omit startup banner log lines")
+ flag.BoolVar(&no_banner, "no_banner", default_no_banner, help)
+
+ help = fmt.Sprintf("Enable generation of simulated alarms")
+ flag.BoolVar(&alarm_sim, "alarm_sim", default_alarm_sim, help)
+
+ help = fmt.Sprintf("Frequency of simulated alarms (in seconds)")
+ flag.IntVar(&alarm_freq, "alarm_freq", default_alarm_freq, help)
+
+ help = fmt.Sprintf("Address of OLT to connect to")
+ flag.StringVar(&parent_addr, "parent_addr", default_parent_addr, help)
+
+ help = fmt.Sprintf("Port of OLT to connect to")
+ flag.IntVar(&parent_port, "parent_port", default_parent_port, help)
+
+ help = fmt.Sprintf("Voltha core endpoint address")
+ flag.StringVar(&vcore_endpoint, "vcore_endpoint", default_vcore_endpoint, help)
+
+ help = fmt.Sprintf("Fluentd host address")
+ flag.StringVar(&fluentd_host, "fluentd", default_fluentd_host, help)
+
+ flag.Parse()
+}
+
+func printBanner() {
+ log.Println(" ____ ____ _ _______ ______ ___")
+ log.Println(" / __ \\/ __ \\/ | / / ___// _/ |/ /")
+ log.Println(" / /_/ / / / / |/ /\\__ \\ / // /|_/ / ")
+ log.Println(" / ____/ /_/ / /| /___/ // // / / / ")
+ log.Println("/_/ \\____/_/ |_//____/___/_/ /_/ ")
+
+ switch device_type {
+ case "OLT":
+ printOltBanner()
+ case "ONU":
+ printOnuBanner()
+ }
+
+ log.Println("(to stop: press Ctrl-C)")
+}
+func printOltBanner() {
+ log.Println(" ____ __ ______")
+ log.Println(" / __ \\/ / /_ __/")
+ log.Println(" / / / / / / / ")
+ log.Println("/ /_/ / /___/ / ")
+ log.Println("\\____/_____/_/ ")
+}
+func printOnuBanner() {
+ log.Println(" ____ _ ____ __")
+ log.Println(" / __ \\/ | / / / / /")
+ log.Println(" / / / / |/ / / / / ")
+ log.Println("/ /_/ / /| / /_/ / ")
+ log.Println("\\____/_/ |_/\\____/ ")
+}
+
+/*
+-----------------------------------------------------------------
+*/
+type PonSimService struct {
+ device core.PonSimInterface
+ server *grpc.GrpcServer
+}
+
+func (s *PonSimService) Start(ctx context.Context) {
+ // GRPC server needs to be secure.
+ // Otherwise communication between adapter and simulator does not occur
+ s.server = grpc.NewGrpcServer(s.device.GetAddress(), s.device.GetPort(), certs, true)
+
+ // Add GRPC services
+ s.server.AddCommonService(s.device)
+ s.server.AddPonSimService(s.device)
+
+ // Add OLT specific services
+ if device_type == core.OLT.String() {
+ s.server.AddOltService(s.device)
+ }
+
+ // Add XPON services unless using BAL
+ if api_type == core.PONSIM.String() {
+ s.server.AddXPonService()
+ } else {
+ s.server.AddBalService()
+ }
+
+ // Start the GRPC server
+ go s.server.Start(ctx)
+
+ // Start the PON device
+ go s.device.Start(ctx)
+}
+
+func (s *PonSimService) Stop(ctx context.Context) {
+ // Stop PON device
+ s.device.Stop(ctx)
+
+ // Stop GRPC server
+ s.server.Stop()
+}
+
+func main() {
+ var device core.PonSimInterface
+
+ // Init based on type of device
+ // Construct OLT/ONU object and pass it down
+ certs = &grpc.GrpcSecurity{
+ CertFile: path.Join(voltha_base, voltha_cert),
+ KeyFile: path.Join(voltha_base, voltha_key),
+ CaFile: path.Join(voltha_base, voltha_ca),
+ }
+
+ // Initialize device with common parameters
+ pon := core.PonSimDevice{
+ Name: name,
+ ExternalIf: external_if,
+ InternalIf: internal_if,
+ Promiscuous: promiscuous,
+ SnapshotLen: snapshot_len,
+ Address: grpc_addr,
+ Port: int32(grpc_port),
+ AlarmsOn: alarm_sim,
+ AlarmsFreq: alarm_freq,
+ Counter: core.NewPonSimMetricCounter(name),
+
+ // TODO: pass certificates
+ //GrpcSecurity: certs,
+ }
+
+ switch device_type {
+ case core.OLT.String():
+ device = core.NewPonSimOltDevice(pon)
+ device.(*core.PonSimOltDevice).MaxOnuCount = onus
+ device.(*core.PonSimOltDevice).VCoreEndpoint = vcore_endpoint
+
+ case core.ONU.String():
+ device = core.NewPonSimOnuDevice(pon)
+ device.(*core.PonSimOnuDevice).ParentAddress = parent_addr
+ device.(*core.PonSimOnuDevice).ParentPort = int32(parent_port)
+
+ default:
+ log.Println("Unknown device type")
+ }
+
+ ps := PonSimService{device: device}
+
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+
+ ps.Start(ctx)
+
+ signals := make(chan os.Signal, 1)
+ signal.Notify(signals, os.Interrupt)
+
+ doneCh := make(chan struct{})
+
+ go func() {
+ for {
+ select {
+ case <-signals:
+ log.Println("Interrupt was detected")
+ doneCh <- struct{}{}
+ }
+ }
+ }()
+
+ <-doneCh
+}
diff --git a/ponsim/v2/protos/ponsim_common.proto b/ponsim/v2/protos/ponsim_common.proto
new file mode 100644
index 0000000..42f5994
--- /dev/null
+++ b/ponsim/v2/protos/ponsim_common.proto
@@ -0,0 +1,19 @@
+syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/ponsim";
+
+package ponsim;
+
+import "google/protobuf/empty.proto";
+
+service PonSimCommon {
+ rpc ProcessData (stream IncomingData) returns (google.protobuf.Empty) {}
+}
+
+message IncomingData {
+ string id = 1;
+ string address = 2;
+ int32 port = 3;
+ bytes payload = 4;
+
+}
\ No newline at end of file
diff --git a/ponsim/v2/protos/ponsim_olt.proto b/ponsim/v2/protos/ponsim_olt.proto
new file mode 100644
index 0000000..82214a7
--- /dev/null
+++ b/ponsim/v2/protos/ponsim_olt.proto
@@ -0,0 +1,32 @@
+syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/ponsim";
+
+package ponsim;
+
+service PonSimOlt {
+ rpc Register (RegistrationRequest) returns (RegistrationReply) {}
+}
+
+message RegistrationRequest {
+ string id = 1;
+ string address = 2;
+ int32 port = 3;
+}
+
+message RegistrationReply {
+ string id = 1;
+
+ enum Status {
+ REGISTERED = 0;
+ FAILED = 1;
+ UNAVAILABLE = 2;
+ }
+
+ Status status = 2;
+ string status_message = 3;
+
+ string parent_address = 4;
+ int32 parent_port = 5;
+ int32 assigned_port = 6;
+}
diff --git a/ponsim/v2/scripts/build_protos.sh b/ponsim/v2/scripts/build_protos.sh
new file mode 100644
index 0000000..daccdbe
--- /dev/null
+++ b/ponsim/v2/scripts/build_protos.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+export SRC_DIR="$1"
+
+echo $SRC_DIR
+
+export MAPS=Mgoogle/protobuf/descriptor.proto=github.com/golang/protobuf/protoc-gen-go/descriptor
+export INCS="\
+ -I $SRC_DIR \
+ -I $GOPATH/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis"
+
+export VOLTHA_PB="\
+ $SRC_DIR/adapter.proto \
+ $SRC_DIR/device.proto \
+ $SRC_DIR/events.proto \
+ $SRC_DIR/health.proto \
+ $SRC_DIR/logical_device.proto \
+ $SRC_DIR/ponsim.proto \
+ $SRC_DIR/voltha.proto"
+
+export COMMON_PB="\
+ $SRC_DIR/common.proto \
+ $SRC_DIR/meta.proto \
+ $SRC_DIR/yang_options.proto"
+
+export PONSIM_PB="$SRC_DIR/ponsim_common.proto $SRC_DIR/ponsim_olt.proto"
+export SCHEMA_PB="$SRC_DIR/schema.proto"
+export IETF_PB="$SRC_DIR/ietf_interfaces.proto"
+export OF_PB="$SRC_DIR/openflow_13.proto"
+export BAL_PB="$SRC_DIR/bal*.proto"
+export BBF_PB="$SRC_DIR/bbf*.proto"
+
+export PB_VARS="\
+ VOLTHA_PB \
+ COMMON_PB \
+ PONSIM_PB \
+ SCHEMA_PB \
+ IETF_PB \
+ OF_PB \
+ BAL_PB \
+ BBF_PB"
+
+for pb_var in $PB_VARS
+do
+ pbs="$(eval echo \$$pb_var)"
+ echo "Compiling $pbs"
+ protoc --go_out=$MAPS,plugins=grpc:$GOPATH/src $INCS $pbs
+done
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal.proto b/voltha/adapters/asfvolt16_olt/protos/bal.proto
index 8d84b73..a72f274 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
//import "google/protobuf/empty.proto";
import "bal_obj.proto";
import "bal_model_types.proto";
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_errno.proto b/voltha/adapters/asfvolt16_olt/protos/bal_errno.proto
index c66133a..1139067 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_errno.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_errno.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
/** Error codes */
enum BalErrno
{
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto b/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
index 477c663..a6255ae 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_indications.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
import "bal_model_ids.proto";
import "bal_model_types.proto";
import "bal.proto";
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_model_ids.proto b/voltha/adapters/asfvolt16_olt/protos/bal_model_ids.proto
index 3dbbcbe..a3a0259 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_model_ids.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_model_ids.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
/** Identifiers for all properties contained in the access_terminal_cfg group.
*/
enum BalAccessTerminalCfgId
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto b/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
index 7c97858..5e81d59 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_model_types.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
import "bal_obj.proto";
/** action ID.
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_msg_type.proto b/voltha/adapters/asfvolt16_olt/protos/bal_msg_type.proto
index e39275a..28494f5 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_msg_type.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_msg_type.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
/** The BAL message subtype
*/
enum BalMsgType
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_obj.proto b/voltha/adapters/asfvolt16_olt/protos/bal_obj.proto
index 9d4b169..1e5b28c 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_obj.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_obj.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
import "bal_msg_type.proto";
import "bal_osmsg.proto";
import "bal_model_ids.proto";
diff --git a/voltha/adapters/asfvolt16_olt/protos/bal_osmsg.proto b/voltha/adapters/asfvolt16_olt/protos/bal_osmsg.proto
index dd246ec..16dcb5e 100644
--- a/voltha/adapters/asfvolt16_olt/protos/bal_osmsg.proto
+++ b/voltha/adapters/asfvolt16_olt/protos/bal_osmsg.proto
@@ -16,6 +16,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/bal";
+
/* BAL subsystem */
enum BalSubsystem
{
diff --git a/voltha/adapters/ponsim_olt/ponsim_olt.py b/voltha/adapters/ponsim_olt/ponsim_olt.py
index bb5b533..82c6602 100644
--- a/voltha/adapters/ponsim_olt/ponsim_olt.py
+++ b/voltha/adapters/ponsim_olt/ponsim_olt.py
@@ -26,7 +26,9 @@
import structlog
from scapy.layers.l2 import Ether, Dot1Q
from scapy.layers.inet import Raw
+from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
+from grpc._channel import _Rendezvous
from common.frameio.frameio import BpfProgramFilter, hexify
from common.utils.asleep import asleep
@@ -39,14 +41,13 @@
from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
from google.protobuf.empty_pb2 import Empty
-
from voltha.protos.logical_device_pb2 import LogicalPort, LogicalDevice
from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPF_FIBER, \
OFPPF_1GB_FD, \
OFPC_GROUP_STATS, OFPC_PORT_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS, \
ofp_switch_features, ofp_desc
from voltha.protos.openflow_13_pb2 import ofp_port
-from voltha.protos.ponsim_pb2 import FlowTable
+from voltha.protos.ponsim_pb2 import FlowTable, PonSimFrame
from voltha.registry import registry
from voltha.protos.bbf_fiber_base_pb2 import \
@@ -58,6 +59,7 @@
from voltha.protos.bbf_fiber_gemport_body_pb2 import GemportsConfigData
from voltha.protos.bbf_fiber_multicast_gemport_body_pb2 import \
MulticastGemportsConfigData
+
from voltha.protos.bbf_fiber_multicast_distribution_set_body_pb2 import \
MulticastDistributionSetData
@@ -154,6 +156,10 @@
self.lc = LoopingCall(callback, self.device.id, prefix)
self.lc.start(interval=self.default_freq / 10)
+ def stop_collector(self):
+ log.info("stopping-pm-collection", device_name=self.name,
+ device_id=self.device.id)
+ self.lc.stop()
class AdapterAlarms:
def __init__(self, adapter, device):
@@ -346,8 +352,10 @@
self.nni_port = None
self.ofp_port_no = None
self.interface = registry('main').get_args().interface
+ self.ponsim_comm = registry('main').get_args().ponsim_comm
self.pm_metrics = None
self.alarms = None
+ self.frames = None
def __del__(self):
if self.io_port is not None:
@@ -379,6 +387,21 @@
return self.channel
+ def close_channel(self):
+ if self.channel is None:
+ self.log.info('grpc-channel-already-closed')
+ return
+ else:
+ if self.frames is not None:
+ self.frames.cancel()
+ self.frames = None
+ self.log.info('cancelled-grpc-frame-stream')
+
+ self.channel.unsubscribe(lambda *args: None)
+ self.channel = None
+
+ self.log.info('grpc-channel-closed')
+
def _get_nni_port(self):
ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_NNI)
if ports:
@@ -499,11 +522,16 @@
vlan=vlan_id
)
- # finally, open the frameio port to receive in-band packet_in messages
- self.log.info('registering-frameio')
- self.io_port = registry('frameio').open_port(
- self.interface, self.rcv_io, is_inband_frame)
- self.log.info('registered-frameio')
+ if self.ponsim_comm == 'grpc':
+ self.log.info('starting-frame-grpc-stream')
+ reactor.callInThread(self.rcv_grpc)
+ self.log.info('started-frame-grpc-stream')
+ else:
+ # finally, open the frameio port to receive in-band packet_in messages
+ self.log.info('registering-frameio')
+ self.io_port = registry('frameio').open_port(
+ self.interface, self.rcv_io, is_inband_frame)
+ self.log.info('registered-frameio')
# Start collecting stats from the device after a brief pause
self.start_kpi_collection(device.id)
@@ -557,21 +585,24 @@
# Reconcile child devices
self.adapter_agent.reconcile_child_devices(device.id)
- # finally, open the frameio port to receive in-band packet_in messages
- self.io_port = registry('frameio').open_port(
- self.interface, self.rcv_io, is_inband_frame)
+ if self.ponsim_comm == 'grpc':
+ reactor.callInThread(self.rcv_grpc)
+ else:
+ # finally, open the frameio port to receive in-band packet_in messages
+ self.io_port = registry('frameio').open_port(
+ self.interface, self.rcv_io, is_inband_frame)
# Start collecting stats from the device after a brief pause
self.start_kpi_collection(device.id)
self.log.info('reconciling-OLT-device-ends')
- def rcv_io(self, port, frame):
- self.log.info('received', iface_name=port.iface_name,
- frame_len=len(frame))
+ def _rcv_frame(self, frame):
pkt = Ether(frame)
+
if pkt.haslayer(Dot1Q):
outer_shim = pkt.getlayer(Dot1Q)
+
if isinstance(outer_shim.payload, Dot1Q):
inner_shim = outer_shim.payload
cvid = inner_shim.vlan
@@ -591,6 +622,33 @@
raw_data = json.loads(pkt.getlayer(Raw).load)
self.alarms.send_alarm(self, raw_data)
+ @inlineCallbacks
+ def rcv_grpc(self):
+ """
+ This call establishes a GRPC stream to receive frames.
+ """
+ stub = ponsim_pb2.PonSimStub(self.get_channel())
+
+ # Attempt to establish a grpc stream with the remote ponsim service
+ self.frames = stub.ReceiveFrames(Empty())
+
+ self.log.info('start-receiving-grpc-frames')
+
+ try:
+ for frame in self.frames:
+ self.log.info('received-grpc-frame', frame_len=len(frame.payload))
+ self._rcv_frame(frame.payload)
+
+ except _Rendezvous, e:
+ log.warn('grpc-connection-lost',message=e.message)
+
+ self.log.info('stopped-receiving-grpc-frames')
+
+ def rcv_io(self, port, frame):
+ self.log.info('received-io-frame', iface_name=port.iface_name,
+ frame_len=len(frame))
+ self._rcv_frame(frame)
+
def update_flow_table(self, flows):
stub = ponsim_pb2.PonSimStub(self.get_channel())
self.log.info('pushing-olt-flow-table')
@@ -623,7 +681,16 @@
Dot1Q(vlan=egress_port, type=pkt.type) /
pkt.payload
)
- self.io_port.send(str(out_pkt))
+
+ if self.ponsim_comm == 'grpc':
+ # send over grpc stream
+ stub = ponsim_pb2.PonSimStub(self.get_channel())
+ frame = PonSimFrame(id=self.device_id, payload=str(out_pkt))
+ stub.SendFrame(frame)
+ else:
+ # send over frameio
+ self.io_port.send(str(out_pkt))
+
@inlineCallbacks
def reboot(self):
@@ -673,6 +740,8 @@
def disable(self):
self.log.info('disabling', device_id=self.device_id)
+ self.stop_kpi_collection()
+
# Get the latest device reference
device = self.adapter_agent.get_device(self.device_id)
@@ -696,8 +765,13 @@
# Set all ports to disabled
self.adapter_agent.disable_all_ports(self.device_id)
- # close the frameio port
- registry('frameio').close_port(self.io_port)
+ self.close_channel()
+ self.log.info('disabled-grpc-channel')
+
+ if self.ponsim_comm == 'frameio':
+ # close the frameio port
+ registry('frameio').close_port(self.io_port)
+ self.log.info('disabled-frameio-port')
# Update the logice device mapping
if self.logical_device_id in \
@@ -788,9 +862,15 @@
self.adapter_agent.update_child_devices_state(device.id,
admin_state=AdminState.ENABLED)
- # finally, open the frameio port to receive in-band packet_in messages
- self.io_port = registry('frameio').open_port(
- self.interface, self.rcv_io, is_inband_frame)
+ if self.ponsim_comm == 'grpc':
+ # establish frame grpc-stream
+ reactor.callInThread(self.rcv_grpc)
+ else:
+ # finally, open the frameio port to receive in-band packet_in messages
+ self.io_port = registry('frameio').open_port(
+ self.interface, self.rcv_io, is_inband_frame)
+
+ self.start_kpi_collection(device.id)
self.log.info('re-enabled', device_id=device.id)
@@ -800,6 +880,14 @@
# Remove all child devices
self.adapter_agent.delete_all_child_devices(self.device_id)
+ self.close_channel()
+ self.log.info('disabled-grpc-channel')
+
+ if self.ponsim_comm == 'frameio':
+ # close the frameio port
+ registry('frameio').close_port(self.io_port)
+ self.log.info('disabled-frameio-port')
+
# TODO:
# 1) Remove all flows from the device
# 2) Remove the device from ponsim
@@ -839,6 +927,10 @@
self.pm_metrics.start_collector(_collect)
+ def stop_kpi_collection(self):
+ self.pm_metrics.stop_collector()
+
+
def get_interface_config(self, data):
interfaceConfig = InterfaceConfig()
if isinstance(data, ChannelgroupConfig):
diff --git a/voltha/main.py b/voltha/main.py
index de2acfc..5001aa1 100755
--- a/voltha/main.py
+++ b/voltha/main.py
@@ -68,6 +68,7 @@
kafka=os.environ.get('KAFKA', 'localhost:9092'),
manhole_port=os.environ.get('MANHOLE_PORT', 12222),
backend=os.environ.get('BACKEND', 'none'),
+ ponsim_comm=os.environ.get('PONSIM_COMM', 'frameio')
)
@@ -225,6 +226,13 @@
choices=['none', 'consul', 'etcd'],
help=_help)
+ _help = "Communication mechanism to use with PON simulator"
+ parser.add_argument('-S', '--ponsim-comm',
+ dest='ponsim_comm',
+ default=defs['ponsim_comm'],
+ choices=['frameio', 'grpc'],
+ help=_help)
+
args = parser.parse_args()
# post-processing
diff --git a/voltha/protos/adapter.proto b/voltha/protos/adapter.proto
index 4af54c3..0123418 100644
--- a/voltha/protos/adapter.proto
+++ b/voltha/protos/adapter.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "google/protobuf/any.proto";
diff --git a/voltha/protos/bbf_fiber.proto b/voltha/protos/bbf_fiber.proto
index 65a3f78..c9ae26d 100644
--- a/voltha/protos/bbf_fiber.proto
+++ b/voltha/protos/bbf_fiber.proto
@@ -3,6 +3,9 @@
*/
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "bbf_fiber_base.proto";
import "bbf_fiber_wavelength_profile_body.proto";
diff --git a/voltha/protos/bbf_fiber_base.proto b/voltha/protos/bbf_fiber_base.proto
index b7f7689..668a82a 100644
--- a/voltha/protos/bbf_fiber_base.proto
+++ b/voltha/protos/bbf_fiber_base.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import public "meta.proto";
diff --git a/voltha/protos/bbf_fiber_channelgroup_body.proto b/voltha/protos/bbf_fiber_channelgroup_body.proto
index 400c2f2..425ea6f 100644
--- a/voltha/protos/bbf_fiber_channelgroup_body.proto
+++ b/voltha/protos/bbf_fiber_channelgroup_body.proto
@@ -1,6 +1,9 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
-import public "meta.proto";
+import "meta.proto";
import "bbf_fiber_types.proto";
message ChannelgroupConfigData {
diff --git a/voltha/protos/bbf_fiber_channelpair_body.proto b/voltha/protos/bbf_fiber_channelpair_body.proto
index 231cd85..b26bc71 100644
--- a/voltha/protos/bbf_fiber_channelpair_body.proto
+++ b/voltha/protos/bbf_fiber_channelpair_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "bbf_fiber_types.proto";
diff --git a/voltha/protos/bbf_fiber_channelpartition_body.proto b/voltha/protos/bbf_fiber_channelpartition_body.proto
index 58e8d93..01c3a11 100644
--- a/voltha/protos/bbf_fiber_channelpartition_body.proto
+++ b/voltha/protos/bbf_fiber_channelpartition_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "bbf_fiber_types.proto";
diff --git a/voltha/protos/bbf_fiber_channeltermination_body.proto b/voltha/protos/bbf_fiber_channeltermination_body.proto
index 892a612..8f4e9ca 100644
--- a/voltha/protos/bbf_fiber_channeltermination_body.proto
+++ b/voltha/protos/bbf_fiber_channeltermination_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
message ChannelterminationConfigData {
diff --git a/voltha/protos/bbf_fiber_gemport_body.proto b/voltha/protos/bbf_fiber_gemport_body.proto
index b08af06..fe0b9e2 100644
--- a/voltha/protos/bbf_fiber_gemport_body.proto
+++ b/voltha/protos/bbf_fiber_gemport_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "meta.proto";
import "bbf_fiber_tcont_body.proto";
diff --git a/voltha/protos/bbf_fiber_multicast_distribution_set_body.proto b/voltha/protos/bbf_fiber_multicast_distribution_set_body.proto
index 525ce11..60b30f6 100644
--- a/voltha/protos/bbf_fiber_multicast_distribution_set_body.proto
+++ b/voltha/protos/bbf_fiber_multicast_distribution_set_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "meta.proto";
import "bbf_fiber_multicast_gemport_body.proto";
diff --git a/voltha/protos/bbf_fiber_multicast_gemport_body.proto b/voltha/protos/bbf_fiber_multicast_gemport_body.proto
index d8d01bc..1045448 100644
--- a/voltha/protos/bbf_fiber_multicast_gemport_body.proto
+++ b/voltha/protos/bbf_fiber_multicast_gemport_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "meta.proto";
diff --git a/voltha/protos/bbf_fiber_ontani_body.proto b/voltha/protos/bbf_fiber_ontani_body.proto
index b6025ea..31e6648 100644
--- a/voltha/protos/bbf_fiber_ontani_body.proto
+++ b/voltha/protos/bbf_fiber_ontani_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
message OntaniConfigData {
diff --git a/voltha/protos/bbf_fiber_tcont_body.proto b/voltha/protos/bbf_fiber_tcont_body.proto
index 2c24e32..922fc34 100644
--- a/voltha/protos/bbf_fiber_tcont_body.proto
+++ b/voltha/protos/bbf_fiber_tcont_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "meta.proto";
import "bbf_fiber_traffic_descriptor_profile_body.proto";
diff --git a/voltha/protos/bbf_fiber_traffic_descriptor_profile_body.proto b/voltha/protos/bbf_fiber_traffic_descriptor_profile_body.proto
index aa51843..f9ac43d 100644
--- a/voltha/protos/bbf_fiber_traffic_descriptor_profile_body.proto
+++ b/voltha/protos/bbf_fiber_traffic_descriptor_profile_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
import "meta.proto";
diff --git a/voltha/protos/bbf_fiber_types.proto b/voltha/protos/bbf_fiber_types.proto
index 119ced0..d27400d 100644
--- a/voltha/protos/bbf_fiber_types.proto
+++ b/voltha/protos/bbf_fiber_types.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber_types;
enum AuthMethodType
diff --git a/voltha/protos/bbf_fiber_v_enet_body.proto b/voltha/protos/bbf_fiber_v_enet_body.proto
index 4817b36..33da555 100644
--- a/voltha/protos/bbf_fiber_v_enet_body.proto
+++ b/voltha/protos/bbf_fiber_v_enet_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
message VEnetConfigData {
diff --git a/voltha/protos/bbf_fiber_v_ontani_body.proto b/voltha/protos/bbf_fiber_v_ontani_body.proto
index ab13163..af2f3ef 100644
--- a/voltha/protos/bbf_fiber_v_ontani_body.proto
+++ b/voltha/protos/bbf_fiber_v_ontani_body.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
message VOntaniConfigData {
diff --git a/voltha/protos/bbf_fiber_wavelength_profile_body.proto b/voltha/protos/bbf_fiber_wavelength_profile_body.proto
index 30763b6..ab077a4 100644
--- a/voltha/protos/bbf_fiber_wavelength_profile_body.proto
+++ b/voltha/protos/bbf_fiber_wavelength_profile_body.proto
@@ -1,6 +1,9 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/bbf_fiber";
+
package bbf_fiber;
-import public "meta.proto";
+import "meta.proto";
message WavelengthProfileData {
string name = 1;
diff --git a/voltha/protos/common.proto b/voltha/protos/common.proto
index 5f311e1..ce165ae 100644
--- a/voltha/protos/common.proto
+++ b/voltha/protos/common.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/common";
+
package voltha;
import "yang_options.proto";
diff --git a/voltha/protos/device.proto b/voltha/protos/device.proto
index 30baf34..6bb3f41 100644
--- a/voltha/protos/device.proto
+++ b/voltha/protos/device.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "meta.proto";
diff --git a/voltha/protos/events.proto b/voltha/protos/events.proto
index 8bffae9..e583546 100644
--- a/voltha/protos/events.proto
+++ b/voltha/protos/events.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "meta.proto";
@@ -135,4 +137,4 @@
// Key/Value storage for extra information that may give context to the alarm
map<string, string> context = 11;
-}
\ No newline at end of file
+}
diff --git a/voltha/protos/health.proto b/voltha/protos/health.proto
index 19383a9..c7ae099 100644
--- a/voltha/protos/health.proto
+++ b/voltha/protos/health.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "google/api/annotations.proto";
diff --git a/voltha/protos/ietf_interfaces.proto b/voltha/protos/ietf_interfaces.proto
index e3f1204..dc927f2 100644
--- a/voltha/protos/ietf_interfaces.proto
+++ b/voltha/protos/ietf_interfaces.proto
@@ -1,4 +1,7 @@
syntax = "proto3";
+
+option go_package = "github.com/opencord/voltha/protos/go/ietf";
+
package ietf_interfaces;
message Interfaces {
diff --git a/voltha/protos/logical_device.proto b/voltha/protos/logical_device.proto
index 94d9588..b35fb90 100644
--- a/voltha/protos/logical_device.proto
+++ b/voltha/protos/logical_device.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "meta.proto";
diff --git a/voltha/protos/meta.proto b/voltha/protos/meta.proto
index b78c9e3..08462d8 100644
--- a/voltha/protos/meta.proto
+++ b/voltha/protos/meta.proto
@@ -17,6 +17,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/common";
+
package voltha;
import "google/protobuf/descriptor.proto";
diff --git a/voltha/protos/openflow_13.proto b/voltha/protos/openflow_13.proto
index 4cdfc12..ea8235a 100644
--- a/voltha/protos/openflow_13.proto
+++ b/voltha/protos/openflow_13.proto
@@ -57,6 +57,8 @@
*/
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/openflow_13";
+
package openflow_13;
import "google/api/annotations.proto";
diff --git a/voltha/protos/ponsim.proto b/voltha/protos/ponsim.proto
index 4915dab..91018ad 100644
--- a/voltha/protos/ponsim.proto
+++ b/voltha/protos/ponsim.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "google/protobuf/empty.proto";
@@ -21,6 +23,11 @@
repeated openflow_13.ofp_flow_stats flows = 2;
}
+message PonSimFrame {
+ string id = 1;
+ bytes payload = 2;
+}
+
message PonSimPacketCounter {
string name = 1;
int64 value = 2;
@@ -63,6 +70,11 @@
}
service PonSim {
+ rpc SendFrame(PonSimFrame)
+ returns (google.protobuf.Empty) {}
+
+ rpc ReceiveFrames(google.protobuf.Empty)
+ returns (stream PonSimFrame) {}
rpc GetDeviceInfo(google.protobuf.Empty)
returns(PonSimDeviceInfo) {}
diff --git a/voltha/protos/schema.proto b/voltha/protos/schema.proto
index 1023f1a..5dfa52d 100644
--- a/voltha/protos/schema.proto
+++ b/voltha/protos/schema.proto
@@ -1,5 +1,7 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/schema";
+
package schema;
import "google/api/annotations.proto";
diff --git a/voltha/protos/voltha.proto b/voltha/protos/voltha.proto
index 15cef20..1dc7897 100644
--- a/voltha/protos/voltha.proto
+++ b/voltha/protos/voltha.proto
@@ -6,6 +6,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/voltha";
+
package voltha;
import "google/protobuf/empty.proto";
diff --git a/voltha/protos/yang_options.proto b/voltha/protos/yang_options.proto
index e64fef1..fbde8a3 100644
--- a/voltha/protos/yang_options.proto
+++ b/voltha/protos/yang_options.proto
@@ -17,6 +17,8 @@
syntax = "proto3";
+option go_package = "github.com/opencord/voltha/protos/go/common";
+
package voltha;
import "google/protobuf/descriptor.proto";