First Commit of Voltha-Go-Controller from Radisys

Change-Id: I8e2e908e7ab09a4fe3d86849da18b6d69dcf4ab0
diff --git a/internal/pkg/application/service.go b/internal/pkg/application/service.go
new file mode 100644
index 0000000..e90b948
--- /dev/null
+++ b/internal/pkg/application/service.go
@@ -0,0 +1,1978 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package application
+
+import (
+	"bytes"
+	"encoding/json"
+	"errors"
+	"net"
+	"reflect"
+	infraerrorCodes "voltha-go-controller/internal/pkg/errorcodes"
+	"sort"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/google/gopacket/layers"
+
+	"voltha-go-controller/internal/pkg/controller"
+	cntlr "voltha-go-controller/internal/pkg/controller"
+	"voltha-go-controller/database"
+	"voltha-go-controller/internal/pkg/of"
+	"voltha-go-controller/internal/pkg/util"
+	errorCodes "voltha-go-controller/internal/pkg/errorcodes"
+	"github.com/opencord/voltha-lib-go/v7/pkg/log"
+)
+
+const (
+	// DSLAttrEnabled constant
+	DSLAttrEnabled string = "ENABLED"
+)
+
+// VoltServiceCfg structure
+// Name -	Uniquely identifies a service across the entire application
+// UniVlan -	The VLAN of the packets entering the UNI of ONU
+// CVlan -	The VLAN to transalate to/from on the PON link
+// SVlan -	The outer VLAN to be used on the NNI of OLT.
+//       -	In general, 4096 is used as NO VLAN for all the above
+// SVlanTpid - SVlan Tag Protocl Identifier
+// Pbits -      Each bit of uint8 represents one p-bit. MSB is pbit 7
+// DhcpRelay -	Whether it is turned on/off
+// CircuitId -	The circuit id to be used with DHCP relay. Unused otherwise
+// RemoveId - 	Same as above
+// Port -	The access port for the service. Each service has a single access
+//		port. The converse is not always true
+// MacLearning - If MAC learning is turned on, the MAC address learned from the
+//		the service activation is used in programming flows
+// MacAddress -	The MAC hardware address learnt on the UNI interface
+// MacAddresses - Not yet implemented. To be used to learn more MAC addresses
+type VoltServiceCfg struct {
+	Name                       string
+	UniVlan                    of.VlanType
+	CVlan                      of.VlanType
+	SVlan                      of.VlanType
+	SVlanTpid                  layers.EthernetType
+	MacAddr                    net.HardwareAddr
+	Pbits                      []of.PbitType
+	DsRemarkPbitsMap           map[int]int // Ex: Remark case {0:0,1:0} and No-remark case {1:1}
+	TechProfileID              uint16
+	CircuitID                  string
+	RemoteID                   []byte
+	Port                       string
+	PonPort                    uint32
+	MacLearning                MacLearningType
+	IsOption82Disabled         bool
+	IgmpEnabled                bool
+	McastService               bool
+	ONTEtherTypeClassification int
+	VlanControl                VlanControl
+	UsMeterProfile             string
+	DsMeterProfile             string
+	AggDsMeterProfile          string
+	VnetID                     string
+	MvlanProfileName           string
+	RemoteIDType               string
+	SchedID                    int
+	AllowTransparent           bool
+	EnableMulticastKPI         bool
+	DataRateAttr               string
+	MinDataRateUs              uint32
+	MinDataRateDs              uint32
+	MaxDataRateUs              uint32
+	MaxDataRateDs              uint32
+
+	Trigger ServiceTrigger
+}
+
+// VoltServiceOper structure
+type VoltServiceOper struct {
+	//MacLearning  bool
+	//MacAddr      net.HardwareAddr
+	Device   string
+	Ipv4Addr net.IP
+	Ipv6Addr net.IP
+
+	UsMeterID    uint32
+	DsMeterID    uint32
+	AggDsMeterID uint32
+
+	//Multiservice-Fix
+	UsHSIAFlowsApplied bool
+	DsHSIAFlowsApplied bool
+	UsDhcpFlowsApplied bool
+	DsDhcpFlowsApplied bool
+	IgmpFlowsApplied   bool
+	Icmpv6FlowsApplied bool
+
+	ServiceLock      sync.RWMutex `json:"-"`
+	PendingFlows     map[string]bool
+	AssociatedFlows  map[string]bool
+	DeleteInProgress bool
+	ForceDelete      bool
+	BwAvailInfo      string
+
+	UpdateInProgress bool
+	Metadata         interface{}
+}
+
+// VoltService structure
+type VoltService struct {
+	VoltServiceCfg
+	VoltServiceOper
+	Version string
+}
+
+//ServiceTrigger - Service activation trigger
+type ServiceTrigger int
+
+const (
+	//NBActivate - Service added due to NB Action
+	NBActivate ServiceTrigger = 0
+	//ServiceVlanUpdate - Service added due to Svlan Update
+	ServiceVlanUpdate ServiceTrigger = 1
+)
+
+// AppMutexes structure
+type AppMutexes struct {
+	ServiceDataMutex sync.Mutex `json:"-"`
+	VnetMutex        sync.Mutex `json:"-"`
+}
+
+//MigrateServiceMetadata - migrate services request metadata
+type MigrateServiceMetadata struct {
+	NewVnetID string
+	RequestID string
+}
+
+// AppMutex variable
+var AppMutex AppMutexes
+
+// NewVoltService for constructor for volt service
+func NewVoltService(cfg *VoltServiceCfg) *VoltService {
+	var vs VoltService
+	vs.VoltServiceCfg = *cfg
+	vs.UsHSIAFlowsApplied = false
+	vs.DsHSIAFlowsApplied = false
+	vs.DeleteInProgress = false
+	//vs.MacAddr, _ = net.ParseMAC("00:00:00:00:00:00")
+
+	vs.MacAddr = cfg.MacAddr
+	vs.Ipv4Addr = net.ParseIP("0.0.0.0")
+	vs.Ipv6Addr = net.ParseIP("::")
+	vs.PendingFlows = make(map[string]bool)
+	vs.AssociatedFlows = make(map[string]bool)
+	return &vs
+}
+
+// WriteToDb commit a service to the DB if service delete is not in-progress
+func (vs *VoltService) WriteToDb() {
+
+	vs.ServiceLock.RLock()
+	defer vs.ServiceLock.RUnlock()
+
+	if vs.DeleteInProgress {
+		logger.Warnw(ctx, "Skipping Redis Update for Service, Service delete in progress", log.Fields{"Service": vs.Name})
+		return
+	}
+	vs.ForceWriteToDb()
+}
+
+//ForceWriteToDb force commit a service to the DB
+func (vs *VoltService) ForceWriteToDb() {
+	b, err := json.Marshal(vs)
+
+	if err != nil {
+		logger.Errorw(ctx, "Json Marshal Failed for Service", log.Fields{"Service": vs.Name})
+		return
+	}
+	if err1 := db.PutService(vs.Name, string(b)); err1 != nil {
+		logger.Errorw(ctx, "DB write oper failed for Service", log.Fields{"Service": vs.Name})
+	}
+}
+
+// isDataRateAttrPresent to check if data attribute is present
+func (vs *VoltService) isDataRateAttrPresent() bool {
+	return vs.DataRateAttr == DSLAttrEnabled
+}
+
+// DelFromDb delete a service from DB
+func (vs *VoltService) DelFromDb() {
+	logger.Debugw(ctx, "Deleting Service from DB", log.Fields{"Name": vs.Name})
+	//TODO - Need to understand and delete the second call
+	//Calling twice has worked though don't know why
+	_ = db.DelService(vs.Name)
+	_ = db.DelService(vs.Name)
+}
+
+// MatchesVlans find the service that matches the VLANs. In this case it is
+// purely based on CVLAN. The CVLAN can sufficiently be used to
+// match a service
+func (vs *VoltService) MatchesVlans(vlans []of.VlanType) bool {
+	if len(vlans) != 1 {
+		return false
+	}
+
+	if vlans[0] == vs.CVlan {
+		return true
+	}
+	return false
+}
+
+// MatchesPbits allows matching a service to a pbit. This is used
+// to search for a service matching the pbits, typically to identify
+// attributes for other flows such as DHCP, IGMP, etc.
+func (vs *VoltService) MatchesPbits(pbits []of.PbitType) bool {
+	for _, pbit := range pbits {
+		for _, pb := range vs.Pbits {
+			if pb == pbit {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+// IsPbitExist allows matching a service to a pbit. This is used
+// to search for a service matching the pbit
+func (vs *VoltService) IsPbitExist(pbit of.PbitType) bool {
+	for _, pb := range vs.Pbits {
+		if pb == pbit {
+			return true
+		}
+	}
+	return false
+}
+
+// AddHsiaFlows - Adds US & DS HSIA Flows for the service
+func (vs *VoltService) AddHsiaFlows() {
+	if err := vs.AddUsHsiaFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vs.triggerServiceFailureInd(statusCode, statusMessage)
+	}
+	if err := vs.AddDsHsiaFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vs.triggerServiceFailureInd(statusCode, statusMessage)
+	}
+}
+
+//DelHsiaFlows - Deletes US & DS HSIA Flows for the service
+func (vs *VoltService) DelHsiaFlows() {
+	if err := vs.DelUsHsiaFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vs.triggerServiceFailureInd(statusCode, statusMessage)
+	}
+
+	if err := vs.DelDsHsiaFlows(); err != nil {
+		statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+		vs.triggerServiceFailureInd(statusCode, statusMessage)
+	}
+}
+
+// AddUsHsiaFlows - Add US HSIA Flows for the service
+func (vs *VoltService) AddUsHsiaFlows() error {
+
+	if vs.DeleteInProgress || vs.UpdateInProgress {
+		logger.Errorw(ctx, "Ignoring US HSIA Flow Push, Service deleteion In-Progress", log.Fields{"Device": vs.Device, "Service": vs.Name})
+		return nil
+	}
+
+	va := GetApplication()
+	logger.Infow(ctx, "Configuring US HSIA Service Flows", log.Fields{"ServiceName": vs.Name})
+	if !vs.UsHSIAFlowsApplied || vgcRebooted {
+		device, err := va.GetDeviceFromPort(vs.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error Getting Device", log.Fields{"Reason": err.Error()})
+			return errorCodes.ErrDeviceNotFound
+		} else if device.State != controller.DeviceStateUP {
+			logger.Warnw(ctx, "Device state Down. Ignoring US HSIA Flow Push", log.Fields{"Service": vs.Name, "Port": vs.Port})
+			return nil
+		}
+
+		vs.Device = device.Name
+		va.AddMeterToDevice(vs.Port, device.Name, vs.UsMeterID, 0)
+		va.AddMeterToDevice(vs.Port, device.Name, vs.DsMeterID, vs.AggDsMeterID)
+
+		logger.Infow(ctx, "Adding HSIA flows", log.Fields{"Name": vs.Name})
+		pBits := vs.Pbits
+
+		//If no pbits configured for service, hence add PbitNone for flows
+		if len(vs.Pbits) == 0 {
+			pBits = append(pBits, PbitMatchNone)
+		}
+		for _, pbits := range pBits {
+			usflows, err := vs.BuildUsHsiaFlows(pbits)
+			if err != nil {
+				logger.Errorw(ctx, "Error Building HSIA US flows", log.Fields{"Reason": err.Error()})
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+				continue
+			}
+			usflows.MigrateCookie = vgcRebooted
+			if err := vs.AddFlows(device, usflows); err != nil {
+				logger.Errorw(ctx, "Error adding HSIA US flows", log.Fields{"Reason": err.Error()})
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+			}
+		}
+		vs.UsHSIAFlowsApplied = true
+		logger.Infow(ctx, "Pushed US HSIA Service Flows", log.Fields{"ServiceName": vs.Name})
+	}
+	vs.WriteToDb()
+	return nil
+}
+
+// AddDsHsiaFlows - Add DS HSIA Flows for the service
+func (vs *VoltService) AddDsHsiaFlows() error {
+	if vs.DeleteInProgress {
+		logger.Errorw(ctx, "Ignoring DS HSIA Flow Push, Service deleteion In-Progress", log.Fields{"Device": vs.Device, "Service": vs.Name})
+		return nil
+	}
+
+	va := GetApplication()
+	logger.Infow(ctx, "Configuring DS HSIA Service Flows", log.Fields{"ServiceName": vs.Name})
+	if !vs.DsHSIAFlowsApplied || vgcRebooted {
+		device, err := va.GetDeviceFromPort(vs.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error Getting Device", log.Fields{"Reason": err.Error()})
+			return errorCodes.ErrDeviceNotFound
+		} else if device.State != controller.DeviceStateUP {
+			logger.Warnw(ctx, "Device state Down. Ignoring DS HSIA Flow Push", log.Fields{"Service": vs.Name, "Port": vs.Port})
+			return nil
+		}
+
+		va.AddMeterToDevice(vs.Port, device.Name, vs.DsMeterID, vs.AggDsMeterID)
+		logger.Infow(ctx, "Adding HSIA flows", log.Fields{"Name": vs.Name})
+
+		//If no pbits configured for service, hence add PbitNone for flows
+		if len(vs.DsRemarkPbitsMap) == 0 {
+			dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(of.PbitMatchNone))
+			if err != nil {
+				logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+				return err
+			}
+			dsflows.MigrateCookie = vgcRebooted
+			if err = vs.AddFlows(device, dsflows); err != nil {
+				logger.Errorw(ctx, "Failed to add HSIA DS flows", log.Fields{"Reason": err})
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+			}
+		} else {
+			// if all 8 p-bits are to be remarked to one-pbit, configure all-to-one remarking flow
+			if _, ok := vs.DsRemarkPbitsMap[int(of.PbitMatchAll)]; ok {
+				dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(of.PbitMatchAll))
+				if err != nil {
+					logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+					return err
+				}
+				logger.Debug(ctx, "Add-one-match-all-pbit-flow")
+				dsflows.MigrateCookie = vgcRebooted
+				if err := vs.AddFlows(device, dsflows); err != nil {
+					logger.Errorw(ctx, "Failed to add HSIA DS flows", log.Fields{"Reason": err})
+					statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+					vs.triggerServiceFailureInd(statusCode, statusMessage)
+				}
+			} else {
+				for matchPbit := range vs.DsRemarkPbitsMap {
+					dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(matchPbit))
+					if err != nil {
+						logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+						statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+						vs.triggerServiceFailureInd(statusCode, statusMessage)
+						continue
+					}
+					dsflows.MigrateCookie = vgcRebooted
+					if err := vs.AddFlows(device, dsflows); err != nil {
+						logger.Errorw(ctx, "Failed to Add HSIA DS flows", log.Fields{"Reason": err})
+						statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+						vs.triggerServiceFailureInd(statusCode, statusMessage)
+					}
+				}
+			}
+		}
+		vs.DsHSIAFlowsApplied = true
+		logger.Infow(ctx, "Pushed DS HSIA Service Flows", log.Fields{"ServiceName": vs.Name})
+	}
+	vs.WriteToDb()
+	return nil
+}
+
+// DelUsHsiaFlows - Deletes US HSIA Flows for the service
+func (vs *VoltService) DelUsHsiaFlows() error {
+
+	logger.Infow(ctx, "Removing US HSIA Services", log.Fields{"Services": vs.Name})
+	if vs.UsHSIAFlowsApplied || vgcRebooted {
+		device, err := GetApplication().GetDeviceFromPort(vs.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error Getting Device", log.Fields{"Reason": err.Error()})
+			return errorCodes.ErrDeviceNotFound
+		}
+
+		logger.Infow(ctx, "Removing HSIA flows", log.Fields{"Name": vs.Name})
+		pBits := vs.Pbits
+
+		//If no pbits configured for service, hence add PbitNone for flows
+		if len(vs.Pbits) == 0 {
+			pBits = append(pBits, PbitMatchNone)
+		}
+		for _, pbits := range pBits {
+			usflows, err := vs.BuildUsHsiaFlows(pbits)
+			if err != nil {
+				logger.Errorw(ctx, "Error Building HSIA US flows", log.Fields{"Reason": err.Error()})
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+				continue
+			}
+			usflows.MigrateCookie = vgcRebooted
+			if err = vs.DelFlows(device, usflows); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+			}
+		}
+		vs.UsHSIAFlowsApplied = false
+	}
+	vs.WriteToDb()
+	return nil
+}
+
+// DelDsHsiaFlows - Deletes DS HSIA Flows for the service
+func (vs *VoltService) DelDsHsiaFlows() error {
+
+	logger.Infow(ctx, "Removing DS HSIA Services", log.Fields{"Services": vs.Name})
+	if vs.DsHSIAFlowsApplied || vgcRebooted {
+		device, err := GetApplication().GetDeviceFromPort(vs.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error Getting Device", log.Fields{"Reason": err.Error()})
+			return errorCodes.ErrDeviceNotFound
+		}
+
+		logger.Infow(ctx, "Removing HSIA flows", log.Fields{"Name": vs.Name})
+		var matchPbit int
+		//If no pbits configured for service, hence add PbitNone for flows
+		if len(vs.DsRemarkPbitsMap) == 0 {
+			dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(PbitMatchNone))
+			if err != nil {
+				logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+				return err
+			}
+			dsflows.MigrateCookie = vgcRebooted
+			if err = vs.DelFlows(device, dsflows); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+			}
+		} else if _, ok := vs.DsRemarkPbitsMap[int(PbitMatchAll)]; ok {
+			dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(int(PbitMatchAll)))
+			if err != nil {
+				logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+				return err
+			}
+			dsflows.MigrateCookie = vgcRebooted
+			if err = vs.DelFlows(device, dsflows); err != nil {
+				statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+				vs.triggerServiceFailureInd(statusCode, statusMessage)
+			}
+		} else {
+			for matchPbit = range vs.DsRemarkPbitsMap {
+				dsflows, err := vs.BuildDsHsiaFlows(of.PbitType(matchPbit))
+				if err != nil {
+					logger.Errorw(ctx, "Error Building HSIA DS flows", log.Fields{"Reason": err.Error()})
+					statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+					vs.triggerServiceFailureInd(statusCode, statusMessage)
+					continue
+				}
+				dsflows.MigrateCookie = vgcRebooted
+				if err = vs.DelFlows(device, dsflows); err != nil {
+					statusCode, statusMessage := infraerrorCodes.GetErrorInfo(err)
+					vs.triggerServiceFailureInd(statusCode, statusMessage)
+				}
+			}
+		}
+		vs.DsHSIAFlowsApplied = false
+	}
+	logger.Infow(ctx, "Deleted HSIA DS flows from DB successfuly", log.Fields{"ServiceName": vs.Name})
+	// Post HSIA configuration success indication on message bus
+	vs.WriteToDb()
+	return nil
+}
+
+// BuildDsHsiaFlows build the DS HSIA flows
+// Called for add/delete HSIA flows
+func (vs *VoltService) BuildDsHsiaFlows(pbits of.PbitType) (*of.VoltFlow, error) {
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+
+	// Get the out and in ports for the flows
+	device, err := GetApplication().GetDeviceFromPort(vs.Port)
+	if err != nil {
+		return nil, errorCodes.ErrDeviceNotFound
+	}
+	inport, _ := GetApplication().GetPortID(device.NniPort)
+	outport, _ := GetApplication().GetPortID(vs.Port)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = outport
+	flow.PortName = vs.Port
+	allowTransparent := 0
+	if vs.AllowTransparent {
+		allowTransparent = 1
+	}
+
+	// initialized actnPbit to 0 for cookie genration backward compatibility.
+	var actnPbit of.PbitType
+	remarkPbit, remarkExists := vs.DsRemarkPbitsMap[int(pbits)]
+
+	generateDSCookie := func(vlan of.VlanType, valToShift uint64) uint64 {
+		//| 12-bit cvlan/UniVlan | 4 bits action pbit | <32-bits uniport>| 16-bits HSIA mask OR flow mask OR pbit |
+		cookie := uint64(vlan)<<52 + uint64(actnPbit)<<48 + uint64(outport)<<16 | of.HsiaFlowMask
+		cookie = cookie | of.DsFlowMask
+		cookie = cookie + (valToShift << 4) + uint64(pbits)
+		return cookie
+	}
+
+	l2ProtoValue, err := GetMetadataForL2Protocol(vs.SVlanTpid)
+	if err != nil {
+		logger.Errorw(ctx, "DS HSIA flow push failed: Invalid SvlanTpid", log.Fields{"SvlanTpid": vs.SVlanTpid, "Service": vs.Name})
+		return nil, err
+	}
+
+	// Add Table-0 flow that deals with the outer VLAN in pOLT
+	{
+		subflow1 := of.NewVoltSubFlow()
+		subflow1.SetTableID(0)
+		subflow1.SetGoToTable(1)
+		subflow1.SetInPort(inport)
+
+		if pbits != PbitMatchNone {
+			subflow1.SetMatchPbit(pbits)
+		}
+
+		if remarkExists && (of.PbitType(remarkPbit) != pbits) {
+			subflow1.SetPcp(of.PbitType(remarkPbit))
+			// match & action pbits are different, set remark-pbit action
+			actnPbit = of.PbitType(remarkPbit)
+			// mask remark p-bit to 4bits
+			actnPbit = actnPbit & 0x0F
+		}
+
+		if err := vs.setDSMatchActionVlanT0(subflow1); err != nil {
+			return nil, err
+		}
+		logger.Info(ctx, "HSIA DS flows MAC Learning & MAC", log.Fields{"ML": vs.MacLearning, "Mac": vs.MacAddr})
+		if NonZeroMacAddress(vs.MacAddr) {
+			subflow1.SetMatchDstMac(vs.MacAddr)
+		}
+		subflow1.Priority = of.HsiaFlowPriority
+		subflow1.SetMeterID(vs.DsMeterID)
+
+		/* WriteMetaData 8 Byte(uint64) usage:
+		| Byte8    | Byte7    | Byte6 | Byte5  | Byte4  | Byte3   | Byte2  | Byte1 |
+		| reserved | reserved | TpID  | TpID   | uinID  | uniID   | uniID  | uniID | */
+		metadata := uint64(vs.CVlan)<<48 + uint64(vs.TechProfileID)<<32 + uint64(outport)
+		subflow1.SetWriteMetadata(metadata)
+
+		/* TableMetaData 8 Byte(uint64) Voltha usage:  (Considering MSB bit as 63rd bit and LSB bit as 0th bit)
+		|               Byte8                   | Byte7    | Byte6    |      Byte5       |  Byte4    | Byte3    | Byte2    | Byte1   |
+		| 0000     |    00     |    0     |  0  | 00000000 | 00000000 |  0000   0000     |  00000000 | 00000000 | 00000000 | 00000000|
+		| reserved | svlanTpID |  Buff us |  AT | schedID  | schedID  | onteth  vlanCtrl |  unitag   | unitag   | ctag     | ctag    |  */
+
+		//TODO-COMM:
+		/* TableMetaData 8 Byte(uint64) Community usage:  (Considering MSB bit as 63rd bit and LSB bit as 0th bit)
+		|               Byte8                   | Byte7    | Byte6    |      Byte5       |  Byte4    | Byte3    | Byte2    | Byte1   |
+		| 0000     |    00     |    0     |  0  | 00000000 | 00000000 |  0000   0000     |  00000000 | 00000000 | 00000000 | 00000000|
+		| reserved | svlanTpID |  Buff us |  AT | schedID  | schedID  | onteth  vlanCtrl |   ctag    |  ctag    |  ctag    | ctag    |  */
+
+		metadata = uint64(l2ProtoValue)<<58 | uint64(allowTransparent)<<56 | uint64(vs.SchedID)<<40 | uint64(vs.ONTEtherTypeClassification)<<36 | uint64(vs.VlanControl)<<32 | uint64(vs.CVlan)
+		subflow1.SetTableMetadata(metadata)
+		// TODO - We are using cookie as key and must come up with better cookie
+		// allocation algorithm
+		/**
+		 * Cokies may clash when -
+		 * on same uni-port we have two sub-service
+		 * 1. U=10, C=100, S=310, p-bit=4 - VLAN_Control = OLT_CVLAN_OLT_SVLAN
+		 * 2. U=10, C=10,  S=320, p-bit=4 - VLAN_control = ONU_CVLAN_ONU_SVLAN
+		 * However, this p-bit re-use will not be allowed by sub-mgr.
+		 */
+		if vs.VlanControl == OLTCVlanOLTSVlan {
+			/**
+			 * The new cookie generation is only for OLT_CVLAN_OLT_SVLAN case (TEF residential case) within a UNI.
+			 * After vgc upgrade, if we have to deactivate an already existing TEF residential service, then we have to
+			 * use old cookie.
+			 */
+			subflow1.Cookie = generateDSCookie(vs.UniVlan, 0)
+			if vgcRebooted {
+				subflow1.OldCookie = generateDSCookie(vs.CVlan, 0)
+			}
+		} else {
+			// In case of Olt_Svlan , CVLAN=UNIVLAN so cookie can be constructed with CVLAN as well
+			subflow1.Cookie = generateDSCookie(vs.CVlan, 0)
+		}
+
+		flow.SubFlows[subflow1.Cookie] = subflow1
+		logger.Infow(ctx, "Building downstream HSIA flow for T0", log.Fields{"cookie": subflow1.Cookie,
+			"subflow": subflow1})
+	}
+
+	//Add Table-1 flow that deals with inner VLAN at the ONU
+	{
+		subflow2 := of.NewVoltSubFlow()
+		subflow2.SetTableID(1)
+		subflow2.SetInPort(inport)
+		if NonZeroMacAddress(vs.MacAddr) {
+			subflow2.SetMatchDstMac(vs.MacAddr)
+		}
+
+		if err := vs.setDSMatchActionVlanT1(subflow2); err != nil {
+			return nil, err
+		}
+		if pbits != PbitMatchNone {
+			subflow2.SetMatchPbit(pbits)
+		}
+
+		if remarkExists && (of.PbitType(remarkPbit) != pbits) {
+			subflow2.SetPcp(of.PbitType(remarkPbit))
+		}
+
+		subflow2.SetOutPort(outport)
+		subflow2.SetMeterID(vs.DsMeterID)
+
+		// refer Table-0 flow generation for byte information
+		metadata := uint64(vs.CVlan)<<48 + uint64(vs.TechProfileID)<<32 + uint64(outport)
+		subflow2.SetWriteMetadata(metadata)
+
+		// Table-1 and inport is NNI: It is a DS flow for ONU, add uniport in metadata to make it unique
+		if util.IsNniPort(inport) {
+			metadata = uint64(outport)
+		} else {
+			// refer Table-0 flow generation for byte information
+			metadata = uint64(l2ProtoValue)<<58 | uint64(allowTransparent)<<56 | uint64(vs.SchedID)<<40 | uint64(vs.ONTEtherTypeClassification)<<36 | uint64(vs.VlanControl)<<32 | uint64(vs.CVlan)
+		}
+		subflow2.SetTableMetadata(metadata)
+		// Setting of Cookie - TODO - Improve the allocation algorithm
+		if vs.VlanControl == OLTCVlanOLTSVlan {
+			/**
+			 * The new cookie generation is only for OLT_CVLAN_OLT_SVLAN case (TEF residential case) within a UNI.
+			 * After vgc upgrade, if we have to deactivate an already existing TEF residential service, then we have to
+			 * use old cookie.
+			 */
+			subflow2.Cookie = generateDSCookie(vs.UniVlan, 1)
+			if vgcRebooted {
+				subflow2.OldCookie = generateDSCookie(vs.CVlan, 1)
+			}
+		} else {
+			// In case of Olt_Svlan , CVLAN=UNIVLAN so cookie can be constructed with CVLAN as well
+			subflow2.Cookie = generateDSCookie(vs.CVlan, 1)
+		}
+
+		subflow2.Priority = of.HsiaFlowPriority
+		flow.SubFlows[subflow2.Cookie] = subflow2
+		logger.Infow(ctx, "Building downstream HSIA flow for T1", log.Fields{"cookie": subflow2.Cookie,
+			"subflow": subflow2})
+	}
+
+	return flow, nil
+}
+
+// BuildUsHsiaFlows build the US HSIA flows
+// Called for add/delete HSIA flows
+func (vs *VoltService) BuildUsHsiaFlows(pbits of.PbitType) (*of.VoltFlow, error) {
+	flow := &of.VoltFlow{}
+	flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+
+	// Get the out and in ports for the flows
+	device, err := GetApplication().GetDeviceFromPort(vs.Port)
+	if err != nil {
+		return nil, errorCodes.ErrDeviceNotFound
+	}
+	outport, _ := GetApplication().GetPortID(device.NniPort)
+	inport, _ := GetApplication().GetPortID(vs.Port)
+	// PortName and PortID to be used for validation of port before flow pushing
+	flow.PortID = inport
+	flow.PortName = vs.Port
+	allowTransparent := 0
+	reqBwInfo := 0
+	if vs.AllowTransparent {
+		allowTransparent = 1
+	}
+	if vs.BwAvailInfo == "" {
+		reqBwInfo = 1
+	}
+
+	// Add Table-0 flow that deals with the inner VLAN in ONU
+	{
+		subflow1 := of.NewVoltSubFlow()
+		subflow1.SetTableID(0)
+		subflow1.SetGoToTable(1)
+		subflow1.SetInPort(inport)
+
+		if pbits != PbitMatchNone {
+			subflow1.SetMatchPbit(pbits)
+		}
+		if err := vs.setUSMatchActionVlanT0(subflow1); err != nil {
+			return nil, err
+		}
+		subflow1.SetMeterID(vs.UsMeterID)
+
+		/* WriteMetaData 8 Byte(uint64) usage:
+		| Byte8    | Byte7    | Byte6 | Byte5  | Byte4  | Byte3   | Byte2  | Byte1 |
+		| reserved | reserved | TpID  | TpID   | uinID  | uniID   | uniID  | uniID | */
+		metadata := uint64(vs.CVlan)<<48 + uint64(vs.TechProfileID)<<32 + uint64(outport)
+		subflow1.SetWriteMetadata(metadata)
+
+		/* TableMetaData 8 Byte(uint64) usage: (Considering MSB bit as 63rd bit and LSB bit as 0th bit)
+		|                         Byte8                     |  Byte7    |  Byte6    |      Byte5       |  Byte4    | Byte3    | Byte2    | Byte1   |
+		| 000      |    0      |    00     |    0     |  0  |  00000000 |  00000000 |  0000   0000     |  00000000 | 00000000 | 00000000 | 00000000|
+		| reserved | reqBwInfo | svlanTpID |  Buff us |  AT |  schedID  |  schedID  | onteth  vlanCtrl |  unitag   | unitag   | ctag     | ctag    | */
+		metadata = uint64(reqBwInfo)<<60 | uint64(allowTransparent)<<56 | uint64(vs.SchedID)<<40 | uint64(vs.ONTEtherTypeClassification)<<36 | uint64(vs.VlanControl)<<32 | uint64(vs.CVlan)
+
+		// // In case of MAC Learning enabled voltha will buffer the US flow installation.
+		// if NonZeroMacAddress(vs.MacAddr) {
+		// 	subflow1.SetMatchSrcMac(vs.MacAddr)
+		// } else if vs.MacLearning != MacLearning {
+		// 	metadata |= 1 << 57
+		// 	logger.Infow(ctx, "Buffer us flow at adapter", log.Fields{"metadata": metadata})
+		// }
+		subflow1.SetTableMetadata(metadata)
+		if vs.VlanControl == OLTCVlanOLTSVlan {
+			/**
+			 * The new cookie generation is only for OLT_CVLAN_OLT_SVLAN case (TEF residential case) within a UNI.
+			 * After vgc upgrade, if we have to deactivate an already existing TEF residential service, then we have to
+			 * use old cookie.
+			 */
+			subflow1.Cookie = vs.generateUSCookie(vs.UniVlan, 0, inport, pbits)
+			if vgcRebooted {
+				subflow1.OldCookie = vs.generateUSCookie(vs.CVlan, 0, inport, pbits)
+			}
+		} else {
+			// In case of Olt_Svlan , CVLAN=UNIVLAN so cookie can be constructed with CVLAN as well
+			subflow1.Cookie = vs.generateUSCookie(vs.CVlan, 0, inport, pbits)
+		}
+		subflow1.Priority = of.HsiaFlowPriority
+		flow.SubFlows[subflow1.Cookie] = subflow1
+		logger.Infow(ctx, "Building upstream HSIA flow for T0", log.Fields{"cookie": subflow1.Cookie, "subflow": subflow1})
+	}
+
+	//Add Table-1 flow that deals with the outer vlan in pOLT
+	{
+		subflow2 := of.NewVoltSubFlow()
+		subflow2.SetTableID(1)
+		subflow2.SetInPort(inport)
+
+		if pbits != PbitMatchNone {
+			subflow2.SetMatchPbit(pbits)
+		}
+
+		if err := vs.setUSMatchActionVlanT1(subflow2); err != nil {
+			return nil, err
+		}
+		subflow2.SetInPort(inport)
+		subflow2.SetOutPort(outport)
+		subflow2.SetMeterID(vs.UsMeterID)
+
+		// refer Table-0 flow generation for byte information
+		metadata := uint64(vs.CVlan)<<48 + uint64(vs.TechProfileID)<<32 + uint64(outport)
+		subflow2.SetWriteMetadata(metadata)
+
+		// refer Table-0 flow generation for byte information
+		metadata = uint64(reqBwInfo)<<60 | uint64(allowTransparent)<<56 | uint64(vs.SchedID)<<40 | uint64(vs.ONTEtherTypeClassification)<<36 | uint64(vs.VlanControl)<<32 | uint64(vs.CVlan)
+		// // In case of MAC Learning enabled voltha will buffer the US flow installation.
+		// if NonZeroMacAddress(vs.MacAddr) {
+		// 	subflow2.SetMatchSrcMac(vs.MacAddr)
+		// } else if vs.MacLearning != MacLearningNone {
+		// 	metadata |= 1 << 57
+		// 	logger.Infow(ctx, "Buffer us flow at adapter", log.Fields{"metadata": metadata})
+		// }
+		subflow2.SetTableMetadata(metadata)
+		if vs.VlanControl == OLTCVlanOLTSVlan {
+			/**
+			 * The new cookie generation is only for OLT_CVLAN_OLT_SVLAN case (TEF residential case) within a UNI.
+			 * After vgc upgrade, if we have to deactivate an already existing TEF residential service, then we have to
+			 * use old cookie.
+			 */
+			subflow2.Cookie = vs.generateUSCookie(vs.UniVlan, 1, inport, pbits)
+			if vgcRebooted {
+				subflow2.OldCookie = vs.generateUSCookie(vs.CVlan, 1, inport, pbits)
+			}
+		} else {
+			// In case of Olt_Svlan , CVLAN=UNIVLAN so cookie can be constructed with CVLAN as well
+			subflow2.Cookie = vs.generateUSCookie(vs.CVlan, 1, inport, pbits)
+		}
+		subflow2.Priority = of.HsiaFlowPriority
+
+		flow.SubFlows[subflow2.Cookie] = subflow2
+		logger.Infow(ctx, "Building upstream HSIA flow for T1", log.Fields{"cookie": subflow2.Cookie, "subflow": subflow2})
+	}
+
+	return flow, nil
+}
+
+func (vs *VoltService) generateUSCookie(vlan of.VlanType, valToShift uint64, inport uint32, pbits of.PbitType) uint64 {
+	//| 12-bit cvlan/UniVlan | 4 bits empty | <32-bits uniport>| 16-bits HSIA mask OR flow mask OR pbit |
+	cookie := uint64(vlan)<<52 + uint64(inport)<<16 | of.HsiaFlowMask
+	cookie = cookie | of.UsFlowMask
+	cookie = cookie + (valToShift << 4) + uint64(pbits)
+	return cookie
+}
+
+// setUSMatchActionVlanT1 - Sets the Match & Action w.r.t Vlans for US Table-1
+// based on different Vlan Controls
+func (vs *VoltService) setUSMatchActionVlanT1(flow *of.VoltSubFlow) error {
+	switch vs.VlanControl {
+	case None:
+		flow.SetMatchVlan(vs.SVlan)
+	case ONUCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.CVlan)
+		flow.SetPushVlan(vs.SVlan, vs.SVlanTpid)
+	case OLTCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.UniVlan)
+		flow.SetSetVlan(vs.CVlan)
+		flow.SetPushVlan(vs.SVlan, vs.SVlanTpid)
+	case ONUCVlan:
+		flow.SetMatchVlan(vs.SVlan)
+	case OLTSVlan:
+		if vs.UniVlan != of.VlanAny && vs.UniVlan != of.VlanNone {
+			flow.SetMatchVlan(vs.UniVlan)
+			flow.SetSetVlan(vs.SVlan)
+		} else if vs.UniVlan != of.VlanNone {
+			flow.SetMatchVlan(vs.UniVlan)
+			flow.SetPushVlan(vs.SVlan, layers.EthernetTypeDot1Q)
+		} else {
+			flow.SetPushVlan(vs.SVlan, layers.EthernetTypeDot1Q)
+		}
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vs.VlanControl})
+		return errorCodes.ErrInvalidParamInRequest
+	}
+	return nil
+}
+
+// setDSMatchActionVlanT0 - Sets the Match & Action w.r.t Vlans for DS Table-0
+// based on different Vlan Controls
+func (vs *VoltService) setDSMatchActionVlanT0(flow *of.VoltSubFlow) error {
+	switch vs.VlanControl {
+	case None:
+		flow.SetMatchVlan(vs.SVlan)
+	case ONUCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.SVlan)
+		flow.SetPopVlan()
+	case OLTCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.SVlan)
+		flow.SetPopVlan()
+		flow.SetSetVlan(vs.UniVlan)
+	case ONUCVlan:
+		flow.SetMatchVlan(vs.SVlan)
+	case OLTSVlan:
+		flow.SetMatchVlan(vs.SVlan)
+		if vs.UniVlan != of.VlanNone && vs.UniVlan != of.VlanAny {
+			flow.SetSetVlan(vs.UniVlan)
+		} else {
+			flow.SetPopVlan()
+		}
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vs.VlanControl})
+		return errorCodes.ErrInvalidParamInRequest
+	}
+	return nil
+}
+
+// setUSMatchActionVlanT0 - Sets the Match & Action w.r.t Vlans for US Table-0
+// based on different Vlan Controls
+func (vs *VoltService) setUSMatchActionVlanT0(flow *of.VoltSubFlow) error {
+	switch vs.VlanControl {
+	case None:
+		flow.SetMatchVlan(vs.SVlan)
+	case ONUCVlanOLTSVlan:
+		if vs.UniVlan != of.VlanNone {
+			flow.SetMatchVlan(vs.UniVlan)
+			flow.SetSetVlan(vs.CVlan)
+		} else {
+			flow.SetPushVlan(vs.CVlan, layers.EthernetTypeDot1Q)
+		}
+	case OLTCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.UniVlan)
+	case ONUCVlan:
+		if vs.UniVlan != of.VlanNone {
+			flow.SetMatchVlan(vs.UniVlan)
+			flow.SetSetVlan(vs.SVlan)
+		} else {
+			flow.SetPushVlan(vs.SVlan, layers.EthernetTypeDot1Q)
+		}
+	case OLTSVlan:
+		flow.SetMatchVlan(vs.UniVlan)
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vs.VlanControl})
+		return errorCodes.ErrInvalidParamInRequest
+	}
+	return nil
+}
+
+// setDSMatchActionVlanT1 - Sets the Match & Action w.r.t Vlans for DS Table-1
+// based on different Vlan Controls
+func (vs *VoltService) setDSMatchActionVlanT1(flow *of.VoltSubFlow) error {
+	switch vs.VlanControl {
+	case None:
+		flow.SetMatchVlan(vs.SVlan)
+	case ONUCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.CVlan)
+		if vs.UniVlan != of.VlanNone {
+			flow.SetSetVlan(vs.UniVlan)
+		} else {
+			flow.SetPopVlan()
+		}
+	case OLTCVlanOLTSVlan:
+		flow.SetMatchVlan(vs.UniVlan)
+	case ONUCVlan:
+		flow.SetMatchVlan(vs.SVlan)
+		if vs.UniVlan != of.VlanNone {
+			flow.SetSetVlan(vs.UniVlan)
+		} else {
+			flow.SetPopVlan()
+		}
+	case OLTSVlan:
+		flow.SetMatchVlan(vs.UniVlan)
+	default:
+		logger.Errorw(ctx, "Invalid Vlan Control Option", log.Fields{"Value": vs.VlanControl})
+		return errorCodes.ErrInvalidParamInRequest
+	}
+	return nil
+}
+
+// SvcUpInd for service up indication
+func (vs *VoltService) SvcUpInd() {
+	vs.AddHsiaFlows()
+}
+
+// SvcDownInd for service down indication
+func (vs *VoltService) SvcDownInd() {
+	vs.DelHsiaFlows()
+}
+
+// SetIpv4Addr to set ipv4 address
+func (vs *VoltService) SetIpv4Addr(addr net.IP) {
+	vs.Ipv4Addr = addr
+}
+
+// SetIpv6Addr to set ipv6 address
+func (vs *VoltService) SetIpv6Addr(addr net.IP) {
+	vs.Ipv6Addr = addr
+}
+
+// SetMacAddr to set mac address
+func (vs *VoltService) SetMacAddr(addr net.HardwareAddr) {
+	vs.MacAddr = addr
+}
+
+// ----------------------------------------------
+// VOLT Application - Related to services
+// ---------------------------------------------
+// ---------------------------------------------------------------
+// Service CRUD functions. These are exposed to the overall binary
+// to be invoked from the point where the CRUD operations are received
+// from the external entities
+
+// AddService :  A service in the context of VOLT is a subscriber or service of a
+// subscriber which is uniquely identified by a combination of MAC
+// address, VLAN tags, 802.1p bits. However, in the context of the
+// current implementation, a service is an entity that is identified by a
+// unique L2 (MAC address + VLANs) or unique L3 (VLANs + IP address)
+// FUNC: Add Service
+func (va *VoltApplication) AddService(cfg VoltServiceCfg, oper *VoltServiceOper) error {
+	var mmUs, mmDs *VoltMeter
+	var err error
+
+	//Take the  Device lock only in case of NB add request.
+	// Allow internal adds since internal add happen only under
+	// 1. Restore Service from DB
+	// 2. Service Migration
+	if oper == nil {
+		if svc := va.GetService(cfg.Name); svc != nil {
+			logger.Warnw(ctx, "Service Already Exists. Ignoring Add Service Request", log.Fields{"Name": cfg.Name})
+			return errors.New("Service Already Exists")
+		}
+	}
+
+	logger.Infow(ctx, "Service to be configured", log.Fields{"Cfg": cfg})
+	// Service doesn't exist. So create it and add to the port
+	vs := NewVoltService(&cfg)
+	if oper != nil {
+		vs.UsHSIAFlowsApplied = oper.UsHSIAFlowsApplied
+		vs.DsHSIAFlowsApplied = oper.DsHSIAFlowsApplied
+		vs.Ipv4Addr = oper.Ipv4Addr
+		vs.Ipv6Addr = oper.Ipv6Addr
+		vs.MacLearning = cfg.MacLearning
+		vs.PendingFlows = oper.PendingFlows
+		vs.AssociatedFlows = oper.AssociatedFlows
+		vs.DeleteInProgress = oper.DeleteInProgress
+		vs.BwAvailInfo = oper.BwAvailInfo
+		vs.Device = oper.Device
+	} else {
+
+		//Sorting Pbit from highest
+		sort.Slice(vs.Pbits, func(i, j int) bool {
+			return vs.Pbits[i] > vs.Pbits[j]
+		})
+		logger.Infow(ctx, "Sorted Pbits", log.Fields{"Pbits": vs.Pbits})
+	}
+	logger.Infow(ctx, "VolthService...", log.Fields{"vs": vs.Name})
+
+	// The bandwidth and shaper profile combined into meter
+	if mmDs, err = va.GetMeter(cfg.DsMeterProfile); err == nil {
+		vs.DsMeterID = mmDs.ID
+	} else {
+		return errors.New("DownStream meter profile not found")
+	}
+
+	// The aggregated downstream meter profile
+	// if mmAg, err = va.GetMeter(cfg.AggDsMeterProfile); err == nil {
+	// 	vs.AggDsMeterID = mmAg.ID
+	// } else {
+	// 	return errors.New("Aggregated meter profile not found")
+	// }
+
+	// if cfg.AggDsMeterProfile == cfg.UsMeterProfile {
+	// 	vs.UsMeterID = mmAg.ID
+	// } else {
+	// The bandwidth and shaper profile combined into meter
+	if mmUs, err = va.GetMeter(cfg.UsMeterProfile); err == nil {
+		vs.UsMeterID = mmUs.ID
+	} else {
+		return errors.New("Upstream meter profile not found")
+	}
+	//}
+
+	AppMutex.ServiceDataMutex.Lock()
+	defer AppMutex.ServiceDataMutex.Unlock()
+
+	// Add the service to the VNET
+	vnet := va.GetVnet(cfg.SVlan, cfg.CVlan, cfg.UniVlan)
+	if vnet != nil {
+		if vpv := va.GetVnetByPort(vs.Port, cfg.SVlan, cfg.CVlan, cfg.UniVlan); vpv != nil {
+			vpv.VpvLock.Lock()
+			vpv.AddSvc(vs)
+			vpv.VpvLock.Unlock()
+		} else {
+			va.AddVnetToPort(vs.Port, vnet, vs)
+		}
+	} else {
+		logger.Error(ctx, "VNET-does-not-exist-for-service", log.Fields{"ServiceName": cfg.Name})
+		return errors.New("VNET doesn't exist")
+	}
+
+	vs.Version = database.PresentVersionMap[database.ServicePath]
+	// Add the service to the volt application
+	va.ServiceByName.Store(vs.Name, vs)
+	vs.WriteToDb()
+
+	if nil == oper {
+
+		if !vs.UsHSIAFlowsApplied {
+			vs.triggerServiceInProgressInd()
+		}
+
+		//Update meter profiles service count if service is being added from northbound
+		mmDs.AssociatedServices++
+		va.UpdateMeterProf(*mmDs)
+		if mmUs != nil {
+			mmUs.AssociatedServices++
+			va.UpdateMeterProf(*mmUs)
+		}
+		//mmAg.AssociatedServices++
+		//va.UpdateMeterProf(*mmAg)
+		logger.Debug(ctx, "northbound-service-add-sucessful", log.Fields{"ServiceName": vs.Name})
+	}
+
+	logger.Warnw(ctx, "Added Service to DB", log.Fields{"Name": vs.Name, "Port": (vs.Port), "ML": vs.MacLearning})
+	return nil
+}
+
+//DelServiceWithPrefix - Deletes service with the provided prefix.
+// Added for DT/TT usecase with sadis replica interface
+func (va *VoltApplication) DelServiceWithPrefix(prefix string) {
+	va.ServiceByName.Range(func(key, value interface{}) bool {
+		srvName := key.(string)
+		vs := value.(*VoltService)
+		if strings.Contains(srvName, prefix) {
+			va.DelService(srvName, true, nil, false)
+
+			vnetName := strconv.FormatUint(uint64(vs.SVlan), 10) + "-"
+			vnetName = vnetName + strconv.FormatUint(uint64(vs.CVlan), 10) + "-"
+			vnetName = vnetName + strconv.FormatUint(uint64(vs.UniVlan), 10)
+
+			if err := va.DelVnet(vnetName, ""); err != nil {
+				logger.Warnw(ctx, "Delete Vnet Failed", log.Fields{"Name": vnetName, "Error": err})
+			}
+		}
+		return true
+	})
+}
+
+// DelService delete a service form the application
+func (va *VoltApplication) DelService(name string, forceDelete bool, newSvc *VoltServiceCfg, serviceMigration bool) {
+
+	AppMutex.ServiceDataMutex.Lock()
+	defer AppMutex.ServiceDataMutex.Unlock()
+
+	logger.Warnw(ctx, "Delete Service Request", log.Fields{"Service": name, "ForceDelete": forceDelete, "serviceMigration": serviceMigration})
+	var noFlowsPresent bool
+
+	vsIntf, ok := va.ServiceByName.Load(name)
+	if !ok {
+		logger.Warnw(ctx, "Service doesn't exist", log.Fields{"ServiceName": name})
+		return
+	}
+	vs := vsIntf.(*VoltService)
+	vpv := va.GetVnetByPort(vs.Port, vs.SVlan, vs.CVlan, vs.UniVlan)
+	if vpv == nil {
+		logger.Errorw(ctx, "Vpv Not found for Service", log.Fields{"vs": vs.Name})
+		return
+	}
+
+	//Set this to avoid race-condition during flow result processing
+	vs.DeleteInProgress = true
+	vs.ForceDelete = forceDelete
+	vs.ForceWriteToDb()
+
+	if len(vs.AssociatedFlows) == 0 {
+		noFlowsPresent = true
+	}
+	vpv.VpvLock.Lock()
+	defer vpv.VpvLock.Unlock()
+
+	vs.DelHsiaFlows()
+
+	if vpv.IgmpEnabled {
+		va.ReceiverDownInd(vpv.Device, vpv.Port)
+	}
+	logger.Infow(ctx, "Delete Service from VPV", log.Fields{"VPV_Port": vpv.Port, "VPV_SVlan": vpv.SVlan, "VPV_CVlan": vpv.CVlan, "VPV_UniVlan": vpv.UniVlan, "ServiceName": name})
+	vpv.DelService(vs)
+	if vpv.servicesCount.Load() == 0 {
+		va.DelVnetFromPort(vs.Port, vpv)
+	}
+
+	// Delete the service immediately in case of Force Delete
+	// This will be enabled when profile reconciliation happens after restore
+	// of backedup data
+	if vs.ForceDelete {
+		vs.DelFromDb()
+		GetApplication().ServiceByName.Delete(vs.Name)
+		logger.Warnw(ctx, "Deleted service from DB/Cache successfully", log.Fields{"serviceName": vs.Name})
+	}
+
+	meterProfiles := make(map[*VoltMeter]bool)
+
+	if nil != newSvc {
+		logger.Infow(ctx, "Old Service meter profiles", log.Fields{"AGG": vs.AggDsMeterProfile, "DS": vs.DsMeterProfile, "US": vs.UsMeterProfile})
+		logger.Infow(ctx, "New Service meter profiles", log.Fields{"AGG": newSvc.AggDsMeterProfile, "DS": newSvc.DsMeterProfile, "US": newSvc.UsMeterProfile})
+	}
+	skipMeterDeletion := false
+	if aggMeter, ok := va.MeterMgr.GetMeterByID(vs.AggDsMeterID); ok {
+		if nil != newSvc && aggMeter.Name == newSvc.AggDsMeterProfile {
+			skipMeterDeletion = true
+		}
+
+		meterProfiles[aggMeter] = skipMeterDeletion
+		skipMeterDeletion = false
+	}
+	if dsMeter, ok := va.MeterMgr.GetMeterByID(vs.DsMeterID); ok {
+		if nil != newSvc && dsMeter.Name == newSvc.DsMeterProfile {
+			skipMeterDeletion = true
+		}
+		meterProfiles[dsMeter] = skipMeterDeletion
+		skipMeterDeletion = false
+	}
+	if vs.AggDsMeterID != vs.UsMeterID {
+		if usMeter, ok := va.MeterMgr.GetMeterByID(vs.UsMeterID); ok {
+			if nil != newSvc && usMeter.Name == newSvc.UsMeterProfile {
+				skipMeterDeletion = true
+			}
+			meterProfiles[usMeter] = skipMeterDeletion
+		}
+	}
+
+	for meter, skipMeterDeletion := range meterProfiles {
+		if nil == meter {
+			logger.Debug(ctx, "Null meter found, continuing")
+			continue
+		}
+		if meter.AssociatedServices > 0 {
+			meter.AssociatedServices--
+			if meter.AssociatedServices == 0 && !skipMeterDeletion {
+				logger.Info(ctx, "Meter should be deleted now\n", log.Fields{"MeterID": meter})
+				va.UpdateMeterProf(*meter)
+			}
+		}
+	}
+
+	if noFlowsPresent || vs.ForceDelete {
+		vs.CheckAndDeleteService()
+	}
+
+	//Delete the per service counter too
+	va.ServiceCounters.Delete(name)
+	if vs.IgmpEnabled && vs.EnableMulticastKPI {
+		_ = db.DelAllServiceChannelCounter(name)
+	}
+}
+
+//AddFlows - Adds the flow to the service
+// Triggers flow addition after registering for flow indication event
+func (vs *VoltService) AddFlows(device *VoltDevice, flow *of.VoltFlow) error {
+
+	// Using locks instead of concurrent map for PendingFlows to avoid
+	// race condition during flow response indication processing
+	vs.ServiceLock.Lock()
+	defer vs.ServiceLock.Unlock()
+
+	for cookie := range flow.SubFlows {
+		cookie := strconv.FormatUint(cookie, 10)
+		fe := &FlowEvent{
+			eType:     EventTypeServiceFlowAdded,
+			device:    device.Name,
+			cookie:    cookie,
+			eventData: vs,
+		}
+		device.RegisterFlowAddEvent(cookie, fe)
+		vs.PendingFlows[cookie] = true
+	}
+	return cntlr.GetController().AddFlows(vs.Port, device.Name, flow)
+}
+
+//FlowInstallSuccess - Called when corresponding service flow installation is success
+// If no more pending flows, HSIA indication wil be triggered
+func (vs *VoltService) FlowInstallSuccess(cookie string, bwAvailInfo of.BwAvailDetails) {
+	if vs.DeleteInProgress {
+		logger.Warnw(ctx, "Skipping Flow Add Success Notification. Service deletion in-progress", log.Fields{"Cookie": cookie, "Service": vs.Name})
+		return
+	}
+	vs.ServiceLock.Lock()
+
+	if _, ok := vs.PendingFlows[cookie]; !ok {
+		logger.Errorw(ctx, "Flow Add Success for unknown Cookie", log.Fields{"Service": vs.Name, "Cookie": cookie})
+		vs.ServiceLock.Unlock()
+		return
+	}
+
+	delete(vs.PendingFlows, cookie)
+	vs.AssociatedFlows[cookie] = true
+	vs.ServiceLock.Unlock()
+	var prevBwAvail, presentBwAvail string
+	if bwAvailInfo.PrevBw != "" && bwAvailInfo.PresentBw != "" {
+		prevBwAvail = bwAvailInfo.PrevBw
+		presentBwAvail = bwAvailInfo.PresentBw
+		vs.BwAvailInfo = prevBwAvail + "," + presentBwAvail
+		logger.Debug(ctx, "Bandwidth-value-formed", log.Fields{"BwAvailInfo": vs.BwAvailInfo})
+	}
+	vs.WriteToDb()
+
+	if len(vs.PendingFlows) == 0 && vs.DsHSIAFlowsApplied {
+
+		device, err := GetApplication().GetDeviceFromPort(vs.Port)
+		if err != nil {
+			logger.Errorw(ctx, "Error Getting Device. Dropping HSIA Success indication to NB", log.Fields{"Reason": err.Error(), "Service": vs.Name, "Port": vs.Port})
+			return
+		} else if device.State != controller.DeviceStateUP {
+			logger.Warnw(ctx, "Device state Down. Dropping HSIA Success indication to NB", log.Fields{"Service": vs.Name, "Port": vs.Port})
+			return
+		}
+
+		if vs.Trigger == ServiceVlanUpdate {
+			vs.Trigger = NBActivate
+			defer vs.WriteToDb()
+		}
+		logger.Infow(ctx, "All Flows installed for Service", log.Fields{"Service": vs.Name})
+		return
+	}
+	logger.Infow(ctx, "Processed Service Flow Add Success Indication", log.Fields{"Cookie": cookie, "Service": vs.Name, "DsFlowsApplied": vs.DsHSIAFlowsApplied})
+}
+
+//FlowInstallFailure - Called when corresponding service flow installation is failed
+// Trigger service failure indication to NB
+func (vs *VoltService) FlowInstallFailure(cookie string, errorCode uint32, errReason string) {
+	vs.ServiceLock.RLock()
+
+	if _, ok := vs.PendingFlows[cookie]; !ok {
+		logger.Errorw(ctx, "Flow Add Failure for unknown Cookie", log.Fields{"Service": vs.Name, "Cookie": cookie})
+		vs.ServiceLock.RUnlock()
+		return
+	}
+	vs.ServiceLock.RUnlock()
+	logger.Errorw(ctx, "HSIA Flow Add Failure Notification", log.Fields{"uniPort": vs.Port, "Cookie": cookie, "Service": vs.Name, "ErrorCode": errorCode, "ErrorReason": errReason})
+	vs.triggerServiceFailureInd(errorCode, errReason)
+}
+
+//DelFlows - Deletes the flow from the service
+// Triggers flow deletion after registering for flow indication event
+func (vs *VoltService) DelFlows(device *VoltDevice, flow *of.VoltFlow) error {
+
+	if !vs.ForceDelete {
+		// Using locks instead of concurrent map for AssociatedFlows to avoid
+		// race condition during flow response indication processing
+		vs.ServiceLock.Lock()
+		defer vs.ServiceLock.Unlock()
+
+		for cookie := range flow.SubFlows {
+			cookie := strconv.FormatUint(cookie, 10)
+			fe := &FlowEvent{
+				eType:     EventTypeServiceFlowRemoved,
+				cookie:    cookie,
+				eventData: vs,
+			}
+			device.RegisterFlowDelEvent(cookie, fe)
+		}
+	}
+	return cntlr.GetController().DelFlows(vs.Port, device.Name, flow)
+}
+
+//CheckAndDeleteService - remove service from DB is there are no pending flows to be removed
+func (vs *VoltService) CheckAndDeleteService() {
+	if vs.DeleteInProgress && len(vs.AssociatedFlows) == 0 && !vs.DsHSIAFlowsApplied {
+		vs.DelFromDb()
+		GetApplication().ServiceByName.Delete(vs.Name)
+		logger.Warnw(ctx, "Deleted service from DB/Cache successfully", log.Fields{"serviceName": vs.Name})
+	}
+}
+
+//FlowRemoveSuccess - Called when corresponding service flow removal is success
+// If no more associated flows, DelHSIA indication wil be triggered
+func (vs *VoltService) FlowRemoveSuccess(cookie string) {
+
+	// if vs.DeleteInProgress {
+	// 	logger.Warnw(ctx, "Skipping Flow Remove Success Notification. Service deletion in-progress", log.Fields{"Cookie": cookie, "Service": vs.Name})
+	// 	return
+	// }
+	vs.ServiceLock.Lock()
+	logger.Infow(ctx, "Processing Service Flow Remove Success Indication", log.Fields{"Cookie": cookie, "Service": vs.Name, "Associated Flows": vs.AssociatedFlows, "DsFlowsApplied": vs.DsHSIAFlowsApplied})
+
+	if _, ok := vs.AssociatedFlows[cookie]; ok {
+		delete(vs.AssociatedFlows, cookie)
+	} else if _, ok := vs.PendingFlows[cookie]; ok {
+		logger.Errorw(ctx, "Service Flow Remove: Cookie Present in Pending Flow list. No Action", log.Fields{"Service": vs.Name, "Cookie": cookie, "AssociatedFlows": vs.AssociatedFlows, "PendingFlows": vs.PendingFlows})
+	} else {
+		logger.Errorw(ctx, "Service Flow Remove Success for unknown Cookie", log.Fields{"Service": vs.Name, "Cookie": cookie, "AssociatedFlows": vs.AssociatedFlows, "PendingFlows": vs.PendingFlows})
+	}
+
+	vs.ServiceLock.Unlock()
+
+	vs.WriteToDb()
+
+	if len(vs.AssociatedFlows) == 0 && !vs.DsHSIAFlowsApplied {
+
+		device := GetApplication().GetDevice(vs.Device)
+		if device == nil {
+			logger.Errorw(ctx, "Error Getting Device. Dropping DEL_HSIA Success indication to NB", log.Fields{"Service": vs.Name, "Port": vs.Port})
+			return
+		} else if device.State != controller.DeviceStateUP {
+			logger.Warnw(ctx, "Device state Down. Dropping DEL_HSIA Success indication to NB", log.Fields{"Service": vs.Name, "Port": vs.Port})
+			return
+		}
+
+		if vs.UpdateInProgress {
+			vs.updateVnetProfile(vs.Device)
+			//Not sending DEL_HSIA Indication since it wil be generated internally by SubMgr
+			return
+		}
+		logger.Infow(ctx, "All Flows removed for Service. Triggering Service De-activation Success indication to NB", log.Fields{"Service": vs.Name, "DeleteFlag": vs.DeleteInProgress})
+		vs.CheckAndDeleteService()
+
+		return
+	}
+	logger.Infow(ctx, "Processed Service Flow Remove Success Indication", log.Fields{"Cookie": cookie, "Service": vs.Name, "Associated Flows": vs.AssociatedFlows, "DsFlowsApplied": vs.DsHSIAFlowsApplied})
+}
+
+//FlowRemoveFailure - Called when corresponding service flow installation is failed
+// Trigger service failure indication to NB
+func (vs *VoltService) FlowRemoveFailure(cookie string, errorCode uint32, errReason string) {
+	vs.ServiceLock.RLock()
+
+	if _, ok := vs.AssociatedFlows[cookie]; !ok {
+		logger.Errorw(ctx, "Flow Failure for unknown Cookie", log.Fields{"Service": vs.Name, "Cookie": cookie})
+		vs.ServiceLock.RUnlock()
+		return
+	}
+	if vs.DeleteInProgress {
+		delete(vs.AssociatedFlows, cookie)
+	}
+	vs.ServiceLock.RUnlock()
+	logger.Errorw(ctx, "Service Flow Remove Failure Notification", log.Fields{"uniPort": vs.Port, "Cookie": cookie, "Service": vs.Name, "ErrorCode": errorCode, "ErrorReason": errReason})
+
+	vs.triggerServiceFailureInd(errorCode, errReason)
+	vs.CheckAndDeleteService()
+}
+
+func (vs *VoltService) triggerServiceFailureInd(errorCode uint32, errReason string) {
+	device, err := GetApplication().GetDeviceFromPort(vs.Port)
+	if err != nil {
+		logger.Errorw(ctx, "Error Getting Device. Dropping DEL_HSIA Failure indication to NB", log.Fields{"Reason": err.Error(), "Service": vs.Name, "Port": vs.Port})
+		return
+	} else if device.State != controller.DeviceStateUP {
+		logger.Warnw(ctx, "Device state Down. Dropping DEL_HSIA Failure indication to NB", log.Fields{"Service": vs.Name, "Port": vs.Port})
+		return
+	}
+}
+
+// RestoreSvcsFromDb read from the DB and restore all the services
+func (va *VoltApplication) RestoreSvcsFromDb() {
+	// VNETS must be learnt first
+	vss, _ := db.GetServices()
+	for _, vs := range vss {
+		b, ok := vs.Value.([]byte)
+		if !ok {
+			logger.Warn(ctx, "The value type is not []byte")
+			continue
+		}
+		var vvs VoltService
+		err := json.Unmarshal(b, &vvs)
+		if err != nil {
+			logger.Warn(ctx, "Unmarshal of VNET failed")
+			continue
+		}
+		logger.Debugw(ctx, "Retrieved Service", log.Fields{"Service": vvs.VoltServiceCfg})
+		if err := va.AddService(vvs.VoltServiceCfg, &vvs.VoltServiceOper); err != nil {
+			logger.Warnw(ctx, "Add New Service Failed", log.Fields{"Service": vvs.Name, "Error": err})
+		}
+
+		if vvs.VoltServiceOper.DeleteInProgress {
+			va.ServicesToDelete[vvs.VoltServiceCfg.Name] = true
+			logger.Warnw(ctx, "Service (restored) to be deleted", log.Fields{"Service": vvs.Name})
+		}
+	}
+}
+
+// GetService to get service
+func (va *VoltApplication) GetService(name string) *VoltService {
+	if vs, ok := va.ServiceByName.Load(name); ok {
+		return vs.(*VoltService)
+	}
+	return nil
+}
+
+// GetCircuitID to get circuit id
+func (vs *VoltService) GetCircuitID() []byte {
+	return []byte(vs.CircuitID)
+}
+
+// GetRemoteID to get remote id
+func (vs *VoltService) GetRemoteID() []byte {
+	return []byte(vs.RemoteID)
+}
+
+// IPAssigned to check if ip is assigned
+func (vs *VoltService) IPAssigned() bool {
+	if vs.Ipv4Addr != nil && !vs.Ipv4Addr.Equal(net.ParseIP("0.0.0.0")) {
+		return true
+	} else if vs.Ipv6Addr != nil && !vs.Ipv6Addr.Equal(net.ParseIP("0:0:0:0:0:0:0:0")) {
+		return true
+	}
+	return false
+}
+
+// GetServiceNameFromCookie to get service name from cookie
+func (va *VoltApplication) GetServiceNameFromCookie(cookie uint64, portName string, pbit uint8, device string, tableMetadata uint64) *VoltService {
+
+	var vlan uint64
+	vlanControl := (tableMetadata >> 32) & 0xF
+
+	if vlanControl == uint64(OLTCVlanOLTSVlan) {
+		// Fetching UniVlan for vlanControl OLTCVLANOLTSVLAN
+		vlan = (tableMetadata >> 16) & 0xFFFF
+	} else {
+		//Fetching CVlan for other vlanControl
+		vlan = cookie >> 52
+	}
+	logger.Infow(ctx, "Configured Params", log.Fields{"VlanControl": vlanControl, "vlan": vlan})
+	var vlans []of.VlanType
+	vlans = append(vlans, of.VlanType(vlan))
+	service := GetApplication().GetServiceFromCvlan(device, portName, vlans, uint8(pbit))
+	if nil != service {
+		logger.Info(ctx, "Service Found for", log.Fields{"serviceName": service.Name, "portName": portName, "ctag": vlan})
+	} else {
+		logger.Errorw(ctx, "No Service for", log.Fields{"portName": portName, "ctag": vlan, "Pbit": pbit, "device": device, "VlanControl": vlanControl})
+	}
+	return service
+}
+
+//MigrateServicesReqStatus - update vnet request status
+type MigrateServicesReqStatus string
+
+const (
+	//MigrateSrvsReqInit constant
+	MigrateSrvsReqInit MigrateServicesReqStatus = "Init"
+	//MigrateSrvsReqDeactTriggered constant
+	MigrateSrvsReqDeactTriggered MigrateServicesReqStatus = "Profiles Deactivated"
+	//MigrateSrvsReqCompleted constant
+	MigrateSrvsReqCompleted MigrateServicesReqStatus = "Update Complete"
+)
+
+//MigrateServicesRequest - update vnet request params
+type MigrateServicesRequest struct {
+	ID                  string
+	OldVnetID           string
+	NewVnetID           string
+	ServicesList        map[string]bool
+	DeviceID            string
+	Status              MigrateServicesReqStatus
+	MigrateServicesLock sync.RWMutex
+}
+
+func newMigrateServicesRequest(id string, oldVnetID string, newVnetID string, serviceMap map[string]bool, deviceID string) *MigrateServicesRequest {
+
+	var msr MigrateServicesRequest
+	msr.OldVnetID = oldVnetID
+	msr.NewVnetID = newVnetID
+	msr.ID = id
+	msr.ServicesList = serviceMap
+	msr.DeviceID = deviceID
+	msr.Status = MigrateSrvsReqInit
+	return &msr
+}
+
+//GetMsrKey - generates migrate service request key
+func (msr *MigrateServicesRequest) GetMsrKey() string {
+	return msr.OldVnetID + "-" + msr.ID
+}
+
+// //isRequestComplete - return if all request has been processed and completed
+// // RequestProcessed indicates that all the profile de-activation has been triggered
+// // And the associated profiles indicates the profiles awaiting results
+// func (msr *MigrateServicesRequest) isRequestComplete() bool {
+// 	//return edr.RequestProcessed && (len(edr.AssociatedProfiles) == 0)
+// 	return (len(edr.AssociatedProfiles) == 0)
+// }
+
+//WriteToDB - writes the udpate vnet request details ot DB
+func (msr *MigrateServicesRequest) WriteToDB() {
+	logger.Debugw(ctx, "Adding Migrate Service Request to DB", log.Fields{"OldVnet": msr.OldVnetID, "NewVnet": msr.NewVnetID, "Device": msr.DeviceID, "RequestID": msr.ID, "ServiceCount": len(msr.ServicesList)})
+	if b, err := json.Marshal(msr); err == nil {
+		if err = db.PutMigrateServicesReq(msr.DeviceID, msr.GetMsrKey(), string(b)); err != nil {
+			logger.Warnw(ctx, "PutMigrateServicesReq Failed", log.Fields{"OldVnet": msr.OldVnetID, "NewVnet": msr.NewVnetID,
+									"Device": msr.DeviceID, "Error": err})
+		}
+	}
+}
+
+//MigrateServices - updated vnet profile for services
+func (va *VoltApplication) MigrateServices(serialNum string, reqID string, oldVnetID, newVnetID string, serviceList []string) error {
+
+	logger.Warnw(ctx, "Migrate Serviec Request Received", log.Fields{"SerialNum": serialNum, "RequestID": reqID, "OldVnet": oldVnetID, "NewVnet": newVnetID, "ServiceList": serviceList})
+	if _, ok := va.VnetsByName.Load(oldVnetID); !ok {
+		return errors.New("Old Vnet Id not found")
+	}
+	if _, ok := va.VnetsByName.Load(newVnetID); !ok {
+		return errors.New("New Vnet Id not found")
+	}
+
+	d := va.GetDeviceBySerialNo(serialNum)
+	if d == nil {
+		logger.Errorw(ctx, "Error Getting Device", log.Fields{"SerialNum": serialNum})
+		return errorCodes.ErrDeviceNotFound
+	}
+
+	serviceMap := make(map[string]bool)
+
+	for _, service := range serviceList {
+		serviceMap[service] = false
+	}
+	msr := newMigrateServicesRequest(reqID, oldVnetID, newVnetID, serviceMap, d.Name)
+	msr.WriteToDB()
+
+	d.AddMigratingServices(msr)
+	go msr.ProcessMigrateServicesProfRequest()
+	return nil
+}
+
+//ProcessMigrateServicesProfRequest - collects all associated profiles
+func (msr *MigrateServicesRequest) ProcessMigrateServicesProfRequest() {
+	va := GetApplication()
+	for srv, processed := range msr.ServicesList {
+
+		//Indicates new service is already created and only deletion of old one is pending
+		if processed {
+			va.DelService(srv, true, nil, true)
+			msr.serviceMigrated(srv)
+			continue
+		}
+
+		logger.Infow(ctx, "Migrate Service Triggering", log.Fields{"Service": srv})
+		if vsIntf, ok := va.ServiceByName.Load(srv); ok {
+			vs := vsIntf.(*VoltService)
+			vpv := va.GetVnetByPort(vs.Port, vs.SVlan, vs.CVlan, vs.UniVlan)
+			if vpv == nil {
+				logger.Errorw(ctx, "Vpv Not found for Service", log.Fields{"vs": vs.Name, "port": vs.Port, "Vnet": vs.VnetID})
+				continue
+			}
+			logger.Infow(ctx, "Migrating Service", log.Fields{"Service": vs.Name, "UsFlowApplied": vs.UsHSIAFlowsApplied})
+			vpv.Blocked = true
+
+			// setDeactTrigger := func(key, value interface{}) bool {
+			// 	vs := value.(*VoltService)
+			vs.ServiceLock.Lock()
+			vs.UpdateInProgress = true
+			metadata := &MigrateServiceMetadata{
+				NewVnetID: msr.NewVnetID,
+				RequestID: msr.ID,
+			}
+			vs.Metadata = metadata
+			vs.ServiceLock.Unlock()
+
+			//vpv flows will be removed when last service is removed from it and
+			// new vpv flows will be installed when new service is added
+			if vs.UsHSIAFlowsApplied {
+				vpv.DelTrapFlows()
+				vs.DelHsiaFlows()
+				logger.Info(ctx, "Remove Service Flows Triggered", log.Fields{"Service": srv, "US": vs.UsHSIAFlowsApplied, "DS": vs.DsHSIAFlowsApplied})
+			} else {
+				vs.updateVnetProfile(msr.DeviceID)
+			}
+		} else {
+			logger.Warnw(ctx, "Migrate Service Failed: Service Not Found", log.Fields{"Service": srv, "Vnet": msr.OldVnetID})
+		}
+	}
+}
+
+//AddMigratingServices - store msr info to device obj
+func (d *VoltDevice) AddMigratingServices(msr *MigrateServicesRequest) {
+
+	var msrMap *util.ConcurrentMap
+	if msrMapIntf, ok := d.MigratingServices.Get(msr.OldVnetID); !ok {
+		msrMap = util.NewConcurrentMap()
+	} else {
+		msrMap = msrMapIntf.(*util.ConcurrentMap)
+	}
+
+	msrMap.Set(msr.ID, msr)
+	logger.Infow(ctx, "1: MsrListLen", log.Fields{"Len": msrMap.Length(), "Vnet": msr.OldVnetID})
+
+	d.MigratingServices.Set(msr.OldVnetID, msrMap)
+	logger.Infow(ctx, "1: DeviceMsr", log.Fields{"Device": d.Name, "Vnet": msr.OldVnetID, "Len": d.MigratingServices.Length()})
+
+}
+
+//getMigrateServicesRequest - fetches msr info from device
+func (va *VoltApplication) getMigrateServicesRequest(deviceID string, oldVnetID string, requestID string) *MigrateServicesRequest {
+	if vd := va.GetDevice(deviceID); vd != nil {
+		logger.Infow(ctx, "2: DeviceMsr", log.Fields{"Device": deviceID, "Vnet": oldVnetID, "Len": vd.MigratingServices.Length()})
+		if msrListIntf, ok := vd.MigratingServices.Get(oldVnetID); ok {
+			msrList := msrListIntf.(*util.ConcurrentMap)
+			logger.Infow(ctx, "2: MsrListLen", log.Fields{"Len": msrList.Length(), "Vnet": oldVnetID})
+			if msrObj, ok := msrList.Get(requestID); ok {
+				return msrObj.(*MigrateServicesRequest)
+			}
+
+		}
+	}
+	logger.Errorw(ctx, "Device Not Found", log.Fields{"Device": deviceID})
+	return nil
+}
+
+//updateMigrateServicesRequest - Updates the device with updated msr
+func (va *VoltApplication) updateMigrateServicesRequest(deviceID string, oldVnetID string, requestID string, msr *MigrateServicesRequest) {
+	if vd := va.GetDevice(deviceID); vd != nil {
+		if msrList, ok := vd.MigratingServices.Get(oldVnetID); ok {
+			if _, ok := msrList.(*util.ConcurrentMap).Get(requestID); ok {
+				msrList.(*util.ConcurrentMap).Set(requestID, msr)
+			}
+		}
+	}
+}
+
+//updateVnetProfile - Called on flow process completion
+// Removes old service and creates new VPV & service with udpated vnet profile
+func (vs *VoltService) updateVnetProfile(deviceID string) {
+
+	logger.Info(ctx, "Update Vnet Profile Triggering", log.Fields{"Service": vs.Name, "US": vs.UsHSIAFlowsApplied, "DS": vs.DsHSIAFlowsApplied})
+
+	nvs := VoltService{}
+	nvs.VoltServiceCfg = vs.VoltServiceCfg
+	nvs.Device = vs.Device
+	nvs.Ipv4Addr = vs.Ipv4Addr
+	nvs.Ipv6Addr = vs.Ipv6Addr
+	nvs.UsMeterID = vs.UsMeterID
+	nvs.DsMeterID = vs.DsMeterID
+	nvs.AggDsMeterID = vs.AggDsMeterID
+	nvs.UsHSIAFlowsApplied = vs.UsHSIAFlowsApplied
+	nvs.DsHSIAFlowsApplied = vs.DsHSIAFlowsApplied
+	nvs.UsDhcpFlowsApplied = vs.UsDhcpFlowsApplied
+	nvs.DsDhcpFlowsApplied = vs.DsDhcpFlowsApplied
+	nvs.IgmpFlowsApplied = vs.IgmpFlowsApplied
+	nvs.Icmpv6FlowsApplied = vs.Icmpv6FlowsApplied
+	nvs.PendingFlows = vs.PendingFlows
+	nvs.AssociatedFlows = vs.AssociatedFlows
+	nvs.DeleteInProgress = vs.DeleteInProgress
+	nvs.ForceDelete = vs.ForceDelete
+	nvs.BwAvailInfo = vs.BwAvailInfo
+	nvs.UpdateInProgress = vs.UpdateInProgress
+
+	if nvs.DeleteInProgress {
+		logger.Warnw(ctx, "Skipping Service Migration. Service Delete in Progress", log.Fields{"Device": deviceID, "Service": vs.Name, "Vnet": vs.VnetID})
+		return
+	}
+
+	metadata := vs.Metadata.(*MigrateServiceMetadata)
+	oldVnetID := vs.VnetID
+	nvs.VnetID = metadata.NewVnetID
+	id := metadata.RequestID
+	oldSrvName := vs.Name
+
+	if metadata == nil || metadata.NewVnetID == "" {
+		logger.Errorw(ctx, "Migrate Service Metadata not found. Dropping vnet profile update request", log.Fields{"Service": vs.Name, "Vnet": vs.VnetID})
+		return
+	}
+
+	//First add the new service and then only delete the old service
+	// Since if post del service in case of pod crash or reboot, the service data will be lost
+	va := GetApplication()
+	msr := va.getMigrateServicesRequest(deviceID, oldVnetID, id)
+	vnets := strings.Split(metadata.NewVnetID, "-")
+	svlan, _ := strconv.Atoi(vnets[0])
+	nvs.SVlan = of.VlanType(svlan)
+	nvs.UpdateInProgress = false
+	nvs.Metadata = nil
+	nvs.Trigger = ServiceVlanUpdate
+
+	svcName := vs.Port + "-" + strconv.FormatUint(uint64(nvs.SVlan), 10) + "-"
+	svcName = svcName + strconv.FormatUint(uint64(vs.CVlan), 10) + "-"
+	nvs.Name = svcName + strconv.FormatUint(uint64(vs.TechProfileID), 10)
+
+	//TODO:Nav Pass a copy, not the pointer
+	logger.Infow(ctx, "Add New Service Triggering", log.Fields{"Service": nvs.Name, "US": nvs.UsHSIAFlowsApplied, "DS": nvs.DsHSIAFlowsApplied, "DelFlag": nvs.DeleteInProgress})
+	if err := va.AddService(nvs.VoltServiceCfg, &nvs.VoltServiceOper); err != nil {
+		logger.Warnw(ctx, "Add New Service Failed", log.Fields{"Service": nvs.Name, "Error": err})
+	}
+	logger.Infow(ctx, "Add New Service Triggered", log.Fields{"Service": nvs.Name, "US": nvs.UsHSIAFlowsApplied, "DS": nvs.DsHSIAFlowsApplied, "DelFlag": nvs.DeleteInProgress})
+
+	msr.ServicesList[oldSrvName] = true
+	va.updateMigrateServicesRequest(deviceID, oldVnetID, id, msr)
+	msr.WriteToDB()
+
+	logger.Infow(ctx, "Del Old Service Triggering", log.Fields{"Service": oldSrvName, "US": vs.UsHSIAFlowsApplied, "DS": vs.DsHSIAFlowsApplied, "DelFlag": vs.DeleteInProgress})
+	va.DelService(oldSrvName, true, nil, true)
+	logger.Infow(ctx, "Del Old Service Triggered", log.Fields{"Service": oldSrvName, "US": vs.UsHSIAFlowsApplied, "DS": vs.DsHSIAFlowsApplied, "DelFlag": vs.DeleteInProgress})
+	msr.serviceMigrated(oldSrvName)
+}
+
+//serviceMigrated - called on successful service updation
+// Removes the service entry from servicelist and deletes the request on process completion
+func (msr *MigrateServicesRequest) serviceMigrated(serviceName string) {
+
+	msr.MigrateServicesLock.Lock()
+	defer msr.MigrateServicesLock.Unlock()
+
+	delete(msr.ServicesList, serviceName)
+
+	if len(msr.ServicesList) == 0 {
+		_ = db.DelMigrateServicesReq(msr.DeviceID, msr.GetMsrKey())
+		return
+	}
+	msr.WriteToDB()
+	//TODO:Nav - Need for any Response to SubMgr?
+}
+
+//TriggerPendingMigrateServicesReq - trigger pending service request
+func (va *VoltApplication) TriggerPendingMigrateServicesReq(device string) {
+	va.FetchAndProcessAllMigrateServicesReq(device, storeAndProcessMigrateSrvRequest)
+}
+
+//FetchAndProcessAllMigrateServicesReq - fetch all pending migrate services req from DB and process based on provided func
+func (va *VoltApplication) FetchAndProcessAllMigrateServicesReq(device string, msrAction func(*MigrateServicesRequest)) {
+
+	msrList, _ := db.GetAllMigrateServicesReq(device)
+	for _, msr := range msrList {
+		b, ok := msr.Value.([]byte)
+		if !ok {
+			logger.Warn(ctx, "The value type is not []byte")
+			continue
+		}
+		msr := va.createMigrateServicesFromString(b)
+		msrAction(msr)
+		logger.Warnw(ctx, "Triggering Pending Migrate Services Req", log.Fields{"OldVnet": msr.OldVnetID, "NewVnet": msr.NewVnetID, "Device": device, "PendingProfiles": len(msr.ServicesList)})
+
+	}
+}
+
+// createMigrateServicesFromString to create Service from string
+func (va *VoltApplication) createMigrateServicesFromString(b []byte) *MigrateServicesRequest {
+	var msr MigrateServicesRequest
+	if err := json.Unmarshal(b, &msr); err == nil {
+		logger.Debugw(ctx, "Adding Migrate Services Request From Db", log.Fields{"Vlan": msr.OldVnetID})
+
+	} else {
+		logger.Warn(ctx, "Unmarshal failed")
+	}
+	return &msr
+}
+
+//storeAndProcessMigrateSrvRequest - stores the msr info in device obj and triggers req
+func storeAndProcessMigrateSrvRequest(msr *MigrateServicesRequest) {
+	d := GetApplication().GetDevice(msr.DeviceID)
+	d.AddMigratingServices(msr)
+	msr.ProcessMigrateServicesProfRequest()
+}
+
+//forceUpdateAllServices - force udpate services with new vnet profile
+func forceUpdateAllServices(msr *MigrateServicesRequest) {
+	for srv := range msr.ServicesList {
+		if vsIntf, ok := GetApplication().ServiceByName.Load(srv); ok {
+			vsIntf.(*VoltService).updateVnetProfile(msr.DeviceID)
+		}
+	}
+	_ = db.DelMigrateServicesReq(msr.DeviceID, msr.GetMsrKey())
+}
+
+//DeepEqualServicecfg - checks if the given service cfgs are same
+func (va *VoltApplication) DeepEqualServicecfg(evs *VoltServiceCfg, nvs *VoltServiceCfg) bool {
+	if nvs.Name != evs.Name {
+		return false
+	}
+	if nvs.UniVlan != evs.UniVlan {
+		return false
+	}
+	if nvs.CVlan != evs.CVlan {
+		return false
+	}
+	if nvs.SVlan != evs.SVlan {
+		return false
+	}
+	if nvs.SVlanTpid != 0 && nvs.SVlanTpid != evs.SVlanTpid {
+		return false
+	}
+	if !util.IsPbitSliceSame(nvs.Pbits, evs.Pbits) {
+		return false
+	}
+	if !reflect.DeepEqual(nvs.DsRemarkPbitsMap, evs.DsRemarkPbitsMap) {
+		return false
+	}
+	if nvs.TechProfileID != evs.TechProfileID {
+		return false
+	}
+	if nvs.CircuitID != evs.CircuitID {
+		return false
+	}
+	if !bytes.Equal(nvs.RemoteID, evs.RemoteID) {
+		return false
+	}
+	if nvs.Port != evs.Port {
+		return false
+	}
+	if nvs.PonPort != evs.PonPort {
+		return false
+	}
+	if evs.MacLearning == MacLearningNone && !util.MacAddrsMatch(nvs.MacAddr, evs.MacAddr) {
+		return false
+	}
+	if nvs.IsOption82Disabled != evs.IsOption82Disabled {
+		return false
+	}
+	if nvs.IgmpEnabled != evs.IgmpEnabled {
+		return false
+	}
+	if nvs.McastService != evs.McastService {
+		return false
+	}
+	if nvs.ONTEtherTypeClassification != evs.ONTEtherTypeClassification {
+		return false
+	}
+	if nvs.UsMeterProfile != evs.UsMeterProfile {
+		return false
+	}
+	if nvs.DsMeterProfile != evs.DsMeterProfile {
+		return false
+	}
+	if nvs.AggDsMeterProfile != evs.AggDsMeterProfile {
+		return false
+	}
+	if nvs.VnetID != evs.VnetID {
+		return false
+	}
+	if nvs.MvlanProfileName != evs.MvlanProfileName {
+		return false
+	}
+	if nvs.RemoteIDType != evs.RemoteIDType {
+		return false
+	}
+	if nvs.SchedID != evs.SchedID {
+		return false
+	}
+	if nvs.AllowTransparent != evs.AllowTransparent {
+		return false
+	}
+	if nvs.EnableMulticastKPI != evs.EnableMulticastKPI {
+		return false
+	}
+	if nvs.DataRateAttr != evs.DataRateAttr {
+		return false
+	}
+	if nvs.MinDataRateUs != evs.MinDataRateUs {
+		return false
+	}
+	if nvs.MinDataRateDs != evs.MinDataRateDs {
+		return false
+	}
+	if nvs.MaxDataRateUs != evs.MaxDataRateUs {
+		return false
+	}
+	if nvs.MaxDataRateDs != evs.MaxDataRateDs {
+		return false
+	}
+
+	return true
+}
+
+//TriggerAssociatedFlowDelete - re-trigger service flow delete for pending delete flows
+func (vs *VoltService) TriggerAssociatedFlowDelete() bool {
+
+	//Clear the Flows flag if already set
+	//This case happens only in case of some race condition
+	if vs.UsHSIAFlowsApplied {
+		if err := vs.DelUsHsiaFlows(); err != nil {
+			logger.Errorw(ctx, "DelUsHsiaFlows Failed", log.Fields{"Device": vs.Device, "Service": vs.Name, "Error": err})
+		}
+	}
+
+	if vs.DsHSIAFlowsApplied {
+		if err := vs.DelDsHsiaFlows(); err != nil {
+			logger.Errorw(ctx, "DelDsHsiaFlows Failed", log.Fields{"Device": vs.Device, "Service": vs.Name, "Error": err})
+		}
+	}
+
+	vs.ServiceLock.Lock()
+	cookieList := []uint64{}
+	for cookie := range vs.AssociatedFlows {
+		cookieList = append(cookieList, convertToUInt64(cookie))
+	}
+	vs.ServiceLock.Unlock()
+
+	if len(cookieList) == 0 {
+		return false
+	}
+
+	//Trigger Flow Delete
+	for _, cookie := range cookieList {
+		if vd := GetApplication().GetDevice(vs.Device); vd != nil {
+			flow := &of.VoltFlow{}
+			flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+			subFlow := of.NewVoltSubFlow()
+			subFlow.Cookie = cookie
+			flow.SubFlows[cookie] = subFlow
+			logger.Infow(ctx, "Retriggering Service Delete Flow", log.Fields{"Device": vs.Device, "Service": vs.Name, "Cookie": cookie})
+			if err := vs.DelFlows(vd, flow); err != nil {
+				logger.Errorw(ctx, "DelFlows Failed", log.Fields{"Device": vs.Device, "Service": vs.Name, "Cookie": cookie, "Error": err})
+			}
+		}
+	}
+	return true
+}
+
+//triggerServiceInProgressInd - Indication is generated when Service is not provisioned after add serviec req from NB
+func (vs *VoltService) triggerServiceInProgressInd() {
+}