VOL-1623-meter support and handling  techprofile and fix for flow delete , now migrated to onosproject/onos:1.13.9-rc4

Change in flowupdate API towards adapters

Remove meter_get API from adapter to core

Added dependent vendor library files downloaded  by "dep-ensure -update"

Added techprofile changes in the single commit

Review comments are addressed

submiting patch for  integration tests for meter changes and modifications in unit test for updated flow decomposer logic
  - submitting on behalf of "Salman.Siddiqui@radisys.com"

Load test for meter updated and other flow management test cases with meter
- Performed load test for 1K meters serially and parallely and added more TC in flow management

Rebased

Load test for meter updated and other flow management test cases with meter
- Performed load test for 1K meters serially and parallely and added more TC in flow management
- submitting on behalf of "Salman.Siddiqui@radisys.com"

pulled latest protos

verified EAPOL/DHCP/HSIA data with Edgecore OLT & TW ONT kit for one subcriber
verified delete/re-add is working end to end for the same subscriber

Change-Id: Idb232b7a0f05dc0c7e68266ac885740a3adff317
diff --git a/tests/core/flow_management_test.go b/tests/core/flow_management_test.go
index ebda7de..3a44c19 100644
--- a/tests/core/flow_management_test.go
+++ b/tests/core/flow_management_test.go
@@ -31,12 +31,15 @@
 	"google.golang.org/grpc/metadata"
 	"math"
 	"os"
+	"reflect"
+	"sync"
 	"testing"
 	"time"
 )
 
 var stub voltha.VolthaServiceClient
 var volthaSerialNumberKey string
