blob: 7d715ee1947a5376f2b70cbde03ce09b24c5e24f [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;
27import org.onosproject.net.pi.model.PiPipeconf;
28import org.onosproject.net.pi.runtime.PiCounterCellData;
29import org.onosproject.net.pi.runtime.PiCounterCellId;
30import org.onosproject.net.pi.runtime.PiCounterId;
31import org.onosproject.net.pi.runtime.PiIndirectCounterCellId;
32import org.onosproject.net.pi.runtime.PiPipeconfService;
33import 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;
42import java.util.concurrent.ExecutionException;
43import java.util.stream.Collectors;
44
45import static org.onosproject.net.pi.runtime.PiCounterType.INDIRECT;
46
47/**
48 * Implementation of the PortStatisticsDiscovery behaviour for the main.p4 program. This behaviour works by using a
49 * P4Runtime client to read the values of the ingress/egress port counters defined in the P4 program.
50 */
51public final class PortStatisticsDiscoveryImpl extends AbstractHandlerBehaviour implements PortStatisticsDiscovery {
52
53 private static final Logger log = LoggerFactory.getLogger(PortStatisticsDiscoveryImpl.class);
54
55 private static final PiCounterId INGRESS_COUNTER_ID = PiCounterId.of("igr_port_counter", INDIRECT);
56 private static final PiCounterId EGRESS_COUNTER_ID = PiCounterId.of("egr_port_counter", INDIRECT);
57
58 @Override
59 public Collection<PortStatistics> discoverPortStatistics() {
60
61 DeviceService deviceService = this.handler().get(DeviceService.class);
62 DeviceId deviceId = this.data().deviceId();
63
64 // Get a client for this device.
65 P4RuntimeController controller = handler().get(P4RuntimeController.class);
66 if (!controller.hasClient(deviceId)) {
67 log.warn("Unable to find client for {}, aborting operation", deviceId);
68 return Collections.emptyList();
69 }
70 P4RuntimeClient client = controller.getClient(deviceId);
71
72 // Get the pipeconf of this device.
73 PiPipeconfService piPipeconfService = handler().get(PiPipeconfService.class);
74 if (!piPipeconfService.ofDevice(deviceId).isPresent() ||
75 !piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).isPresent()) {
76 log.warn("Unable to get the pipeconf of {}, aborting operation", deviceId);
77 return Collections.emptyList();
78 }
79 PiPipeconf pipeconf = piPipeconfService.getPipeconf(piPipeconfService.ofDevice(deviceId).get()).get();
80
81 // Prepare PortStatistics objects to return, one per port of this device.
82 Map<Long, DefaultPortStatistics.Builder> portStatBuilders = Maps.newHashMap();
83 deviceService
84 .getPorts(deviceId)
85 .forEach(p -> portStatBuilders.put(p.number().toLong(),
86 DefaultPortStatistics.builder()
87 .setPort(p.number())
88 .setDeviceId(deviceId)));
89
90 // Generate the counter cell IDs.
91 Set<PiCounterCellId> counterCellIds = Sets.newHashSet();
92 portStatBuilders.keySet().forEach(p -> {
93 // Counter cell/index = port number.
94 counterCellIds.add(PiIndirectCounterCellId.of(INGRESS_COUNTER_ID, p));
95 counterCellIds.add(PiIndirectCounterCellId.of(EGRESS_COUNTER_ID, p));
96 });
97
98 // Query the device.
99 Collection<PiCounterCellData> counterEntryResponse;
100 try {
101 counterEntryResponse = client.readCounterCells(counterCellIds, pipeconf).get();
102 } catch (InterruptedException | ExecutionException e) {
103 log.warn("Exception while reading port counters from {}: {}", deviceId, e.toString());
104 log.debug("", e);
105 return Collections.emptyList();
106 }
107
108 // Process response.
109 counterEntryResponse.forEach(counterData -> {
110 if (counterData.cellId().type() != INDIRECT) {
111 log.warn("Invalid counter data type {}, skipping", counterData.cellId().type());
112 return;
113 }
114 PiIndirectCounterCellId indCellId = (PiIndirectCounterCellId) counterData.cellId();
115 if (!portStatBuilders.containsKey(indCellId.index())) {
116 log.warn("Unrecognized counter index {}, skipping", counterData);
117 return;
118 }
119 DefaultPortStatistics.Builder statsBuilder = portStatBuilders.get(indCellId.index());
120 if (counterData.cellId().counterId().equals(INGRESS_COUNTER_ID)) {
121 statsBuilder.setPacketsReceived(counterData.packets());
122 statsBuilder.setBytesReceived(counterData.bytes());
123 } else if (counterData.cellId().counterId().equals(EGRESS_COUNTER_ID)) {
124 statsBuilder.setPacketsSent(counterData.packets());
125 statsBuilder.setBytesSent(counterData.bytes());
126 } else {
127 log.warn("Unrecognized counter ID {}, skipping", counterData);
128 }
129 });
130
131 return portStatBuilders
132 .values()
133 .stream()
134 .map(DefaultPortStatistics.Builder::build)
135 .collect(Collectors.toList());
136 }
137}