blob: 1a3e79938a2cc158734f9e8cc1341ca648a3b74f [file] [log] [blame]
/*
* 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 org.onosproject.net.behaviour.upf;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import org.onosproject.net.meter.Band;
import org.onosproject.net.meter.DefaultBand;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.onosproject.net.behaviour.upf.UpfEntityType.APPLICATION_METER;
import static org.onosproject.net.behaviour.upf.UpfEntityType.SESSION_METER;
import static org.onosproject.net.meter.Band.Type.MARK_RED;
import static org.onosproject.net.meter.Band.Type.MARK_YELLOW;
/**
* A structure representing a UPF meter, either for metering session (UE) or
* application traffic.
* UPF meters represent PFCP QER MBR and GBR information.
* UPF meters of type session support only the peak band.
* UPF meters of type application support both peak and committed bands.
*/
public final class UpfMeter implements UpfEntity {
private final int cellId;
private final ImmutableMap<Band.Type, Band> meterBands;
private final UpfEntityType type;
private UpfMeter(int cellId, Map<Band.Type, Band> meterBands, UpfEntityType type) {
this.cellId = cellId;
this.meterBands = ImmutableMap.copyOf(meterBands);
this.type = type;
}
@Override
public String toString() {
return String.format("UpfMeter(type=%s, index=%d, committed=%s, peak=%s)",
type, cellId, committedBand().orElse(null),
peakBand().orElse(null));
}
@Override
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object == null) {
return false;
}
if (getClass() != object.getClass()) {
return false;
}
UpfMeter that = (UpfMeter) object;
return this.type.equals(that.type) &&
this.cellId == that.cellId &&
this.meterBands.equals(that.meterBands);
}
@Override
public int hashCode() {
return Objects.hash(type, cellId, meterBands);
}
@Override
public UpfEntityType type() {
return this.type;
}
/**
* Get the meter cell index of this meter.
*
* @return the cell index
*/
public int cellId() {
return this.cellId;
}
/**
* Get the committed band of this meter.
*
* @return the committed band, Empty if none
*/
public Optional<Band> committedBand() {
return Optional.ofNullable(meterBands.getOrDefault(MARK_YELLOW, null));
}
/**
* Get the peak band of this meter.
*
* @return the peak band, Empty if none
*/
public Optional<Band> peakBand() {
return Optional.ofNullable(meterBands.getOrDefault(MARK_RED, null));
}
/**
* Check if this UPF meter is for sessions (UE) traffic.
*
* @return true if the meter is for session traffic
*/
public boolean isSession() {
return type.equals(SESSION_METER);
}
/**
* Check if this UPF meter is for application traffic.
*
* @return true if the meter is for application traffic
*/
public boolean isApplication() {
return type.equals(APPLICATION_METER);
}
/**
* Check if this UPF meter is a reset.
*
* @return true if this represents a meter reset.
*/
public boolean isReset() {
return meterBands.isEmpty();
}
/**
* Return a session UPF meter with no bands. Used to reset the meter.
*
* @param cellId the meter cell index of this meter
* @return a UpfMeter of type session with no bands
*/
public static UpfMeter resetSession(int cellId) {
return new UpfMeter(cellId, Maps.newHashMap(), SESSION_METER);
}
/**
* Return an application UPF meter with no bands. Used to reset the meter.
*
* @param cellId the meter cell index of this meter
* @return a UpfMeter of type application with no bands
*/
public static UpfMeter resetApplication(int cellId) {
return new UpfMeter(cellId, Maps.newHashMap(), APPLICATION_METER);
}
public static Builder builder() {
return new Builder();
}
/**
* Builder of UpfMeter object. Use {@link #resetApplication(int)} and
* {@link #resetSession(int)} to reset the meter config.
*/
public static class Builder {
private Integer cellId = null;
private Map<Band.Type, Band> bands = Maps.newHashMap();
private UpfEntityType type;
public Builder() {
}
/**
* Set the meter cell index of this meter.
*
* @param cellId the meter cell index
* @return this builder object
*/
public Builder setCellId(int cellId) {
this.cellId = cellId;
return this;
}
/**
* Set the committed band of this meter.
* Valid only for meter of type application.
*
* @param cir the Committed Information Rate in bytes/s
* @param cburst the Committed Burst in bytes
* @return this builder object
*/
public Builder setCommittedBand(long cir, long cburst) {
this.bands.put(MARK_YELLOW,
DefaultBand.builder()
.ofType(MARK_YELLOW)
.withRate(cir)
.burstSize(cburst)
.build()
);
return this;
}
/**
* Set the peak band of this meter.
*
* @param pir the Peak Information Rate in bytes/s
* @param pburst the Peak Burst in bytes
* @return this builder object
*/
public Builder setPeakBand(long pir, long pburst) {
this.bands.put(MARK_RED,
DefaultBand.builder()
.ofType(MARK_RED)
.withRate(pir)
.burstSize(pburst)
.build()
);
return this;
}
/**
* Make this meter a session meter.
*
* @return this builder object
*/
public Builder setSession() {
this.type = SESSION_METER;
return this;
}
/**
* Make this meter an application meter.
*
* @return this builder object
*/
public Builder setApplication() {
this.type = APPLICATION_METER;
return this;
}
public UpfMeter build() {
checkNotNull(type, "A meter type must be assigned");
switch (type) {
case SESSION_METER:
checkArgument(!bands.containsKey(MARK_YELLOW),
"Committed band can not be provided for session meter!");
break;
case APPLICATION_METER:
checkArgument((bands.containsKey(MARK_YELLOW) && bands.containsKey(MARK_RED)) || bands.isEmpty(),
"Bands (committed and peak) must be provided together or not at all!");
break;
default:
// I should never reach this point
throw new IllegalArgumentException("Invalid meter type, I should never reach this point");
}
checkNotNull(cellId, "Meter cell ID must be provided!");
return new UpfMeter(cellId, bands, type);
}
}
}