Introducing BandwidthProfileConfigBehaviour to manage policers/markers

- Creating BandwidthProfile Class to represent generic policers/markers
- BandwidthProfile can be Single/Two Rate, Two/Three Color depending on parameters
- Including fromMeter() method to convert a Meter into a BandwidthProfile
- Adding unit tests for BandwidthProfile class
- Creating BandwidthProfileAction class to represent a color (green/yellow/red) action
- BandwidthProfileAction allows remarking one of: DSCP class, IP precedence, Drop precedence
- Adding unit tests for BandwidthProfileAction class
- Creating DscpClass and IPPrecedence Enums and unit tests in org.onlab.packet
- Adding kBps/MBps/GBps methods in org.onlab.util.Bandwidth class

Change-Id: I54156329a527ebd9165d8f55e03e0782925caa2b
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfile.java b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfile.java
new file mode 100644
index 0000000..2a934fd
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfile.java
@@ -0,0 +1,551 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.behaviour;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.DscpClass;
+import org.onlab.util.Bandwidth;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.Meter;
+import static org.onosproject.net.behaviour.BandwidthProfileAction.Action;
+
+import java.util.Iterator;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Implementation of a generic bandwidth profile (marker/policer).
+ */
+@Beta
+public final class BandwidthProfile {
+
+    /**
+     * Denotes the type of the bandwidth profile.
+     */
+    enum Type {
+        /**
+         * Corresponds to a Single Rate Two Color Marker/Policer.
+         */
+        sr2CM,
+
+        /**
+         * Corresponds to a Single Rate Three Color Marker/Policer.
+         * (IETF RFC 2697)
+         */
+        srTCM,
+
+        /**
+         * Corresponds to a Two Rate Three Color Marker/Policer.
+         * (IETF RFC 2698)
+         */
+        trTCM
+    }
+
+    private final String name;
+    private final Bandwidth cir;
+    private final Bandwidth pir;
+    private final Integer cbs;
+    private final Integer pbs;
+    private final Integer ebs;
+    private final BandwidthProfileAction greenAction;
+    private final BandwidthProfileAction yellowAction;
+    private final BandwidthProfileAction redAction;
+    private final boolean colorAware;
+
+    /**
+     * BandwidthProfile constructor.
+     *
+     * @param name the profile name
+     * @param cir the committed information rate (CIR)
+     * @param cbs the committed burst size (CBS) measured in bytes
+     * @param pir the peak information rate (PIR)
+     * @param pbs the peak burst size (PBS) measured in bytes
+     * @param greenAction the action to be taken for traffic that conforms
+     *                    to the CIR/CBS
+     * @param yellowAction srTCM: the action to be taken for traffic that
+     *                     conforms to the EBS but not to the CIR/CBS
+     *                     trTCM: the action to be taken for traffic that
+     *                     conforms to the PIR/PBS but not to the CIR/CBS
+     * @param redAction sr2CM: the action to be taken for traffic that
+     *                  does not conform to the CIR/CBS
+     *                  srTCM: the action to be taken for traffic that
+     *                  does not conform to the EBS
+     *                  trTCM: the action to be taken for traffic that
+     *                  does not conform to the PIR/PBS
+     * @param colorAware indicates whether the profile considers incoming
+     *                   traffic as already colored
+     */
+    private BandwidthProfile(String name,
+                             Bandwidth cir, Bandwidth pir,
+                             Integer cbs, Integer pbs, Integer ebs,
+                             BandwidthProfileAction greenAction,
+                             BandwidthProfileAction yellowAction,
+                             BandwidthProfileAction redAction,
+                             boolean colorAware) {
+        this.name = name;
+        this.cir = cir;
+        this.pir = pir;
+        this.cbs = cbs;
+        this.pbs = pbs;
+        this.ebs = ebs;
+        this.greenAction = greenAction;
+        this.yellowAction = yellowAction;
+        this.redAction = redAction;
+        this.colorAware = colorAware;
+    }
+
+    /**
+     * Obtains the name of this bandwidth profile.
+     *
+     * @return the bandwidth profile name
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Obtains the committed information rate (CIR) of this bandwidth profile.
+     *
+     * @return the CIR of the bandwidth profile
+     */
+    public Bandwidth cir() {
+        return cir;
+    }
+
+    /**
+     * Obtains the peak information rate (PIR) of this bandwidth profile.
+     * If this profile does not have a PIR, null is returned.
+     *
+     * @return the PIR of the profile; null if the profile does not have a PIR
+     */
+    public Bandwidth pir() {
+        return pir;
+    }
+
+    /**
+     * Obtains the committed burst size (CBS) of this bandwidth profile.
+     * The CBS is measured in bytes.
+     * If this profile does not have a CBS, null is returned.
+     *
+     * @return the CBS of the profile (bytes);
+     * null if the profile does not have a CBS
+     */
+    public Integer cbs() {
+        return cbs;
+    }
+
+    /**
+     * Obtains the peak burst size (PBS) of this bandwidth profile.
+     * The PBS is measured in bytes.
+     * If this profile does not have a PBS, null is returned.
+     *
+     * @return the PBS of the bandwidth profile (bytes);
+     * null if the profile does not have a PBS
+     */
+    public Integer pbs() {
+        return pbs;
+    }
+
+    /**
+     * Obtains the excess burst size (EBS) of this bandwidth profile.
+     * The EBS is measured in bytes.
+     * If this profile does not have an EBS, null is returned.
+     *
+     * @return the EBS of the bandwidth profile (bytes);
+     * null if the profile does not have an EBS
+     */
+    public Integer ebs() {
+        return ebs;
+    }
+
+    /**
+     * Obtains the action to be taken for traffic marked as green.
+     * Green color marking is applied to traffic that conforms to the CIR/CBS.
+     *
+     * @return the action to be taken for traffic marked as green
+     */
+    public BandwidthProfileAction greenAction() {
+        return greenAction;
+    }
+
+    /**
+     * Obtains the action to be taken for traffic marked as yellow.
+     * Yellow color marking is applied to traffic that does not conform
+     * to the CIR/CBS but conforms to one of:
+     * <ul>
+     *     <li>EBS (srTCM type)</li>
+     *     <li>PIR/PBS (trTCM type)</li>
+     * </ul>
+     * If this profile does has neither EBS or PIR/PBS, null is returned.
+     *
+     * @return the action to be taken for traffic marked as yellow;
+     * null if neither EBS nor PIR/PBS are defined
+     */
+    public BandwidthProfileAction yellowAction() {
+        return yellowAction;
+    }
+
+    /**
+     * Obtains the action to be taken for traffic marked as red.
+     * Red color marking is applied to traffic that does not conform
+     * to one of the following:
+     * <ul>
+     *     <li>CIR/CBS (sr2CM type)</li>
+     *     <li>EBS (srTCM type)</li>
+     *     <li>PIR/PBS (trTCM type)</li>
+     * </ul>
+     *
+     * @return the action to be taken for traffic marked as red
+     */
+    public BandwidthProfileAction redAction() {
+        return redAction;
+    }
+
+    /**
+     * Obtains the color-aware mode of the bandwidth profile.
+     *
+     * @return true if the bandwidth profile is color-aware; false otherwise
+     */
+    public boolean colorAware() {
+        return colorAware;
+    }
+
+    /**
+     * Obtains the bandwidth profile type depending on the profile parameters.
+     * <ul>
+     *     <li>When PIR is defined, the profile corresponds to a
+     *     Two Rate Three Color Marker (trTCM)</li>
+     *     <li>When EBS is defined, the profile corresponds to a
+     *     Single Rate Three Color Marker (srTCM)</li>
+     *     <li>When neither PIR nor EBS are defined, the profile corresponds to a
+     *     Single Rate Two Color Marker/Policer (sr2CM)</li>
+     * </ul>
+     *
+     * @return the bandwidth profile type
+     */
+    public Type type() {
+        return pir != null ? Type.trTCM :
+                ebs != null ? Type.srTCM : Type.sr2CM;
+    }
+
+    /**
+     * Creates a bandwidth profile based on the parameters of a Meter.
+     * NOTE: The dropPrecedence in the Meter is interpreted as
+     * the DSCP class to set on the packet
+     *
+     * @param meter the Meter to be used for creating the bandwidth profile
+     * @return the bandwidth profile created
+     */
+    public static BandwidthProfile fromMeter(Meter meter) {
+
+        checkNotNull(meter);
+        checkArgument(meter.bands().size() <= 2,
+                      "Meter must have no more than two bands.");
+
+        Iterator<Band> bandIterator = meter.bands().iterator();
+        Band bandOne = bandIterator.next();
+        Band bandTwo = bandIterator.hasNext() ? bandIterator.next() : null;
+
+        // Assign values to yellowBand and redBand depending on
+        // the number of bands in the meter.
+        // If only one band exists it will be designated as the redBand.
+        // If two bands exist, the one with the lower rate will be
+        // the yellowBand and the other the redBand.
+        Band yellowBand = (bandTwo == null ? null :
+                bandTwo.rate() > bandOne.rate() ? bandOne : bandTwo);
+        Band redBand = (bandTwo == null ? bandOne :
+                yellowBand == bandOne ? bandTwo : bandOne);
+
+        BandwidthProfile.Builder bandwidthProfileBuilder = new Builder()
+                // Consider the meter id as the bandwidth profile name
+                .name(meter.id().toString())
+                .colorAware(false)
+                // The implicit green action is pass
+                .greenAction(getBuilder(Action.PASS).build());
+
+        if (yellowBand != null) {
+            // Try to add yellow action; CIR/CBS will be obtained from
+            // yellowBand and PIR/PBS from redBand.
+            BandwidthProfileAction yellowAction =
+                    getBwProfileActionFromBand(yellowBand);
+            checkNotNull(yellowAction,
+                         "Could not obtain yellow action from meter band");
+            bandwidthProfileBuilder
+                    .cir(Bandwidth.kBps(yellowBand.rate()))
+                    .cbs(yellowBand.burst() == null ? null :
+                                 yellowBand.burst().intValue())
+                    .pir(Bandwidth.kBps(redBand.rate()))
+                    .pbs(redBand.burst() == null ? null :
+                                 redBand.burst().intValue())
+                    .yellowAction(yellowAction);
+        } else {
+            // No yellow action to add; CIR/CBS will be obtained from redBand
+            bandwidthProfileBuilder
+                    .cir(Bandwidth.kBps(redBand.rate()))
+                    .cbs(redBand.burst() == null ? null :
+                                 redBand.burst().intValue());
+        }
+
+        // Try to add red action in any case
+        BandwidthProfileAction redAction =
+                getBwProfileActionFromBand(redBand);
+        checkNotNull(redAction,
+                     "Could not obtain red action from meter band");
+
+        return bandwidthProfileBuilder
+                .redAction(redAction)
+                .build();
+    }
+
+    private static BandwidthProfileAction.Builder getBuilder(Action action) {
+        return BandwidthProfileAction.builder().action(action);
+    }
+
+    private static BandwidthProfileAction getBwProfileActionFromBand(Band band) {
+        checkNotNull(band.type(),
+                     "Could not obtain BW profile: Meter band type is null");
+        Action action = null;
+        if (band.type().equals(Band.Type.DROP)) {
+            action = Action.DISCARD;
+        } else if (band.type().equals(Band.Type.REMARK)) {
+            action = Action.REMARK;
+        }
+        checkNotNull(action,
+                     "Could not obtain BW profile: Invalid meter band type");
+        BandwidthProfileAction.Builder actionBuilder = getBuilder(action);
+        if (band.type().equals(Band.Type.REMARK)) {
+            checkNotNull(band.dropPrecedence(),
+                         "Could not obtain DSCP class from meter band");
+            actionBuilder.dscpClass(DscpClass.fromShort(band.dropPrecedence()));
+        }
+        return actionBuilder.build();
+    }
+
+    /**
+     * Returns a new builder.
+     *
+     * @return new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of BandwidthProfile entities.
+     */
+    public static final class Builder {
+
+        private String name;
+        private Bandwidth cir;
+        private Bandwidth pir;
+        private Integer cbs;
+        private Integer pbs;
+        private Integer ebs;
+        private BandwidthProfileAction greenAction;
+        private BandwidthProfileAction yellowAction;
+        private BandwidthProfileAction redAction;
+        private boolean colorAware;
+
+        /**
+         * Sets the name of this bandwidth profile builder.
+         *
+         * @param name the builder name to set
+         * @return this builder instance
+         */
+        public Builder name(String name) {
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * Sets the committed information rate (CIR) of this builder.
+         *
+         * @param cir the builder CIR to set
+         * @return this builder instance
+         */
+        public Builder cir(Bandwidth cir) {
+            this.cir = cir;
+            return this;
+        }
+
+        /**
+         * Sets the peak information rate (PIR) of this builder.
+         *
+         * @param pir the builder PIR to set
+         * @return this builder instance
+         */
+        public Builder pir(Bandwidth pir) {
+            this.pir = pir;
+            return this;
+        }
+
+        /**
+         * Sets the committed burst size (CBS) of this builder.
+         * The CBS is measured in bytes.
+         *
+         * @param cbs the builder CBS to set
+         * @return this builder instance
+         */
+        public Builder cbs(Integer cbs) {
+            this.cbs = cbs;
+            return this;
+        }
+
+        /**
+         * Sets the peak burst size (PBS) of this builder.
+         * The PBS is measured in bytes.
+         *
+         * @param pbs the builder CBS to set
+         * @return this builder instance
+         */
+        public Builder pbs(Integer pbs) {
+            this.pbs = pbs;
+            return this;
+        }
+
+        /**
+         * Sets the excess burst size (EBS) of this builder.
+         * The EBS is measured in bytes.
+         *
+         * @param ebs the builder EBS to set
+         * @return this builder instance
+         */
+        public Builder ebs(Integer ebs) {
+            this.ebs = ebs;
+            return this;
+        }
+
+        /**
+         * Sets the green action of this builder.
+         *
+         * @param greenAction the builder green action to set
+         * @return this builder instance
+         */
+        public Builder greenAction(BandwidthProfileAction greenAction) {
+            this.greenAction = greenAction;
+            return this;
+        }
+
+        /**
+         * Sets the yellow action of this builder.
+         *
+         * @param yellowAction the builder green action to set
+         * @return this builder instance
+         */
+        public Builder yellowAction(BandwidthProfileAction yellowAction) {
+            this.yellowAction = yellowAction;
+            return this;
+        }
+
+        /**
+         * Sets the red action of this builder.
+         *
+         * @param redAction the builder green action to set
+         * @return this builder instance
+         */
+        public Builder redAction(BandwidthProfileAction redAction) {
+            this.redAction = redAction;
+            return this;
+        }
+
+        /**
+         * Sets the color-aware mode of this builder.
+         *
+         * @param colorAware true if profile to be build is color-aware;
+         *                   false otherwise
+         * @return this builder instance
+         */
+        public Builder colorAware(boolean colorAware) {
+            this.colorAware = colorAware;
+            return this;
+        }
+
+        /**
+         * Builds a new BandwidthProfile instance.
+         * based on this builder's parameters
+         *
+         * @return a new BandwidthProfile instance
+         */
+        public BandwidthProfile build() {
+            checkNotNull(name, "Bandwidth profile must have a name");
+            checkNotNull(cir, "Bandwidth profile must have a CIR");
+            checkNotNull(greenAction,
+                         "Bandwidth profile must have a green action");
+            checkNotNull(redAction,
+                         "Bandwidth profile must have a red action");
+            checkArgument(pir != null || pbs == null,
+                          "Bandwidth profile cannot have PBS without PIR");
+            checkArgument(pir == null || ebs == null,
+                          "Bandwidth profile cannot have both PIR and EBS");
+            checkArgument(yellowAction == null && pir == null && ebs == null ||
+                                  yellowAction != null &&
+                                          (pir != null ^ ebs != null),
+                          "Bandwidth profile must have a yellow action only " +
+                                  "when either PIR or EBS are defined");
+            return new BandwidthProfile(name,
+                                        cir, pir, cbs, pbs, ebs,
+                                        greenAction, yellowAction, redAction,
+                                        colorAware);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(cir, pir, cbs, pbs, ebs,
+                            greenAction, yellowAction, redAction,
+                            colorAware);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof BandwidthProfile) {
+            final BandwidthProfile that = (BandwidthProfile) obj;
+            return this.getClass() == that.getClass() &&
+                    Objects.equals(this.cir, that.cir) &&
+                    Objects.equals(this.pir, that.pir)  &&
+                    Objects.equals(this.cbs, that.cbs) &&
+                    Objects.equals(this.pbs, that.pbs)  &&
+                    Objects.equals(this.ebs, that.ebs) &&
+                    Objects.equals(this.greenAction, that.greenAction)  &&
+                    Objects.equals(this.yellowAction, that.yellowAction) &&
+                    Objects.equals(this.redAction, that.redAction) &&
+                    Objects.equals(this.colorAware, that.colorAware);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("name", name)
+                .add("cir", cir)
+                .add("pir", pir)
+                .add("cbs", cbs)
+                .add("pbs", pbs)
+                .add("ebs", ebs)
+                .add("greenAction", greenAction)
+                .add("yellowAction", yellowAction)
+                .add("redAction", redAction)
+                .add("colorAware", colorAware)
+                .toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileAction.java b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileAction.java
new file mode 100644
index 0000000..ae6a153
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileAction.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.behaviour;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import org.onlab.packet.DscpClass;
+import org.onlab.packet.IPPrecedence;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+/**
+ * Represents an action to be taken by a marker/policer.
+ */
+@Beta
+public final class BandwidthProfileAction {
+
+    /**
+     * Denotes the type of action to be taken.
+     */
+    public enum Action {
+        /**
+         * Traffic is allowed to pass unmodified.
+         */
+        PASS,
+
+        /**
+         * Traffic is allowed to pass after being appropriately remarked.
+         */
+        REMARK,
+
+        /**
+         * Traffic is dropped.
+         */
+        DISCARD
+    }
+
+    private final Action action;
+    private final DscpClass dscpClass;
+    private final IPPrecedence ipPrecedence;
+    private final Short dropPrecedence;
+
+    private BandwidthProfileAction(Action action,
+                                   DscpClass dscpClass,
+                                   IPPrecedence ipPrecedence,
+                                   Short dropPrecedence) {
+        this.action = action;
+        this.dscpClass = dscpClass;
+        this.ipPrecedence = ipPrecedence;
+        this.dropPrecedence = dropPrecedence;
+    }
+
+    /**
+     * Obtains the type of this bandwidth profile action object.
+     *
+     * @return the bandwidth profile action type
+     */
+    public Action getAction() {
+        return this.action;
+    }
+
+    /**
+     * Obtains the DSCP class corresponding to the REMARK action.
+     * If this is not a REMARK action or if another field is remarked
+     * null is returned.
+     *
+     * @return the DSCP class for the action; may be null
+     */
+    public DscpClass getDscpClass() {
+        return this.dscpClass;
+    }
+
+    /**
+     * Obtains the IP precedence corresponding to the REMARK action.
+     * If this is not a REMARK action or if another field is remarked
+     * null is returned.
+     *
+     * @return the IP precedence for the action; may be null
+     */
+    public IPPrecedence getIpPrecedence() {
+        return this.ipPrecedence;
+    }
+
+    /**
+     * Obtains the drop precedence corresponding to the REMARK action.
+     * If this is not a REMARK action or if another field is remarked
+     * null is returned.
+     *
+     * @return the drop precedence for the action; may be null
+     */
+    public Short getDropPrecedence() {
+        return this.dropPrecedence;
+    }
+
+    /**
+     * Returns a new builder.
+     *
+     * @return new builder
+     */
+    public static Builder builder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder of BandwidthProfileAction entities.
+     */
+    public static final class Builder {
+
+        private Action action;
+        private DscpClass dscpClass;
+        private IPPrecedence ipPrecedence;
+        private Short dropPrecedence;
+
+        /**
+         * Sets the type of this builder.
+         *
+         * @param action the builder type to set
+         * @return this builder instance
+         */
+        public Builder action(Action action) {
+            this.action = action;
+            return this;
+        }
+
+        /**
+         * Sets the DSCP class of this builder.
+         *
+         * @param dscpClass the builder DSCP class to set
+         * @return this builder instance
+         */
+        public Builder dscpClass(DscpClass dscpClass) {
+            this.dscpClass = dscpClass;
+            return this;
+        }
+
+        /**
+         * Sets the IP precedence of this builder.
+         *
+         * @param ipPrecedence the builder IP precedence to set
+         * @return this builder instance
+         */
+        public Builder ipPrecedence(IPPrecedence ipPrecedence) {
+            this.ipPrecedence = ipPrecedence;
+            return this;
+        }
+
+        /**
+         * Sets the drop precedence of this builder.
+         *
+         * @param dropPrecedence the drop IP precedence to set
+         * @return this builder instance
+         */
+        public Builder dropPrecedence(Short dropPrecedence) {
+            this.dropPrecedence = dropPrecedence;
+            return this;
+        }
+
+        /**
+         * Builds a new BandwidthProfileAction based on builder's parameters.
+         *
+         * @return a new BandwidthProfileAction instance
+         */
+        public BandwidthProfileAction build() {
+            checkNotNull(action);
+            checkArgument(!action.equals(Action.REMARK) ||
+                                  (dscpClass != null ^
+                                          ipPrecedence != null ^
+                                          dropPrecedence != null),
+                          "Exactly one remark type must be defined");
+            return new BandwidthProfileAction(action,
+                                              dscpClass,
+                                              ipPrecedence,
+                                              dropPrecedence);
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(action, dscpClass, ipPrecedence, dropPrecedence);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof BandwidthProfileAction) {
+            final BandwidthProfileAction that = (BandwidthProfileAction) obj;
+            return this.getClass() == that.getClass() &&
+                    Objects.equals(this.action, that.action) &&
+                    Objects.equals(this.dscpClass, that.dscpClass) &&
+                    Objects.equals(this.ipPrecedence, that.ipPrecedence) &&
+                    Objects.equals(this.dropPrecedence, that.dropPrecedence);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return MoreObjects.toStringHelper(getClass())
+                .add("action", action == null ? null : action.name())
+                .add("dscpClass", dscpClass == null ? null : dscpClass.name())
+                .add("ipPrecedence", ipPrecedence)
+                .add("dropPrecedence", dropPrecedence)
+                .toString();
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileConfigBehaviour.java b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileConfigBehaviour.java
new file mode 100644
index 0000000..bd222cd
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/net/behaviour/BandwidthProfileConfigBehaviour.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.behaviour;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.driver.HandlerBehaviour;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Collection;
+
+/**
+ * Means to configure bandwidth profiles on devices.
+ */
+@Beta
+public interface BandwidthProfileConfigBehaviour extends HandlerBehaviour {
+    /**
+     * Adds a new bandwidth profile on the device.
+     * If a profile with the same name already exists on the device, the profile
+     * is not added.
+     *
+     * @param bwProfile the bandwidth profile to add
+     * @return true, if the profile was added successfully; false otherwise
+     */
+    default boolean addBandwidthProfile(BandwidthProfile bwProfile) {
+        return addBandwidthProfile(Collections.singletonList(bwProfile));
+    }
+
+    /**
+     * Adds new bandwidth profiles on the device.
+     * If profiles with the same names already exist on the device, the
+     * conflicting profiles are not added.
+     *
+     * @param bwProfiles the bandwidth profiles to add
+     * @return true, if any of the profiles were added successfully;
+     * false otherwise
+     */
+    boolean addBandwidthProfile(Collection<BandwidthProfile> bwProfiles);
+
+    /**
+     * Removes an existing bandwidth profile from a device.
+     * Returns false if the profile does not exist on the device.
+     *
+     * @param profileName the name of the profile to remove from the device
+     * @return true, if the profile was removed successfully; false otherwise
+     */
+    default boolean removeBandwidthProfile(String profileName) {
+        return removeBandwidthProfile(Collections.singletonList(profileName));
+    }
+
+    /**
+     * Removes existing bandwidth profiles from a device.
+     * Returns false if none of the profiles exist on the device.
+     *
+     * @param profileNames the names of the profiles to remove from the device
+     * @return true, if any of the profiles were removed successfully;
+     * false otherwise
+     */
+    boolean removeBandwidthProfile(Collection<String> profileNames);
+
+    /**
+     * Removes all existing bandwidth profiles from a device.
+     * Returns true if no profiles exist on the device.
+     *
+     * @return true, if all profiles were removed successfully; false otherwise
+     */
+    boolean removeAllBandwidthProfiles();
+
+    /**
+     * Updates an already configured bandwidth profile on the device.
+     * Returns false if the profile does not exist on the device.
+     *
+     * @param bwProfile the updated bandwidth profile
+     * @return true, if the profile was updated successfully; false otherwise
+     */
+    default boolean updateBandwidthProfile(BandwidthProfile bwProfile) {
+        return updateBandwidthProfile(Collections.singletonList(bwProfile));
+    }
+
+    /**
+     * Updates already configured bandwidth profiles on the device.
+     * Returns false if none of the profiles exist on the device.
+     *
+     * @param bwProfiles the updated bandwidth profile
+     * @return true, if any of the profiles were updated successfully;
+     * false otherwise
+     */
+    boolean updateBandwidthProfile(
+            Collection<BandwidthProfile> bwProfiles);
+
+    /**
+     * Obtains an already configured bandwidth profile from the device.
+     *
+     * @param profileName the name of the profile to obtain from the device
+     * @return the bandwidth profile; null if the profile does not exist
+     * @throws IOException if profile could not be obtained due to
+     * communication issues with the device
+     */
+    BandwidthProfile getBandwidthProfile(String profileName) throws IOException;
+
+    /**
+     * Obtains all already configured bandwidth profiles from the device.
+     *
+     * @return the bandwidth profiles; empty collection if no profiles exist
+     * @throws IOException if profiles could not be obtained due to
+     * communication issues with the device
+     */
+    Collection<BandwidthProfile> getAllBandwidthProfiles() throws IOException;
+}
diff --git a/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileActionTest.java b/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileActionTest.java
new file mode 100644
index 0000000..e8bc68c
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileActionTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.behaviour;
+
+import com.google.common.testing.EqualsTester;
+import org.junit.Test;
+import org.onlab.packet.DscpClass;
+import static org.onosproject.net.behaviour.BandwidthProfileAction.Action;
+
+/**
+ * Test for BandwidthProfileAction class.
+ */
+public class BandwidthProfileActionTest {
+
+    @Test
+    public void testEquals() {
+        BandwidthProfileAction passAction1 = BandwidthProfileAction.builder()
+                .action(Action.PASS)
+                .build();
+        BandwidthProfileAction passAction2 = BandwidthProfileAction.builder()
+                .action(Action.PASS)
+                .build();
+        BandwidthProfileAction discardAction1 = BandwidthProfileAction.builder()
+                .action(Action.DISCARD)
+                .build();
+        BandwidthProfileAction discardAction2 = BandwidthProfileAction.builder()
+                .action(Action.DISCARD)
+                .build();
+        BandwidthProfileAction remarkAction1 = BandwidthProfileAction.builder()
+                .action(Action.REMARK)
+                .dscpClass(DscpClass.AF11)
+                .build();
+        BandwidthProfileAction remarkAction2 = BandwidthProfileAction.builder()
+                .action(Action.REMARK)
+                .dscpClass(DscpClass.AF11)
+                .build();
+        new EqualsTester()
+                .addEqualityGroup(passAction1, passAction2)
+                .addEqualityGroup(discardAction1, discardAction2)
+                .addEqualityGroup(remarkAction1, remarkAction2)
+                .testEquals();
+    }
+}
diff --git a/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileTest.java b/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileTest.java
new file mode 100644
index 0000000..8052593
--- /dev/null
+++ b/core/api/src/test/java/org/onosproject/net/behaviour/BandwidthProfileTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * 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.behaviour;
+
+import org.junit.Test;
+import org.onlab.packet.DscpClass;
+import org.onlab.util.Bandwidth;
+import org.onosproject.TestApplicationId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.meter.Band;
+import org.onosproject.net.meter.DefaultBand;
+import org.onosproject.net.meter.DefaultMeter;
+import org.onosproject.net.meter.Meter;
+import org.onosproject.net.meter.MeterId;
+import static org.onosproject.net.behaviour.BandwidthProfileAction.Action;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test for BandwidthProfile class.
+ */
+public class BandwidthProfileTest {
+
+    private static final long ONE = 1L;
+    private static final long ONE_K = 1_000L;
+    private static final long TWO_K = 2_000L;
+    private static final long EIGHT_K = 8_000L;
+    private static final long ONE_M = 1_000_000L;
+    private static final long TEN_M = 10_000_000L;
+
+    @Test
+    public void testMeterConversion() {
+        DeviceId deviceId = DeviceId.deviceId("netconf:10.0.0.1:22");
+        ApplicationId appId = TestApplicationId.create("org.onosproject.foo.app");
+        Meter.Builder meterBuilder = new DefaultMeter.Builder()
+                .withId(MeterId.meterId(ONE))
+                .withUnit(Meter.Unit.KB_PER_SEC)
+                .forDevice(deviceId)
+                .burst();
+
+        // Create Meter with single band
+        Band band1 = DefaultBand.builder()
+                .ofType(Band.Type.DROP)
+                .withRate(TEN_M)
+                .burstSize(TWO_K)
+                .build();
+        Meter meter = meterBuilder
+                .fromApp(appId)
+                .withBands(Arrays.asList(band1))
+                .build();
+        BandwidthProfile bandwidthProfile = BandwidthProfile.fromMeter(meter);
+
+        assertEquals("wrong bw profile name",
+                     bandwidthProfile.name(), meter.id().toString());
+        assertEquals("wrong bw profile type",
+                     bandwidthProfile.type(), BandwidthProfile.Type.sr2CM);
+        assertEquals("wrong bw profile CIR",
+                     bandwidthProfile.cir().bps(), band1.rate() * EIGHT_K, 0);
+        assertEquals("wrong bw profile CBS",
+                     (long) bandwidthProfile.cbs(), (long) band1.burst());
+        assertNull(bandwidthProfile.pir());
+        assertNull(bandwidthProfile.pbs());
+        assertNull(bandwidthProfile.ebs());
+        assertEquals("wrong green action",
+                     bandwidthProfile.greenAction(),
+                     getBuilder(Action.PASS).build());
+        assertNull(bandwidthProfile.yellowAction());
+        assertEquals("wrong red action",
+                     bandwidthProfile.redAction(),
+                     getBuilder(Action.DISCARD).build());
+        assertEquals("wrong color-aware mode",
+                     bandwidthProfile.colorAware(), false);
+
+        // Create Meter with two bands
+        Band band2 = DefaultBand.builder().burstSize(ONE_K)
+                .ofType(Band.Type.REMARK)
+                .dropPrecedence((short) 0b001010)
+                .withRate(ONE_M)
+                .build();
+        meter = meterBuilder
+                .fromApp(appId)
+                .withBands(Arrays.asList(band1, band2))
+                .build();
+        bandwidthProfile = BandwidthProfile.fromMeter(meter);
+
+        assertEquals("wrong bw profile name",
+                     bandwidthProfile.name(), meter.id().toString());
+        assertEquals("wrong bw profile type",
+                     bandwidthProfile.type(), BandwidthProfile.Type.trTCM);
+        assertEquals("wrong bw profile CIR",
+                     bandwidthProfile.cir().bps(), band2.rate() * EIGHT_K, 0);
+        assertEquals("wrong bw profile CBS",
+                     (long) bandwidthProfile.cbs(), (long) band2.burst());
+        assertEquals("wrong bw profile PIR",
+                     bandwidthProfile.pir().bps(), band1.rate() * EIGHT_K, 0);
+        assertEquals("wrong bw profile PBS",
+                     (long) bandwidthProfile.pbs(), (long) band1.burst());
+        assertNull(bandwidthProfile.ebs());
+        assertEquals("wrong green action",
+                     bandwidthProfile.greenAction(),
+                     getBuilder(Action.PASS).build());
+        assertEquals("wrong yellow action",
+                     bandwidthProfile.yellowAction(),
+                     getBuilder(Action.REMARK)
+                             .dscpClass(DscpClass.AF11)
+                             .build());
+        assertEquals("wrong red action",
+                     bandwidthProfile.redAction(),
+                     getBuilder(Action.DISCARD).build());
+        assertEquals("wrong color-aware mode",
+                     bandwidthProfile.colorAware(), false);
+    }
+
+    @Test
+    public void testType() {
+        BandwidthProfile.Builder bwProfileBuilder = BandwidthProfile.builder()
+                .name("profile")
+                .cir(Bandwidth.bps(ONE_M))
+                .cbs((int) ONE_K)
+                .greenAction(getBuilder(Action.PASS).build())
+                .redAction(getBuilder(Action.DISCARD).build())
+                .colorAware(false);
+        assertEquals("wrong bw profile type",
+                     bwProfileBuilder.build().type(),
+                     BandwidthProfile.Type.sr2CM);
+
+        bwProfileBuilder.ebs((int) TWO_K)
+                .yellowAction(getBuilder(Action.REMARK)
+                                      .dscpClass(DscpClass.AF11)
+                                      .build());
+        assertEquals("wrong bw profile type",
+                     bwProfileBuilder.build().type(),
+                     BandwidthProfile.Type.srTCM);
+        bwProfileBuilder.ebs(null);
+
+        bwProfileBuilder.pir(Bandwidth.bps(TEN_M))
+                .pbs((int) TWO_K);
+        assertEquals("wrong bw profile type",
+                     bwProfileBuilder.build().type(),
+                     BandwidthProfile.Type.trTCM);
+    }
+
+    @Test
+    public void testEquals() {
+        BandwidthProfile bwProfile1 = new BandwidthProfile.Builder()
+                .name("profile1")
+                .cir(Bandwidth.bps(ONE_M))
+                .cbs((int) ONE_K)
+                .pir(Bandwidth.bps(TEN_M))
+                .pbs((int) TWO_K)
+                .greenAction(getBuilder(Action.PASS).build())
+                .yellowAction(getBuilder(Action.REMARK)
+                                      .dscpClass(DscpClass.AF11)
+                                      .build())
+                .redAction(getBuilder(Action.DISCARD).build())
+                .colorAware(false)
+                .build();
+        BandwidthProfile bwProfile2 = new BandwidthProfile.Builder()
+                .name("profile2")
+                .cir(Bandwidth.bps(ONE_M))
+                .cbs((int) ONE_K)
+                .pir(Bandwidth.bps(TEN_M))
+                .pbs((int) TWO_K)
+                .greenAction(getBuilder(Action.PASS).build())
+                .yellowAction(getBuilder(Action.REMARK)
+                                      .dscpClass(DscpClass.AF11)
+                                      .build())
+                .redAction(getBuilder(Action.DISCARD).build())
+                .colorAware(false)
+                .build();
+        assertTrue("wrong equals method", bwProfile1.equals(bwProfile2));
+    }
+
+    private static BandwidthProfileAction.Builder getBuilder(Action action) {
+        return new BandwidthProfileAction.Builder().action(action);
+    }
+}