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/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")
+}