Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2021-present Open Networking Foundation |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package org.onosproject.net.behaviour.upf; |
| 18 | |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 19 | import com.google.common.annotations.Beta; |
| 20 | |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 21 | import java.util.Objects; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 22 | import java.util.Optional; |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 23 | |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 24 | import static com.google.common.base.Preconditions.checkArgument; |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 25 | import static com.google.common.base.Preconditions.checkNotNull; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 26 | import static org.onosproject.net.behaviour.upf.UpfEntityType.COUNTER; |
| 27 | import static org.onosproject.net.behaviour.upf.UpfEntityType.EGRESS_COUNTER; |
| 28 | import static org.onosproject.net.behaviour.upf.UpfEntityType.INGRESS_COUNTER; |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 29 | |
| 30 | /** |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 31 | * A structure for compactly passing UPF counter (ingress, egress or both) values |
| 32 | * for a given counter ID. Contains four counts: Ingress Packets, Ingress Bytes, |
| 33 | * Egress Packets, Egress Bytes. UpfCounter can be used ONLY on {@code apply} |
| 34 | * and {@code readAll} calls in the {@link UpfDevice} interface. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 35 | */ |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 36 | @Beta |
| 37 | public final class UpfCounter implements UpfEntity { |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 38 | private final int cellId; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 39 | private final Long ingressPkts; |
| 40 | private final Long ingressBytes; |
| 41 | private final Long egressPkts; |
| 42 | private final Long egressBytes; |
| 43 | private final UpfEntityType type; |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 44 | |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 45 | private UpfCounter(int cellId, Long ingressPkts, Long ingressBytes, |
| 46 | Long egressPkts, Long egressBytes, UpfEntityType type) { |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 47 | this.cellId = cellId; |
| 48 | this.ingressPkts = ingressPkts; |
| 49 | this.ingressBytes = ingressBytes; |
| 50 | this.egressPkts = egressPkts; |
| 51 | this.egressBytes = egressBytes; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 52 | this.type = type; |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 53 | } |
| 54 | |
| 55 | public static Builder builder() { |
| 56 | return new Builder(); |
| 57 | } |
| 58 | |
| 59 | @Override |
| 60 | public String toString() { |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 61 | switch (this.type) { |
| 62 | case COUNTER: |
| 63 | return String.format("UpfStats(cell_id=%d, ingress=(%dpkts,%dbytes), egress=(%dpkts,%dbytes))", |
| 64 | cellId, ingressPkts, ingressBytes, egressPkts, egressBytes); |
| 65 | case INGRESS_COUNTER: |
| 66 | return String.format("UpfIngressCounter(cell_id=%d, packets=%d, bytes=%d))", |
| 67 | cellId, ingressPkts, ingressBytes); |
| 68 | case EGRESS_COUNTER: |
| 69 | return String.format("UpfEgressCounter(cell_id=%d, packets=%d, bytes=%d))", |
| 70 | cellId, egressPkts, egressBytes); |
| 71 | default: |
| 72 | throw new IllegalStateException("I should never reach this point!"); |
| 73 | } |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 74 | } |
| 75 | |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 76 | @Override |
| 77 | public boolean equals(Object object) { |
| 78 | if (object == this) { |
| 79 | return true; |
| 80 | } |
| 81 | if (object == null) { |
| 82 | return false; |
| 83 | } |
| 84 | if (getClass() != object.getClass()) { |
| 85 | return false; |
| 86 | } |
| 87 | UpfCounter that = (UpfCounter) object; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 88 | return this.cellId == that.cellId && this.type == that.type; |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 89 | } |
| 90 | |
| 91 | /** |
| 92 | * Returns whether this UpfCounter is exactly equal to the given UpfCounter, |
| 93 | * including their packets and bytes values. |
| 94 | * |
| 95 | * @param that other {@link UpfCounter} instance to compare |
| 96 | * @return true if exactly equals, false otherwise |
| 97 | */ |
| 98 | public boolean exactlyEquals(UpfCounter that) { |
| 99 | return this.equals(that) && |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 100 | (this.ingressPkts == that.ingressPkts || this.ingressPkts.equals(that.ingressPkts)) && |
| 101 | (this.ingressBytes == that.ingressBytes || this.ingressBytes.equals(that.ingressBytes)) && |
| 102 | (this.egressPkts == that.egressPkts || this.egressPkts.equals(that.egressPkts)) && |
| 103 | (this.egressBytes == that.egressBytes || this.egressBytes.equals(that.egressBytes)); |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 104 | } |
| 105 | |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 106 | @Override |
| 107 | public int hashCode() { |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 108 | return Objects.hash(cellId, type); |
Daniele Moro | de1f1f7 | 2022-01-26 16:47:08 +0100 | [diff] [blame] | 109 | } |
| 110 | |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 111 | /** |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 112 | * Get the cell ID (index) of the dataplane counter that produced this set of stats. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 113 | * |
| 114 | * @return counter cell ID |
| 115 | */ |
| 116 | public int getCellId() { |
| 117 | return cellId; |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Get the number of packets that hit this counter in the dataplane ingress pipeline. |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 122 | * Return a value only if the counter is of type {@code UpfEntityType.COUNTER} |
| 123 | * or {@code UpfEntityType.INGRESS_COUNTER}, otherwise an empty Optional. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 124 | * |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 125 | * @return ingress packet count or empty if this is of type {@code UpfEntityType.EGRESS_COUNTER} |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 126 | */ |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 127 | public Optional<Long> getIngressPkts() { |
| 128 | return Optional.ofNullable(ingressPkts); |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 129 | } |
| 130 | |
| 131 | /** |
| 132 | * Get the number of packets that hit this counter in the dataplane egress pipeline. |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 133 | * Return a value only if the counter is of type {@code UpfEntityType.COUNTER} |
| 134 | * or {@code UpfEntityType.EGRESS_COUNTER}, otherwise an empty Optional. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 135 | * |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 136 | * @return egress packet count or empty if this is of type {@code UpfEntityType.INGRESS_COUNTER} |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 137 | */ |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 138 | public Optional<Long> getEgressPkts() { |
| 139 | return Optional.ofNullable(egressPkts); |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 140 | } |
| 141 | |
| 142 | /** |
| 143 | * Get the number of packet bytes that hit this counter in the dataplane ingress pipeline. |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 144 | * Return value only if the counter is of type {{@code UpfEntityType.COUNTER} |
| 145 | * or {@code UpfEntityType.INGRESS_COUNTER}, otherwise an empty Optional. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 146 | * |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 147 | * @return ingress byte count or empty if this is of type {@code UpfEntityType.EGRESS_COUNTER} |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 148 | */ |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 149 | public Optional<Long> getIngressBytes() { |
| 150 | return Optional.ofNullable(ingressBytes); |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Get the number of packet bytes that hit this counter in the dataplane egress pipeline. |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 155 | * Return a value only if the counter is of type {@code UpfEntityType.COUNTER} |
| 156 | * or {@code UpfEntityType.EGRESS_COUNTER}, otherwise an empty Optional. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 157 | * |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 158 | * @return egress byte count or empty if this is of type {@code UpfEntityType.INGRESS_COUNTER} |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 159 | */ |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 160 | public Optional<Long> getEgressBytes() { |
| 161 | return Optional.ofNullable(egressBytes); |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 162 | } |
| 163 | |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 164 | @Override |
| 165 | public UpfEntityType type() { |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 166 | return type; |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 167 | } |
| 168 | |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 169 | /** |
| 170 | * Sum the content of the given UpfCounter to the counter values contained |
| 171 | * in this instance. |
| 172 | * |
| 173 | * @param that The UpfCounter to sum to this instance |
| 174 | * @return a new UpfCounter instance with sum counters. |
| 175 | * @throws IllegalArgumentException if the given UpfCounter is not referring |
| 176 | * to the same type and id as this |
| 177 | */ |
| 178 | public UpfCounter sum(UpfCounter that) throws IllegalArgumentException { |
| 179 | if (!this.equals(that)) { |
| 180 | throw new IllegalArgumentException( |
| 181 | "The given UpfCounter is not of the same type or refers to a different index"); |
| 182 | } |
| 183 | UpfCounter.Builder builder = UpfCounter.builder().withCellId(this.getCellId()); |
| 184 | if (this.type.equals(UpfEntityType.COUNTER) || this.type.equals(UpfEntityType.INGRESS_COUNTER)) { |
| 185 | builder.setIngress(this.ingressPkts + that.ingressPkts, |
| 186 | this.ingressBytes + that.ingressBytes); |
| 187 | } |
| 188 | if (this.type.equals(UpfEntityType.COUNTER) || this.type.equals(UpfEntityType.EGRESS_COUNTER)) { |
| 189 | builder.setEgress(this.egressPkts + that.egressPkts, |
| 190 | this.egressBytes + that.egressBytes); |
| 191 | } |
| 192 | return builder.build(); |
| 193 | } |
| 194 | |
| 195 | /** |
| 196 | * Builder for UpfCounter. |
| 197 | */ |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 198 | public static class Builder { |
| 199 | private Integer cellId; |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 200 | private Long ingressPkts; |
| 201 | private Long ingressBytes; |
| 202 | private Long egressPkts; |
| 203 | private Long egressBytes; |
| 204 | private UpfEntityType type = COUNTER; |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 205 | |
| 206 | public Builder() { |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 207 | } |
| 208 | |
| 209 | /** |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 210 | * Set the Cell ID (index) of the datalane counter that produced this set of stats. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 211 | * |
| 212 | * @param cellId the counter cell ID |
| 213 | * @return This builder |
| 214 | */ |
| 215 | public Builder withCellId(int cellId) { |
| 216 | this.cellId = cellId; |
| 217 | return this; |
| 218 | } |
| 219 | |
| 220 | /** |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 221 | * Set the number of packets and bytes that hit the counter in the dataplane ingress pipeline. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 222 | * |
| 223 | * @param ingressPkts ingress packet count |
| 224 | * @param ingressBytes egress packet count |
| 225 | * @return This builder |
| 226 | */ |
| 227 | public Builder setIngress(long ingressPkts, long ingressBytes) { |
| 228 | this.ingressPkts = ingressPkts; |
| 229 | this.ingressBytes = ingressBytes; |
| 230 | return this; |
| 231 | } |
| 232 | |
| 233 | /** |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 234 | * Set the number of packets and bytes that hit the counter in the dataplane egress pipeline. |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 235 | * |
| 236 | * @param egressPkts egress packet count |
| 237 | * @param egressBytes egress byte count |
| 238 | * @return This builder |
| 239 | */ |
| 240 | public Builder setEgress(long egressPkts, long egressBytes) { |
| 241 | this.egressPkts = egressPkts; |
| 242 | this.egressBytes = egressBytes; |
| 243 | return this; |
| 244 | } |
| 245 | |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 246 | /** |
| 247 | * Set the counter as ingress only counter. |
| 248 | * |
| 249 | * @return This builder |
| 250 | */ |
| 251 | public Builder isIngressCounter() { |
| 252 | this.type = INGRESS_COUNTER; |
| 253 | return this; |
| 254 | } |
| 255 | |
| 256 | /** |
| 257 | * Set the counter as egress only counter. |
| 258 | * |
| 259 | * @return This builder |
| 260 | */ |
| 261 | public Builder isEgressCounter() { |
| 262 | this.type = EGRESS_COUNTER; |
| 263 | return this; |
| 264 | } |
| 265 | |
tosinski | a57652d | 2021-11-22 16:53:00 +0100 | [diff] [blame] | 266 | public UpfCounter build() { |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 267 | checkNotNull(cellId, "CellID must be provided"); |
Daniele Moro | db0e125 | 2022-04-28 19:37:32 +0200 | [diff] [blame] | 268 | switch (type) { |
| 269 | case INGRESS_COUNTER: |
| 270 | checkArgument(this.ingressBytes != null && this.ingressPkts != null, |
| 271 | "Ingress counter values must be provided"); |
| 272 | this.egressBytes = null; |
| 273 | this.egressPkts = null; |
| 274 | break; |
| 275 | case EGRESS_COUNTER: |
| 276 | checkArgument(this.egressBytes != null && this.egressPkts != null, |
| 277 | "Egress counter values must be provided"); |
| 278 | this.ingressBytes = null; |
| 279 | this.ingressPkts = null; |
| 280 | break; |
| 281 | case COUNTER: |
| 282 | checkArgument(this.ingressBytes != null && this.ingressPkts != null && |
| 283 | this.egressBytes != null && this.egressPkts != null, |
| 284 | "Ingress and egress counter values must be provided"); |
| 285 | break; |
| 286 | default: |
| 287 | // I should never reach this point |
| 288 | throw new IllegalArgumentException("I should never reach this point!"); |
| 289 | } |
| 290 | return new UpfCounter(cellId, ingressPkts, ingressBytes, egressPkts, egressBytes, type); |
Daniele Moro | dbcffda | 2021-06-11 16:41:48 +0200 | [diff] [blame] | 291 | } |
| 292 | } |
| 293 | } |