blob: 48e7b134565a7197cc56f949e4eb105ff69d2ca8 [file] [log] [blame]
Carmelo Cascone770507f2017-09-14 20:58:04 +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
17package org.onosproject.p4tutorial.pipeconf;
18
19import com.google.common.collect.Maps;
20import com.google.common.collect.Sets;
21import org.onosproject.net.DeviceId;
22import org.onosproject.net.device.DefaultPortStatistics;
23import org.onosproject.net.device.DeviceService;
24import org.onosproject.net.device.PortStatistics;
25import org.onosproject.net.device.PortStatisticsDiscovery;
26import org.onosproject.net.driver.AbstractHandlerBehaviour;
Carmelo Cascone87892e22017-11-13 16:01:29 -080027import org.onosproject.net.pi.model.PiCounterId;
Carmelo Cascone770507f2017-09-14 20:58:04 +020028import org.onosproject.net.pi.model.PiPipeconf;
steven308017632e152018-10-20 00:51:08 +080029import org.onosproject.net.pi.runtime.PiCounterCell;
Carmelo Cascone4c289b72019-01-22 15:30:45 -080030import org.onosproject.net.pi.runtime.PiCounterCellHandle;
Carmelo Cascone770507f2017-09-14 20:58:04 +020031import org.onosproject.net.pi.runtime.PiCounterCellId;
Carmelo Cascone39c28ca2017-11-15 13:03:57 -080032import org.onosproject.net.pi.service.PiPipeconfService;
Carmelo Cascone770507f2017-09-14 20:58:04 +020033import org.onosproject.p4runtime.api.P4RuntimeClient;
34import org.onosproject.p4runtime.api.P4RuntimeController;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
38import java.util.Collection;
39import java.util.Collections;
40import java.util.Map;
41import java.util.Set;
Carmelo Cascone770507f2017-09-14 20:58:04 +020042import java.util.stream.Collectors;
43
Carmelo Cascone87892e22017-11-13 16:01:29 -080044import static org.onosproject.net.pi.model.PiCounterType.INDIRECT;
Carmelo Cascone770507f2017-09-14 20:58:04 +020045
46/**
Carmelo Cascone700648c2018-04-11 12:02:16 -070047 * Implementation of the PortStatisticsDiscovery behaviour for the mytunnel.p4 program. This behaviour works by using a
Carmelo Cascone770507f2017-09-14 20:58:04 +020048 * P4Runtime client to read the values of the ingress/egress port counters defined in the P4 program.
49 */
50public final class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
51
52 private static final Logger log = LoggerFactory.getLogger(PortStatisticsDiscoveryImpl.class);
53
Carmelo Cascone700648c2018-04-11 12:02:16 -070054 private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("c_ingress.rx_port_counter");
55 private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("c_ingress.tx_port_counter");
Carmelo Cascone770507f2017-09-14 20:58:04 +020056
57 @Override
58 public Collection<PortStatistics> discoverPortStatistics() {
59
60 DeviceService deviceService = this.handler().get(DeviceService.class);
61 DeviceId deviceId = this.data().deviceId();
62
63 // Get a client for this device.
64 P4RuntimeController controller = handler().get(P4RuntimeController.class);
Carmelo Cascone158b8c42018-07-04 19:42:37 +020065 P4RuntimeClient client = controller.getClient(deviceId);
66 if (client == null) {
Carmelo Cascone770507f2017-09-14 20:58:04 +020067 log.warn("Unable to find client for {}, aborting operation", deviceId);
68 return Collections.emptyList();
69 }
Carmelo Cascone770507f2017-09-14 20:58:04 +020070
71 // Get the pipeconf of this device.
72 PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
73 if (!piPipeconfService.ofDevice(deviceId).isPresent() ||
74 !piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
75 log.warn("Unable to get the pipeconf of {}, aborting operation", deviceId);
76 return Collections.emptyList();
77 }
78 PiPipeconf pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
79
80 // Prepare PortStatistics objects to return, one per port of this device.
81 Map<Long, DefaultPortStatistics.Builder> portStatBuilders = Maps.newHashMap();
82 deviceService
83 .getPorts(deviceId)
84 .forEach(p -> portStatBuilders.put(p.number().toLong(),
85 DefaultPortStatistics.builder()
86 .setPort(p.number())
87 .setDeviceId(deviceId)));
88
89 // Generate the counter cell IDs.
90 Set<PiCounterCellId> counterCellIds = Sets.newHashSet();
91 portStatBuilders.keySet().forEach(p -> {
92 // Counter cell/index = port number.
Carmelo Cascone87892e22017-11-13 16:01:29 -080093 counterCellIds.add(PiCounterCellId.ofIndirect(INGRESS_COUNTER_ID, p));
94 counterCellIds.add(PiCounterCellId.ofIndirect(EGRESS_COUNTER_ID, p));
Carmelo Cascone770507f2017-09-14 20:58:04 +020095 });
Carmelo Cascone4c289b72019-01-22 15:30:45 -080096 Set<PiCounterCellHandle> counterCellHandles = counterCellIds.stream()
97 .map(id -> PiCounterCellHandle.of(deviceId, id))
98 .collect(Collectors.toSet());
Carmelo Cascone770507f2017-09-14 20:58:04 +020099
100 // Query the device.
Carmelo Cascone4c289b72019-01-22 15:30:45 -0800101 Collection<PiCounterCell> counterEntryResponse = client.read(pipeconf)
102 .handles(counterCellHandles).submitSync()
103 .all(PiCounterCell.class);
Carmelo Cascone770507f2017-09-14 20:58:04 +0200104
105 // Process response.
steven308017632e152018-10-20 00:51:08 +0800106 counterEntryResponse.forEach(counterCell -> {
107 if (counterCell.cellId().counterType() != INDIRECT) {
108 log.warn("Invalid counter data type {}, skipping", counterCell.cellId().counterType());
Carmelo Cascone770507f2017-09-14 20:58:04 +0200109 return;
110 }
steven308017632e152018-10-20 00:51:08 +0800111 if (!portStatBuilders.containsKey(counterCell.cellId().index())) {
112 log.warn("Unrecognized counter index {}, skipping", counterCell);
Carmelo Cascone770507f2017-09-14 20:58:04 +0200113 return;
114 }
steven308017632e152018-10-20 00:51:08 +0800115 DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(counterCell.cellId().index());
116 if (counterCell.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
117 statsBuilder.setPacketsReceived(counterCell.data().packets());
118 statsBuilder.setBytesReceived(counterCell.data().bytes());
119 } else if (counterCell.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
120 statsBuilder.setPacketsSent(counterCell.data().packets());
121 statsBuilder.setBytesSent(counterCell.data().bytes());
Carmelo Cascone770507f2017-09-14 20:58:04 +0200122 } else {
steven308017632e152018-10-20 00:51:08 +0800123 log.warn("Unrecognized counter ID {}, skipping", counterCell);
Carmelo Cascone770507f2017-09-14 20:58:04 +0200124 }
125 });
126
127 return portStatBuilders
128 .values()
129 .stream()
130 .map(DefaultPortStatistics.Builder::build)
131 .collect(Collectors.toList());
132 }
133}