blob: c39500970500a083689f660d04e49f999af9080d [file] [log] [blame]
/*
* Copyright 2021-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.annotations.Beta;
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.COUNTER;
import static org.onosproject.net.behaviour.upf.UpfEntityType.EGRESS_COUNTER;
import static org.onosproject.net.behaviour.upf.UpfEntityType.INGRESS_COUNTER;
/**
* A structure for compactly passing UPF counter (ingress, egress or both) values
* for a given counter ID. Contains four counts: Ingress Packets, Ingress Bytes,
* Egress Packets, Egress Bytes. UpfCounter can be used ONLY on {@code apply}
* and {@code readAll} calls in the {@link UpfDevice} interface.
*/
@Beta
public final class UpfCounter implements UpfEntity {
private final int cellId;
private final Long ingressPkts;
private final Long ingressBytes;
private final Long egressPkts;
private final Long egressBytes;
private final UpfEntityType type;
private UpfCounter(int cellId, Long ingressPkts, Long ingressBytes,
Long egressPkts, Long egressBytes, UpfEntityType type) {
this.cellId = cellId;
this.ingressPkts = ingressPkts;
this.ingressBytes = ingressBytes;
this.egressPkts = egressPkts;
this.egressBytes = egressBytes;
this.type = type;
}
public static Builder builder() {
return new Builder();
}
@Override
public String toString() {
switch (this.type) {
case COUNTER:
return String.format("UpfStats(cell_id=%d, ingress=(%dpkts,%dbytes), egress=(%dpkts,%dbytes))",
cellId, ingressPkts, ingressBytes, egressPkts, egressBytes);
case INGRESS_COUNTER:
return String.format("UpfIngressCounter(cell_id=%d, packets=%d, bytes=%d))",
cellId, ingressPkts, ingressBytes);
case EGRESS_COUNTER:
return String.format("UpfEgressCounter(cell_id=%d, packets=%d, bytes=%d))",
cellId, egressPkts, egressBytes);
default:
throw new IllegalStateException("I should never reach this point!");
}
}
@Override
public boolean equals(Object object) {
if (object == this) {
return true;
}
if (object == null) {
return false;
}
if (getClass() != object.getClass()) {
return false;
}
UpfCounter that = (UpfCounter) object;
return this.cellId == that.cellId && this.type == that.type;
}
/**
* Returns whether this UpfCounter is exactly equal to the given UpfCounter,
* including their packets and bytes values.
*
* @param that other {@link UpfCounter} instance to compare
* @return true if exactly equals, false otherwise
*/
public boolean exactlyEquals(UpfCounter that) {
return this.equals(that) &&
(this.ingressPkts == that.ingressPkts || this.ingressPkts.equals(that.ingressPkts)) &&
(this.ingressBytes == that.ingressBytes || this.ingressBytes.equals(that.ingressBytes)) &&
(this.egressPkts == that.egressPkts || this.egressPkts.equals(that.egressPkts)) &&
(this.egressBytes == that.egressBytes || this.egressBytes.equals(that.egressBytes));
}
@Override
public int hashCode() {
return Objects.hash(cellId, type);
}
/**
* Get the cell ID (index) of the dataplane counter that produced this set of stats.
*
* @return counter cell ID
*/
public int getCellId() {
return cellId;
}
/**
* Get the number of packets that hit this counter in the dataplane ingress pipeline.
* Return a value only if the counter is of type {@code UpfEntityType.COUNTER}
* or {@code UpfEntityType.INGRESS_COUNTER}, otherwise an empty Optional.
*
* @return ingress packet count or empty if this is of type {@code UpfEntityType.EGRESS_COUNTER}
*/
public Optional<Long> getIngressPkts() {
return Optional.ofNullable(ingressPkts);
}
/**
* Get the number of packets that hit this counter in the dataplane egress pipeline.
* Return a value only if the counter is of type {@code UpfEntityType.COUNTER}
* or {@code UpfEntityType.EGRESS_COUNTER}, otherwise an empty Optional.
*
* @return egress packet count or empty if this is of type {@code UpfEntityType.INGRESS_COUNTER}
*/
public Optional<Long> getEgressPkts() {
return Optional.ofNullable(egressPkts);
}
/**
* Get the number of packet bytes that hit this counter in the dataplane ingress pipeline.
* Return value only if the counter is of type {{@code UpfEntityType.COUNTER}
* or {@code UpfEntityType.INGRESS_COUNTER}, otherwise an empty Optional.
*
* @return ingress byte count or empty if this is of type {@code UpfEntityType.EGRESS_COUNTER}
*/
public Optional<Long> getIngressBytes() {
return Optional.ofNullable(ingressBytes);
}
/**
* Get the number of packet bytes that hit this counter in the dataplane egress pipeline.
* Return a value only if the counter is of type {@code UpfEntityType.COUNTER}
* or {@code UpfEntityType.EGRESS_COUNTER}, otherwise an empty Optional.
*
* @return egress byte count or empty if this is of type {@code UpfEntityType.INGRESS_COUNTER}
*/
public Optional<Long> getEgressBytes() {
return Optional.ofNullable(egressBytes);
}
@Override
public UpfEntityType type() {
return type;
}
/**
* Sum the content of the given UpfCounter to the counter values contained
* in this instance.
*
* @param that The UpfCounter to sum to this instance
* @return a new UpfCounter instance with sum counters.
* @throws IllegalArgumentException if the given UpfCounter is not referring
* to the same type and id as this
*/
public UpfCounter sum(UpfCounter that) throws IllegalArgumentException {
if (!this.equals(that)) {
throw new IllegalArgumentException(
"The given UpfCounter is not of the same type or refers to a different index");
}
UpfCounter.Builder builder = UpfCounter.builder().withCellId(this.getCellId());
if (this.type.equals(UpfEntityType.COUNTER) || this.type.equals(UpfEntityType.INGRESS_COUNTER)) {
builder.setIngress(this.ingressPkts + that.ingressPkts,
this.ingressBytes + that.ingressBytes);
}
if (this.type.equals(UpfEntityType.COUNTER) || this.type.equals(UpfEntityType.EGRESS_COUNTER)) {
builder.setEgress(this.egressPkts + that.egressPkts,
this.egressBytes + that.egressBytes);
}
return builder.build();
}
/**
* Builder for UpfCounter.
*/
public static class Builder {
private Integer cellId;
private Long ingressPkts;
private Long ingressBytes;
private Long egressPkts;
private Long egressBytes;
private UpfEntityType type = COUNTER;
public Builder() {
}
/**
* Set the Cell ID (index) of the datalane counter that produced this set of stats.
*
* @param cellId the counter cell ID
* @return This builder
*/
public Builder withCellId(int cellId) {
this.cellId = cellId;
return this;
}
/**
* Set the number of packets and bytes that hit the counter in the dataplane ingress pipeline.
*
* @param ingressPkts ingress packet count
* @param ingressBytes egress packet count
* @return This builder
*/
public Builder setIngress(long ingressPkts, long ingressBytes) {
this.ingressPkts = ingressPkts;
this.ingressBytes = ingressBytes;
return this;
}
/**
* Set the number of packets and bytes that hit the counter in the dataplane egress pipeline.
*
* @param egressPkts egress packet count
* @param egressBytes egress byte count
* @return This builder
*/
public Builder setEgress(long egressPkts, long egressBytes) {
this.egressPkts = egressPkts;
this.egressBytes = egressBytes;
return this;
}
/**
* Set the counter as ingress only counter.
*
* @return This builder
*/
public Builder isIngressCounter() {
this.type = INGRESS_COUNTER;
return this;
}
/**
* Set the counter as egress only counter.
*
* @return This builder
*/
public Builder isEgressCounter() {
this.type = EGRESS_COUNTER;
return this;
}
public UpfCounter build() {
checkNotNull(cellId, "CellID must be provided");
switch (type) {
case INGRESS_COUNTER:
checkArgument(this.ingressBytes != null && this.ingressPkts != null,
"Ingress counter values must be provided");
this.egressBytes = null;
this.egressPkts = null;
break;
case EGRESS_COUNTER:
checkArgument(this.egressBytes != null && this.egressPkts != null,
"Egress counter values must be provided");
this.ingressBytes = null;
this.ingressPkts = null;
break;
case COUNTER:
checkArgument(this.ingressBytes != null && this.ingressPkts != null &&
this.egressBytes != null && this.egressPkts != null,
"Ingress and egress counter values must be provided");
break;
default:
// I should never reach this point
throw new IllegalArgumentException("I should never reach this point!");
}
return new UpfCounter(cellId, ingressPkts, ingressBytes, egressPkts, egressBytes, type);
}
}
}