blob: ff48b57d556a1bcf826175da3ef8be5525fc8a55 [file] [log] [blame]
Carmelo Casconeb045ddc2017-09-01 01:26:35 +02001/*
2 * Copyright 2017-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
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070017package org.onosproject.pipelines.basic;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020018
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
Carmelo Cascone07bc58e2018-08-21 17:06:38 -070021import org.apache.commons.lang3.tuple.Pair;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070022import org.onosproject.net.DeviceId;
Carmelo Cascone07bc58e2018-08-21 17:06:38 -070023import org.onosproject.net.PortNumber;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020024import org.onosproject.net.device.DefaultPortStatistics;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070025import org.onosproject.net.device.DeviceService;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020026import org.onosproject.net.device.PortStatistics;
27import org.onosproject.net.device.PortStatisticsDiscovery;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070028import org.onosproject.net.driver.AbstractHandlerBehaviour;
Carmelo Cascone87892e22017-11-13 16:01:29 -080029import org.onosproject.net.pi.model.PiCounterId;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070030import org.onosproject.net.pi.model.PiPipeconf;
steven308017632e152018-10-20 00:51:08 +080031import org.onosproject.net.pi.runtime.PiCounterCell;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080032import org.onosproject.net.pi.runtime.PiCounterCellHandle;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020033import org.onosproject.net.pi.runtime.PiCounterCellId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080034import org.onosproject.net.pi.service.PiPipeconfService;
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070035import org.onosproject.p4runtime.api.P4RuntimeClient;
36import org.onosproject.p4runtime.api.P4RuntimeController;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020039
40import java.util.Collection;
41import java.util.Collections;
42import java.util.Map;
43import java.util.Set;
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020044import java.util.stream.Collectors;
45
Carmelo Cascone87892e22017-11-13 16:01:29 -080046import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
Carmelo Cascone776be382018-12-12 19:03:57 -080047import static org.onosproject.pipelines.basic.BasicConstants.EGRESS_PORT_COUNTERS_EGRESS_EGRESS_PORT_COUNTER;
48import static org.onosproject.pipelines.basic.BasicConstants.INGRESS_PORT_COUNTERS_INGRESS_INGRESS_PORT_COUNTER;
Carmelo Cascone7f75be42017-09-07 14:37:02 +020049
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020050/**
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070051 * Implementation of the PortStatisticsBehaviour for basic.p4.
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020052 */
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070053public class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020054
Carmelo Casconec2be50a2019-04-10 00:15:39 -070055 private static final long DEFAULT_P4_DEVICE_ID = 1;
Carmelo Cascone07bc58e2018-08-21 17:06:38 -070056 private static final Map<Pair<DeviceId, PortNumber>, Long> PORT_START_TIMES =
57 Maps.newConcurrentMap();
58
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070059 protected final Logger log = LoggerFactory.getLogger(getClass());
60
Carmelo Casconecb0a49c2017-10-03 14:32:23 +020061 /**
Carmelo Cascone2b057522017-10-04 14:35:29 +020062 * Returns the ID of the ingress port counter.
Carmelo Casconecb0a49c2017-10-03 14:32:23 +020063 *
Carmelo Cascone2b057522017-10-04 14:35:29 +020064 * @return counter ID
Carmelo Casconecb0a49c2017-10-03 14:32:23 +020065 */
Carmelo Cascone2b057522017-10-04 14:35:29 +020066 public PiCounterId ingressCounterId() {
Carmelo Cascone776be382018-12-12 19:03:57 -080067 return INGRESS_PORT_COUNTERS_INGRESS_INGRESS_PORT_COUNTER;
Carmelo Cascone2b057522017-10-04 14:35:29 +020068 }
69
70 /**
71 * Returns the ID of the egress port counter.
72 *
73 * @return counter ID
74 */
75 public PiCounterId egressCounterId() {
Carmelo Cascone776be382018-12-12 19:03:57 -080076 return EGRESS_PORT_COUNTERS_EGRESS_EGRESS_PORT_COUNTER;
Carmelo Casconecb0a49c2017-10-03 14:32:23 +020077 }
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020078
79 @Override
80 public Collection<PortStatistics> discoverPortStatistics() {
81
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070082 DeviceService deviceService = this.handler().get(DeviceService.class);
83 DeviceId deviceId = this.data().deviceId();
84
85 PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
86 if (!piPipeconfService.ofDevice(deviceId).isPresent() ||
87 !piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
88 log.warn("Unable to get the pipeconf of {}, aborting operation", deviceId);
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020089 return Collections.emptyList();
90 }
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070091 PiPipeconf pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
92
93 P4RuntimeController controller = handler().get(P4RuntimeController.class);
Carmelo Casconec2be50a2019-04-10 00:15:39 -070094 P4RuntimeClient client = controller.get(deviceId);
Carmelo Cascone158b8c42018-07-04 19:42:37 +020095 if (client == null) {
Carmelo Casconeca94bcf2017-10-27 14:16:59 -070096 log.warn("Unable to find client for {}, aborting operation", deviceId);
97 return Collections.emptyList();
98 }
Carmelo Casconeb045ddc2017-09-01 01:26:35 +020099
100 Map<Long, DefaultPortStatistics.Builder> portStatBuilders = Maps.newHashMap();
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200101 deviceService.getPorts(deviceId)
Carmelo Cascone07bc58e2018-08-21 17:06:38 -0700102 .forEach(p -> portStatBuilders.put(
103 p.number().toLong(),
104 DefaultPortStatistics.builder()
105 .setPort(p.number())
106 .setDeviceId(deviceId)
107 .setDurationSec(getDuration(p.number()))));
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200108
109 Set<PiCounterCellId> counterCellIds = Sets.newHashSet();
110 portStatBuilders.keySet().forEach(p -> {
111 // Counter cell/index = port number.
Carmelo Cascone87892e22017-11-13 16:01:29 -0800112 counterCellIds.add(PiCounterCellId.ofIndirect(ingressCounterId(), p));
113 counterCellIds.add(PiCounterCellId.ofIndirect(egressCounterId(), p));
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200114 });
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800115 Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
116 .map(id -> PiCounterCellHandle.of(deviceId, id))
117 .collect(Collectors.toSet());
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200118
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800119 // Query the device.
Carmelo Casconec2be50a2019-04-10 00:15:39 -0700120 Collection<PiCounterCell> counterEntryResponse = client.read(
121 DEFAULT_P4_DEVICE_ID, pipeconf)
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800122 .handles(counterCellHandles).submitSync()
123 .all(PiCounterCell.class);
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200124
steven308017632e152018-10-20 00:51:08 +0800125 counterEntryResponse.forEach(counterCell -> {
126 if (counterCell.cellId().counterType() != INDIRECT) {
127 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200128 return;
129 }
steven308017632e152018-10-20 00:51:08 +0800130 PiCounterCellId indCellId = counterCell.cellId();
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200131 if (!portStatBuilders.containsKey(indCellId.index())) {
steven308017632e152018-10-20 00:51:08 +0800132 log.warn("Unrecognized counter index {}, skipping", counterCell);
Carmelo Cascone7f75be42017-09-07 14:37:02 +0200133 return;
134 }
135 DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(indCellId.index());
steven308017632e152018-10-20 00:51:08 +0800136 if (counterCell.cellId().counterId().equals(ingressCounterId())) {
137 statsBuilder.setPacketsReceived(counterCell.data().packets());
138 statsBuilder.setBytesReceived(counterCell.data().bytes());
139 } else if (counterCell.cellId().counterId().equals(egressCounterId())) {
140 statsBuilder.setPacketsSent(counterCell.data().packets());
141 statsBuilder.setBytesSent(counterCell.data().bytes());
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200142 } else {
steven308017632e152018-10-20 00:51:08 +0800143 log.warn("Unrecognized counter ID {}, skipping", counterCell);
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200144 }
Carmelo Cascone07bc58e2018-08-21 17:06:38 -0700145
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200146 });
147
148 return portStatBuilders
149 .values()
150 .stream()
151 .map(DefaultPortStatistics.Builder::build)
152 .collect(Collectors.toList());
153 }
Carmelo Cascone07bc58e2018-08-21 17:06:38 -0700154
155 private long getDuration(PortNumber port) {
156 // FIXME: This is a workaround since we cannot determine the port
157 // duration from a P4 counter. We'll be fixed by gNMI.
158 final long now = System.currentTimeMillis() / 1000;
159 final Long startTime = PORT_START_TIMES.putIfAbsent(
160 Pair.of(data().deviceId(), port), now);
161 return startTime == null ? now : now - startTime;
162 }
Carmelo Casconeb045ddc2017-09-01 01:26:35 +0200163}