[ONOS-7051] Support for P4Runtime meters

Change-Id: Id71374af65aeb84b71636b4ec230dc6001a77a8b
diff --git a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
index 5d7965b..04e3e56 100644
--- a/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
+++ b/core/net/src/main/java/org/onosproject/net/meter/impl/MeterManager.java
@@ -30,6 +30,7 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.MeterCellId.MeterCellType;
 import org.onosproject.net.meter.Meter;
 import org.onosproject.net.meter.MeterEvent;
 import org.onosproject.net.meter.MeterFailReason;
@@ -132,10 +133,10 @@
                     }
                 });
 
-            };
+        };
 
         executorService = newFixedThreadPool(numThreads,
-                groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
+                                             groupedThreads(GROUP_THREAD_NAME, WORKER_PATTERN, log));
         modified(context);
         log.info("Started");
     }
@@ -146,7 +147,7 @@
             readComponentConfiguration(context);
         }
         defaultProvider.init(deviceService, createProviderService(defaultProvider),
-                mastershipService, fallbackMeterPollFrequency);
+                             mastershipService, fallbackMeterPollFrequency);
     }
 
     @Deactivate
@@ -289,7 +290,7 @@
                     // The meter is missing in the device. Reinstall!
                     log.debug("Adding meter missing in device {} {}", deviceId, m);
                     provider().performMeterOperation(deviceId,
-                            new MeterOperation(m, MeterOperation.Type.ADD));
+                                                     new MeterOperation(m, MeterOperation.Type.ADD));
                 }
             });
 
@@ -297,12 +298,19 @@
             meterEntriesMap.entrySet().stream()
                     .filter(md -> !allMeters.stream().anyMatch(m -> m.id().equals(md.getKey())))
                     .forEach(mio -> {
-                        // The meter is missin in onos. Uninstall!
-                        log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
                         Meter meter = mio.getValue();
-                        provider().performMeterOperation(deviceId,
-                                new MeterOperation(meter, MeterOperation.Type.REMOVE));
-            });
+                        // FIXME: Removing a meter is meaningfull for OpenFlow, but not for P4Runtime.
+                        // In P4Runtime meter cells cannot be removed. For the
+                        // moment, we make the distinction between OpenFlow and
+                        // P4Runtime by looking at the MeterCellType (always
+                        // INDEX for OpenFlow).
+                        if (meter.meterCellId().type() == MeterCellType.INDEX) {
+                            // The meter is missing in onos. Uninstall!
+                            log.debug("Remove meter in device not in onos {} {}", deviceId, mio.getKey());
+                            provider().performMeterOperation(deviceId,
+                                                             new MeterOperation(meter, MeterOperation.Type.REMOVE));
+                        }
+                    });
 
             meterEntries.stream()
                     .filter(m -> allMeters.stream()
@@ -339,11 +347,11 @@
             switch (event.type()) {
                 case METER_ADD_REQ:
                     executorService.execute(new MeterInstaller(deviceId, event.subject(),
-                            MeterOperation.Type.ADD));
+                                                               MeterOperation.Type.ADD));
                     break;
                 case METER_REM_REQ:
                     executorService.execute(new MeterInstaller(deviceId, event.subject(),
-                            MeterOperation.Type.REMOVE));
+                                                               MeterOperation.Type.REMOVE));
                     break;
                 case METER_ADDED:
                     log.info("Meter added {}", event.subject());
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
new file mode 100644
index 0000000..3831699
--- /dev/null
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiMeterTranslatorImpl.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2017-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 org.onosproject.net.pi.impl;
+
+import org.onosproject.net.Device;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.pi.model.PiPipeconf;
+import org.onosproject.net.pi.runtime.PiMeterBand;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
+import org.onosproject.net.pi.runtime.PiMeterCellId;
+import org.onosproject.net.pi.service.PiTranslationException;
+
+import static org.onosproject.net.meter.MeterCellId.MeterCellType.PIPELINE_INDEPENDENT;
+
+/**
+ * Implementation of meter translation logic.
+ */
+final class PiMeterTranslatorImpl {
+
+    private PiMeterTranslatorImpl() {
+        // Hides constructor.
+    }
+
+    private static final int TRTCM_RATES = 2;
+
+    /**
+     * Returns a PI meter config equivalent to the given meter, for the given pipeconf and device.
+     *
+     * @param meter    meter
+     * @param pipeconf pipeconf
+     * @param device   device
+     * @return PI meter configs
+     * @throws PiTranslationException if the meter cannot be translated
+     */
+    static PiMeterCellConfig translate(Meter meter, PiPipeconf pipeconf, Device device) throws PiTranslationException {
+
+        if (meter.meterCellId().type() != PIPELINE_INDEPENDENT) {
+            throw new PiTranslationException("PI meter cell type must be PIPELINE_INDEPENDENT!");
+        }
+
+        // FIXME: we might want to move this check to P4Runtime driver or protocol layer.
+        // In general, This check is more of P4Runtime limitation, we should do this check in the low level layer.
+        if (meter.bands().size() > TRTCM_RATES) {
+            throw new PiTranslationException("PI meter can not have more than 2 bands!");
+        }
+
+
+        PiMeterCellConfig.Builder builder = PiMeterCellConfig.builder();
+        for (Band band : meter.bands()) {
+            if (band.type() != Band.Type.NONE) {
+                throw new PiTranslationException("PI meter can not have band with other types except NONE!");
+            }
+
+            PiMeterBand piMeterBand = new PiMeterBand(band.rate(), band.burst());
+            builder.withMeterBand(piMeterBand);
+        }
+
+        return builder.withMeterCellId((PiMeterCellId) meter.meterCellId()).build();
+    }
+}
diff --git a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
index 9780fac..c69ced9 100644
--- a/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
+++ b/core/net/src/main/java/org/onosproject/net/pi/impl/PiTranslationServiceImpl.java
@@ -27,13 +27,17 @@
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.group.Group;
+import org.onosproject.net.meter.Meter;
 import org.onosproject.net.pi.model.PiPipeconf;
 import org.onosproject.net.pi.runtime.PiActionGroup;