+var mutex sync.Mutex
 
 /*
  This local "integration" test uses one RW-Core, one simulated_olt and one simulated_onu adapter to test flows
@@ -60,9 +63,11 @@
 var composePath string
 
 const (
-	GRPC_PORT        = 50057
-	NUM_OLTS         = 1
-	NUM_ONUS_PER_OLT = 4 // This should coincide with the number of onus per olt in adapters-simulated.yml file
+	GrpcPort        = 50057
+	NumOfOLTs       = 1
+	NumOfONUsPerOLT = 4 // This should coincide with the number of onus per olt in adapters-simulated.yml file
+	MeterIDStart    = 1
+	MeterIDStop     = 1000
 )
 
 func setup() {
@@ -94,7 +99,7 @@
 		os.Exit(10)
 	}
 
-	stub, err = tu.SetupGrpcConnectionToCore(grpcHostIP, GRPC_PORT)
+	stub, err = tu.SetupGrpcConnectionToCore(grpcHostIP, GrpcPort)
 	if err != nil {
 		fmt.Println("Failure connecting to Voltha Core:", err)
 		os.Exit(11)
@@ -120,10 +125,10 @@
 	if err != nil {
 		return err
 	}
+	mutex.Lock()
 	for _, d := range retrievedDevices.Items {
 		allDevices[d.Id] = d
 	}
-
 	retrievedLogicalDevices, err := tu.ListLogicalDevices(stub)
 	if err != nil {
 		return err
@@ -132,6 +137,7 @@
 	for _, ld := range retrievedLogicalDevices.Items {
 		allLogicalDevices[ld.Id] = ld
 	}
+	mutex.Unlock()
 	return nil
 }
 
@@ -143,10 +149,11 @@
 	return fu.MkSimpleFlowMod(matchFields, fa.Actions, fa.Command, fa.KV)
 }
 
-func addEAPOLFlow(stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, port *voltha.LogicalPort, ch chan interface{}) {
+func addEAPOLFlow(stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, port *voltha.LogicalPort, meterID uint64,
+	ch chan interface{}) {
 	var fa *fu.FlowArgs
 	fa = &fu.FlowArgs{
-		KV: fu.OfpFlowModArgs{"priority": 2000},
+		KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
 		MatchFields: []*ofp.OfpOxmOfbField{
 			fu.InPort(port.OfpPort.PortNo),
 			fu.EthType(0x888e),
@@ -155,6 +162,11 @@
 			fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
 		},
 	}
+	// Don't add meterID 0
+	if meterID == 0 {
+		delete(fa.KV, "meter_id")
+	}
+
 	matchFields := make([]*ofp.OfpOxmField, 0)
 	for _, val := range fa.MatchFields {
 		matchFields = append(matchFields, &ofp.OfpOxmField{Field: &ofp.OfpOxmField_OfbField{OfbField: val}})
@@ -202,8 +214,9 @@
 	return true
 }
 
-func verifyEAPOLFlows(t *testing.T, ld *voltha.LogicalDevice, lPortNos ...uint32) {
+func verifyEAPOLFlows(t *testing.T, ld *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) {
 	// First get the flows from the logical device
+	fmt.Println("Info: verifying EAPOL flows")
 	lFlows := ld.Flows
 	assert.Equal(t, getNumUniPort(ld, lPortNos...), len(lFlows.Items))
 
@@ -220,7 +233,7 @@
 		onuDeviceId = lPort.DeviceId
 		var fa *fu.FlowArgs
 		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": 2000},
+			KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
 			MatchFields: []*ofp.OfpOxmOfbField{
 				fu.InPort(lPort.OfpPort.PortNo),
 				fu.EthType(0x888e),
@@ -229,13 +242,16 @@
 				fu.Output(uint32(ofp.OfpPortNo_OFPP_CONTROLLER)),
 			},
 		}
+		if meterID == 0 {
+			delete(fa.KV, "meter_id")
+		}
 		expectedLdFlow := fu.MkFlowStat(fa)
 		assert.Equal(t, true, tu.IsFlowPresent(expectedLdFlow, lFlows.Items))
 	}
 
 	//	Verify the OLT flows
 	retrievedOltFlows := allDevices[ld.RootDeviceId].Flows.Items
-	assert.Equal(t, NUM_OLTS*getNumUniPort(ld, lPortNos...)*2, len(retrievedOltFlows))
+	assert.Equal(t, NumOfOLTs*getNumUniPort(ld, lPortNos...)*1, len(retrievedOltFlows))
 	for _, lPort := range ld.Ports {
 		if lPort.RootPort {
 			continue
@@ -245,10 +261,9 @@
 		}
 
 		fa := &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": 2000},
+			KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
 			MatchFields: []*ofp.OfpOxmOfbField{
 				fu.InPort(1),
-				fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | lPort.OfpPort.PortNo),
 				fu.TunnelId(uint64(lPort.OfpPort.PortNo)),
 				fu.EthType(0x888e),
 			},
@@ -260,23 +275,6 @@
 		}
 		expectedOltFlow := fu.MkFlowStat(fa)
 		assert.Equal(t, true, tu.IsFlowPresent(expectedOltFlow, retrievedOltFlows))
-
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": 2000},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				fu.InPort(2),
-				fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 4000),
-				fu.VlanPcp(0),
-				fu.Metadata_ofp(uint64(lPort.OfpPort.PortNo)),
-				fu.TunnelId(uint64(lPort.OfpPort.PortNo)),
-			},
-			Actions: []*ofp.OfpAction{
-				fu.PopVlan(),
-				fu.Output(1),
-			},
-		}
-		expectedOltFlow = fu.MkFlowStat(fa)
-		assert.Equal(t, true, tu.IsFlowPresent(expectedOltFlow, retrievedOltFlows))
 	}
 	//	Verify the ONU flows
 	retrievedOnuFlows := allDevices[onuDeviceId].Flows.Items
@@ -311,7 +309,7 @@
 
 }
 
-func installEapolFlows(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, lPortNos ...uint32) error {
+func installEapolFlows(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) error {
 	requestNum := 0
 	combineCh := make(chan interface{})
 	if len(lPortNos) > 0 {
@@ -319,20 +317,19 @@
 		for _, p := range lPortNos {
 			for _, lport := range lDevice.Ports {
 				if !lport.RootPort && lport.OfpPort.PortNo == p {
-					go addEAPOLFlow(stub, lDevice, lport, combineCh)
+					go addEAPOLFlow(stub, lDevice, lport, meterID, combineCh)
 					requestNum += 1
 				}
 			}
 		}
 	} else {
-		fmt.Println("Installing EAPOL flows on logical device ", lDevice.Id)
+		fmt.Println("Installing EAPOL flows on logical device", lDevice.Id)
 		for _, lport := range lDevice.Ports {
 			if !lport.RootPort {
-				go addEAPOLFlow(stub, lDevice, lport, combineCh)
+				go addEAPOLFlow(stub, lDevice, lport, meterID, combineCh)
 				requestNum += 1
 			}
 		}
-
 	}
 	receivedResponse := 0
 	var err error
@@ -373,13 +370,13 @@
 	return err
 }
 
-func deleteEapolFlow(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, lPortNo uint32) error {
+func deleteEapolFlow(stub voltha.VolthaServiceClient, lDevice *voltha.LogicalDevice, meterID uint64, lPortNo uint32) error {
 	fmt.Println("Deleting flows from port ", lPortNo, " of logical device ", lDevice.Id)
 	ui := uuid.New()
 	var fa *fu.FlowArgs
 	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(volthaSerialNumberKey, ui.String()))
 	fa = &fu.FlowArgs{
-		KV: fu.OfpFlowModArgs{"priority": 2000},
+		KV: fu.OfpFlowModArgs{"priority": 2000, "meter_id": meterID},
 		MatchFields: []*ofp.OfpOxmOfbField{
 			fu.InPort(lPortNo),
 			fu.EthType(0x888e),
@@ -399,20 +396,19 @@
 	return err
 }
 
-func runInstallEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, lPortNos ...uint32) {
+func runInstallEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, meterID uint64, lPortNos ...uint32) {
 	err := refreshLocalDeviceCache(stub)
 	assert.Nil(t, err)
 
 	for _, ld := range allLogicalDevices {
-		err = installEapolFlows(stub, ld, lPortNos...)
+		err = installEapolFlows(stub, ld, meterID, lPortNos...)
 		assert.Nil(t, err)
 	}
 
 	err = refreshLocalDeviceCache(stub)
 	assert.Nil(t, err)
-
 	for _, ld := range allLogicalDevices {
-		verifyEAPOLFlows(t, ld, lPortNos...)
+		verifyEAPOLFlows(t, ld, meterID, lPortNos...)
 	}
 }
 
@@ -434,7 +430,7 @@
 	}
 }
 
-func runDeleteEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, lPortNos ...uint32) {
+func runDeleteEapolFlows(t *testing.T, stub voltha.VolthaServiceClient, ld *voltha.LogicalDevice, meterID uint64, lPortNos ...uint32) {
 	err := refreshLocalDeviceCache(stub)
 	assert.Nil(t, err)
 
@@ -443,7 +439,7 @@
 		assert.Nil(t, err)
 	} else {
 		for _, lPortNo := range lPortNos {
-			err = deleteEapolFlow(stub, ld, lPortNo)
+			err = deleteEapolFlow(stub, ld, meterID, lPortNo)
 			assert.Nil(t, err)
 		}
 	}
@@ -459,6 +455,173 @@
 	}
 }
 
+func formulateMeterModUpdateRequest(command ofp.OfpMeterModCommand, meterType ofp.OfpMeterBandType, ldID string, rate,
+	burstSize uint32, meterID uint32) *ofp.MeterModUpdate {
+	meterModUpdateRequest := &ofp.MeterModUpdate{
+		Id: ldID,
+		MeterMod: &ofp.OfpMeterMod{
+			Command: command,
+			Flags:   0,
+			MeterId: meterID,
+			Bands: []*ofp.OfpMeterBandHeader{{
+				Type:      meterType,
+				Rate:      rate,
+				BurstSize: burstSize,
+				Data:      nil,
+			}},
+		},
+	}
+	return meterModUpdateRequest
+}
+
+func formulateMeters(rate, burstsize, meterID uint32, flowCount uint32) *ofp.Meters {
+	// Formulate and return the applied meter band
+	ofpMeterBandHeaderSlice := []*ofp.OfpMeterBandHeader{{
+		Type:      ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER,
+		Rate:      rate,
+		BurstSize: burstsize,
+	}}
+	BandStats := []*ofp.OfpMeterBandStats{{}}
+	appliedMeters_Meters := []*ofp.OfpMeterEntry{{
+		Config: &ofp.OfpMeterConfig{
+			Flags:   0,
+			MeterId: meterID,
+			Bands:   ofpMeterBandHeaderSlice,
+		},
+		Stats: &ofp.OfpMeterStats{
+			MeterId:   meterID,
+			BandStats: BandStats,
+			FlowCount: flowCount,
+		},
+	}}
+	appliedMeters := &ofp.Meters{
+		Items: appliedMeters_Meters,
+	}
+	return appliedMeters
+}
+
+func meterAdd(t *testing.T, meterID, rate, burstSize uint32) {
+	var err error
+	for _, logicalDevice := range allLogicalDevices {
+		meterModupdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_ADD,
+			ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, rate, burstSize, meterID)
+		_, err = stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModupdateRequest)
+		assert.Nil(t, err)
+	}
+}
+
+func meterMod(t *testing.T, meterID, rate, burstSize uint32) {
+	for _, logicalDevice := range allLogicalDevices {
+		meterModUpdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_MODIFY,
+			ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, rate, burstSize, meterID)
+		_, err := stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModUpdateRequest)
+		assert.Nil(t, err)
+	}
+}
+
+//MeterDel deletes a meter with given meter-ID
+func meterDel(t *testing.T, meterID uint32) {
+	for _, logicalDevice := range allLogicalDevices {
+		meterModUpdateRequest := formulateMeterModUpdateRequest(ofp.OfpMeterModCommand_OFPMC_DELETE,
+			ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER, logicalDevice.Id, uint32(1600), uint32(1600), meterID)
+		_, err := stub.UpdateLogicalDeviceMeterTable(context.Background(), meterModUpdateRequest)
+		assert.Nil(t, err)
+	}
+}
+
+// WaitGroup passed to this function must not be nil
+func addMultipleMeterSequentially(t *testing.T, startMeterId, endMeterId, rate, burstSize uint32, wg *sync.WaitGroup) {
+	defer wg.Done()
+	for meterID := startMeterId; meterID <= endMeterId; meterID++ {
+		meterAdd(t, meterID, rate, burstSize)
+	}
+}
+
+func modMultipleMeterSequentially(t *testing.T, startMeterId, endMeterId, rate, burstSize uint32, wg *sync.WaitGroup) {
+	defer wg.Done()
+	for meterID := startMeterId; meterID <= endMeterId; meterID++ {
+		meterMod(t, meterID, rate, burstSize)
+	}
+}
+
+func delMultipleMeterSequential(t *testing.T, startMeterId, endMeterId uint32, wg *sync.WaitGroup) {
+	defer wg.Done()
+	for meterID := startMeterId; meterID <= endMeterId; meterID++ {
+		meterDel(t, meterID)
+	}
+}
+
+func verifyMeter(t *testing.T, meterID, rate, burstSize, flowCount uint32) {
+	expectedMeter := formulateOfpMeterEntry(meterID, flowCount, rate, burstSize)
+	isMeterPresent := false
+	for _, lD := range allLogicalDevices {
+		for _, meter := range lD.Meters.Items {
+			isMeterPresent = reflect.DeepEqual(meter, expectedMeter)
+			if isMeterPresent == true {
+				break
+			}
+		}
+		if isMeterPresent {
+			break
+		}
+	}
+	if !isMeterPresent {
+		fmt.Printf("Error : Expected %+v\n", expectedMeter)
+	}
+	assert.Equal(t, true, isMeterPresent)
+}
+
+func verifyNoMeters(t *testing.T) {
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	expectedMeters := &ofp.Meters{}
+	for _, logicalDevice := range allLogicalDevices {
+		result := reflect.DeepEqual(logicalDevice.Meters, expectedMeters)
+		fmt.Println("Meter Present After Delete :--- ", logicalDevice.Meters)
+		assert.Equal(t, true, result)
+	}
+}
+
+func formulateOfpMeterEntry(meterID, flowCount, rate, burstSize uint32) *ofp.OfpMeterEntry {
+	value := &ofp.OfpMeterEntry{
+		Config: &ofp.OfpMeterConfig{
+			MeterId: meterID,
+			Bands: []*ofp.OfpMeterBandHeader{{
+				Type:      ofp.OfpMeterBandType_OFPMBT_EXPERIMENTER,
+				Rate:      rate,
+				BurstSize: burstSize,
+			}},
+		},
+		Stats: &ofp.OfpMeterStats{
+			MeterId:   meterID,
+			FlowCount: flowCount,
+			BandStats: []*ofp.OfpMeterBandStats{{}},
+		},
+	}
+
+	return value
+}
+
+func deleteAllMeters(t *testing.T) {
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	for _, lD := range allLogicalDevices {
+		for _, meter := range lD.Meters.Items {
+			meterDel(t, meter.Config.MeterId)
+		}
+	}
+}
+
+func installMultipleFlowsWithMeter(t *testing.T, startMeterId, stopMeterId uint64, wg *sync.WaitGroup) {
+	defer wg.Done()
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	for meterID := startMeterId; meterID <= stopMeterId; meterID++ {
+		runInstallEapolFlows(t, stub, uint64(meterID), 100, 101)
+	}
+}
+
 func createAndEnableDevices(t *testing.T) {
 	err := tu.SetAllLogLevel(stub, voltha.Logging{Level: common.LogLevel_WARNING})
 	assert.Nil(t, err)
@@ -472,45 +635,270 @@
 	oltDevice, err := tu.PreProvisionDevice(stub)
 	assert.Nil(t, err)
 
-	fmt.Println("Creation of ", NUM_OLTS, " OLT devices took:", time.Since(startTime))
+	fmt.Println("Creation of ", NumOfOLTs, " OLT devices took:", time.Since(startTime))
 
 	startTime = time.Now()
 
 	//Enable all parent device - this will enable the child devices as well as validate the child devices
-	err = tu.EnableDevice(stub, oltDevice, NUM_ONUS_PER_OLT)
+	err = tu.EnableDevice(stub, oltDevice, NumOfONUsPerOLT)
 	assert.Nil(t, err)
 
 	fmt.Println("Enabling of  OLT device took:", time.Since(startTime))
 
 	// Wait until the core and adapters sync up after an enabled
-	time.Sleep(time.Duration(math.Max(10, float64(NUM_OLTS*NUM_ONUS_PER_OLT)/2)) * time.Second)
+	time.Sleep(time.Duration(math.Max(10, float64(NumOfOLTs*NumOfONUsPerOLT)/2)) * time.Second)
 
-	err = tu.VerifyDevices(stub, NUM_ONUS_PER_OLT)
+	err = tu.VerifyDevices(stub, NumOfONUsPerOLT)
 	assert.Nil(t, err)
 
-	lds, err := tu.VerifyLogicalDevices(stub, oltDevice, NUM_ONUS_PER_OLT)
+	lds, err := tu.VerifyLogicalDevices(stub, oltDevice, NumOfONUsPerOLT)
 	assert.Nil(t, err)
 	assert.Equal(t, 1, len(lds.Items))
 }
 
 func TestFlowManagement(t *testing.T) {
-	//1. Test creation and activation of the devices.  This will validate the devices as well as the logical device created/
+	//1. Test creation and activation of the devices.  This will validate the devices as well as the logical device created
 	createAndEnableDevices(t)
 
 	//2. Test installation of EAPOL flows
-	runInstallEapolFlows(t, stub)
+	runInstallEapolFlows(t, stub, 0)
 
 	//3. Test deletion of all EAPOL flows
 	runDeleteAllFlows(t, stub)
 
 	//4. Test installation of EAPOL flows on specific ports
-	runInstallEapolFlows(t, stub, 101, 102)
+	runInstallEapolFlows(t, stub, 0, 101, 102)
 
 	lds, err := tu.ListLogicalDevices(stub)
 	assert.Nil(t, err)
 
 	//5. Test deletion of EAPOL on a specific port for a given logical device
-	runDeleteEapolFlows(t, stub, lds.Items[0], 101)
+	runDeleteEapolFlows(t, stub, lds.Items[0], 0, 101)
+}
+
+// Meter Add Test
+func TestMeters(t *testing.T) {
+	//1. Start test for meter add
+	meterAdd(t, 1, 1600, 1600)
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	verifyMeter(t, 1, 1600, 1600, 0)
+
+	//2. Start test for meter mod
+	meterMod(t, 1, 6400, 6400)
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	verifyMeter(t, 1, 6400, 6400, 0)
+
+	//3. Start test for meter del
+	meterDel(t, 1)
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	verifyNoMeters(t)
+}
+
+func TestFlowManagementWithMeters(t *testing.T) {
+	//1. Delete existing flows
+	for _, logicaldevice := range allLogicalDevices {
+		err := deleteAllFlows(stub, logicaldevice)
+		assert.Nil(t, err)
+	}
+	deleteAllMeters(t)
+	//2. Refresh the local cache so that the changes are reflected here
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//3. Add meter with ID
+	meterAdd(t, 1, 1600, 1600)
+
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//4. Verify that the meter is installed
+	verifyMeter(t, 1, 1600, 1600, 0)
+
+	//3. Test installation of EAPOL flows with meter (Add and verify the flow)
+	runInstallEapolFlows(t, stub, MeterIDStart)
+
+	//4. Test deletion of all EAPOL flows
+	runDeleteAllFlows(t, stub)
+
+	//5. Test installation of EAPOL flows on specific ports with meter (Add and verify the flows)
+	runInstallEapolFlows(t, stub, MeterIDStart, 101, 102)
+
+	lds, err := tu.ListLogicalDevices(stub)
+	assert.Nil(t, err)
+
+	//6. Test deletion of EAPOL on a specific port for a given logical device
+	runDeleteEapolFlows(t, stub, lds.Items[0], MeterIDStart, 101)
+}
+
+func TestMultipleMeterAddSequential(t *testing.T) {
+	//1. Delete existing flows
+	for _, logicaldevice := range allLogicalDevices {
+		err := deleteAllFlows(stub, logicaldevice)
+		assert.Nil(t, err)
+	}
+
+	// Delete All The Previously Installed Meters
+	deleteAllMeters(t)
+	// Verify that no meters are present
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	verifyNoMeters(t)
+	//2. Add Meter Sequentially
+	// Here wait-group is not required, creating and passing wait-group only because the function addMultipleMeterSequentially
+	// expects wait group.
+	var wg sync.WaitGroup
+	wg.Add(1)
+	addMultipleMeterSequentially(t, MeterIDStart, MeterIDStop, 1600, 1600, &wg)
+
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//3. Verify that the meters are installed
+	for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
+		verifyMeter(t, uint32(meterID), 1600, 1600, 0)
+	}
+}
+
+func TestMeterDeleteParallel(t *testing.T) {
+	var wg sync.WaitGroup
+	wg.Add(4)
+	go delMultipleMeterSequential(t, MeterIDStart, MeterIDStop/4, &wg)
+	go delMultipleMeterSequential(t, MeterIDStop/4+1, (MeterIDStop / 2), &wg)
+	go delMultipleMeterSequential(t, (MeterIDStop/2)+1, MeterIDStop/4*3, &wg)
+	go delMultipleMeterSequential(t, (MeterIDStop/4*3)+1, MeterIDStop, &wg)
+	wg.Wait()
+
+	verifyNoMeters(t)
+}
+
+func TestMultipleMeterAddParallel(t *testing.T) {
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	for _, logicaldevice := range allLogicalDevices {
+		err := deleteAllFlows(stub, logicaldevice)
+		assert.Nil(t, err)
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(4)
+	go addMultipleMeterSequentially(t, MeterIDStart, (MeterIDStop / 4), 3200, 3200, &wg)
+	go addMultipleMeterSequentially(t, (MeterIDStop/4 + 1), MeterIDStop/2, 3200, 3200, &wg)
+	go addMultipleMeterSequentially(t, MeterIDStop/2+1, (MeterIDStop / 4 * 3), 3200, 3200, &wg)
+	go addMultipleMeterSequentially(t, (MeterIDStop/4*3)+1, MeterIDStop, 3200, 3200, &wg)
+
+	wg.Wait()
+
+	// Verify the devices
+	err = tu.VerifyDevices(stub, NumOfONUsPerOLT)
+	assert.Nil(t, err)
+
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
+		fmt.Println("Verifying meter ID :", meterID)
+		verifyMeter(t, uint32(meterID), 3200, 3200, 0)
+	}
+}
+
+func TestMultipleMeterModSequential(t *testing.T) {
+	var wg sync.WaitGroup
+	wg.Add(1)
+	//1. Modify all the existing meters
+	modMultipleMeterSequentially(t, MeterIDStart, MeterIDStop, 6400, 6400, &wg)
+	//2. Verify that the device state is proper after updating so many meters
+	err := tu.VerifyDevices(stub, NumOfONUsPerOLT)
+	assert.Nil(t, err)
+
+	//3. Refresh logical device cache
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//4. Verify that all the meters got modified
+	for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
+		fmt.Println("Verifying meter ID :", meterID)
+		verifyMeter(t, uint32(meterID), 6400, 6400, 0)
+	}
+}
+
+func TestMultipleMeterModParallel(t *testing.T) {
+	var wg sync.WaitGroup
+	wg.Add(4)
+	//1. Modify all the existing meters
+	go modMultipleMeterSequentially(t, MeterIDStart, MeterIDStop/4, 1600, 1600, &wg)
+	go modMultipleMeterSequentially(t, (MeterIDStop/4 + 1), MeterIDStop/2, 1600, 1600, &wg)
+	go modMultipleMeterSequentially(t, (MeterIDStop/2 + 1), (MeterIDStop / 4 * 3), 1600, 1600, &wg)
+	go modMultipleMeterSequentially(t, (MeterIDStop/4*3)+1, MeterIDStop, 1600, 1600, &wg)
+	//2. Verify that the device state is proper after updating so many meters
+	err := tu.VerifyDevices(stub, NumOfONUsPerOLT)
+	assert.Nil(t, err)
+	wg.Wait()
+
+	//3. Refresh logical device cache
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//4. Verify that all the meters got modified
+	for meterID := MeterIDStart; meterID <= MeterIDStop; meterID++ {
+		fmt.Println("Verifying meter ID :", meterID)
+		verifyMeter(t, uint32(meterID), 1600, 1600, 0)
+	}
+}
+
+func TestMultipleMeterDelSequential(t *testing.T) {
+	var wg sync.WaitGroup
+	wg.Add(1)
+	//1. Delete all the given meters sequentially
+	delMultipleMeterSequential(t, MeterIDStart, MeterIDStop, &wg)
+	//2. Verify that all the given meters are deleted
+	verifyNoMeters(t)
+}
+
+func TestMultipleFlowsWithMeters(t *testing.T) {
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//1. Delete all the previous flows
+	for _, lD := range allLogicalDevices {
+		deleteAllFlows(stub, lD)
+	}
+
+	//2. Add multiple meters for the flows to refer to
+	var wg sync.WaitGroup
+	wg.Add(2)
+	addMultipleMeterSequentially(t, MeterIDStart, 10, 1600, 1600, &wg)
+	// Adding meter verification here again will increase the time taken by the test. So leaving meter verification
+	installMultipleFlowsWithMeter(t, 1, 10, &wg)
+}
+
+func TestFlowDeletionWithMeterDelete(t *testing.T) {
+	err := refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+
+	//1. Delete all meters
+	deleteAllMeters(t)
+	//2. Verify that all the meters are deleted
+	verifyNoMeters(t)
+	//3. As no meters are present, so all flows referring to these meters should also be deleted
+	// Flow referring to meterID 1 - 10 were installed in the above test "TestMultipleFlowsWithMeters"
+	err = refreshLocalDeviceCache(stub)
+	assert.Nil(t, err)
+	for _, lD := range allLogicalDevices {
+		verifyNOFlows(t, lD, 100, 101)
+	}
+}
+
+func TestAddFlowWithInvalidMeter(t *testing.T) {
+	var err error
+	for _, ld := range allLogicalDevices {
+		// Adding flows with meter-ID which is not present should throw error stating -
+		// "Meter-referred-by-flow-is-not-found-in-logicaldevice"
+		err = installEapolFlows(stub, ld, 1, 100, 101)
+		assert.NotNil(t, err)
+	}
 }
 
 func TestMain(m *testing.M) {