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