+import org.onosproject.net.pi.runtime.PiMeterCellConfig;
 import org.onosproject.net.pi.runtime.PiTableEntry;
 import org.onosproject.net.pi.service.PiFlowRuleTranslationStore;
 import org.onosproject.net.pi.service.PiFlowRuleTranslator;
 import org.onosproject.net.pi.service.PiGroupTranslationStore;
 import org.onosproject.net.pi.service.PiGroupTranslator;
+import org.onosproject.net.pi.service.PiMeterTranslationStore;
+import org.onosproject.net.pi.service.PiMeterTranslator;
 import org.onosproject.net.pi.service.PiTranslationException;
 import org.onosproject.net.pi.service.PiTranslationService;
 import org.slf4j.Logger;
@@ -59,13 +63,18 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     private PiGroupTranslationStore groupTranslationStore;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    private PiMeterTranslationStore meterTranslationStore;
+
     private PiFlowRuleTranslator flowRuleTranslator;
     private PiGroupTranslator groupTranslator;
+    private PiMeterTranslator meterTranslator;
 
     @Activate
     public void activate() {
         flowRuleTranslator = new InternalFlowRuleTranslator(flowRuleTranslationStore);
         groupTranslator = new InternalGroupTranslator(groupTranslationStore);
+        meterTranslator = new InternalMeterTranslator(meterTranslationStore);
         log.info("Started");
     }
 
@@ -73,6 +82,7 @@
     public void deactivate() {
         flowRuleTranslator = null;
         groupTranslator = null;
+        meterTranslator = null;
         log.info("Stopped");
     }
 
@@ -86,6 +96,11 @@
         return groupTranslator;
     }
 
+    @Override
+    public PiMeterTranslator meterTranslator() {
+        return meterTranslator;
+    }
+
     private Device getDevice(DeviceId deviceId) throws PiTranslationException {
         final Device device = deviceService.getDevice(deviceId);
         if (device == null) {
@@ -125,5 +140,21 @@
                     .translate(original, pipeconf, getDevice(original.deviceId()));
         }
     }
+
+    private final class InternalMeterTranslator
+            extends AbstractPiTranslatorImpl<Meter, PiMeterCellConfig>
+            implements PiMeterTranslator {
+
+        private InternalMeterTranslator(PiMeterTranslationStore store) {
+            super(store);
+        }
+
+        @Override
+        public PiMeterCellConfig translate(Meter original, PiPipeconf pipeconf)
+                throws PiTranslationException {
+            return PiMeterTranslatorImpl
+                    .translate(original, pipeconf, getDevice(original.deviceId()));
+        }
+    }
 }