blob: ee7ccc5df9604e333f283f53fcf14466f28bd170 [file] [log] [blame]
Yi Tseng27851e32018-11-01 18:30:04 -07001/*
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.drivers.gnmi;
18
19import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080021import com.google.common.util.concurrent.Futures;
Yi Tseng27851e32018-11-01 18:30:04 -070022import gnmi.Gnmi;
23import gnmi.Gnmi.GetRequest;
24import gnmi.Gnmi.GetResponse;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080025import org.onlab.packet.ChassisId;
Carmelo Casconec2be50a2019-04-10 00:15:39 -070026import org.onosproject.gnmi.api.GnmiClient;
27import org.onosproject.gnmi.api.GnmiController;
28import org.onosproject.grpc.utils.AbstractGrpcHandlerBehaviour;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080029import org.onosproject.net.AnnotationKeys;
Yi Tseng27851e32018-11-01 18:30:04 -070030import org.onosproject.net.DefaultAnnotations;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080031import org.onosproject.net.Device;
Yi Tseng27851e32018-11-01 18:30:04 -070032import org.onosproject.net.PortNumber;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080033import org.onosproject.net.device.DefaultDeviceDescription;
Yi Tseng27851e32018-11-01 18:30:04 -070034import org.onosproject.net.device.DefaultPortDescription;
35import org.onosproject.net.device.DeviceDescription;
36import org.onosproject.net.device.DeviceDescriptionDiscovery;
37import org.onosproject.net.device.PortDescription;
38import org.slf4j.Logger;
39import org.slf4j.LoggerFactory;
40
Yi Tseng27851e32018-11-01 18:30:04 -070041import java.util.Collections;
42import java.util.List;
43import java.util.Map;
Yi Tseng27851e32018-11-01 18:30:04 -070044
45import static gnmi.Gnmi.Path;
46import static gnmi.Gnmi.PathElem;
47import static gnmi.Gnmi.Update;
48
49/**
50 * Class that discovers the device description and ports of a device that
51 * supports the gNMI protocol and Openconfig models.
52 */
53public class OpenConfigGnmiDeviceDescriptionDiscovery
Carmelo Casconec2be50a2019-04-10 00:15:39 -070054 extends AbstractGrpcHandlerBehaviour<GnmiClient, GnmiController>
Yi Tseng27851e32018-11-01 18:30:04 -070055 implements DeviceDescriptionDiscovery {
56
Yi Tseng27851e32018-11-01 18:30:04 -070057 private static final Logger log = LoggerFactory
58 .getLogger(OpenConfigGnmiDeviceDescriptionDiscovery.class);
59
pierventre50696a72021-06-01 12:26:36 +020060 private static final String LAST_CHANGE = "last-change";
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080061
62 private static final String UNKNOWN = "unknown";
Yi Tseng59d5f3e2018-11-27 23:09:41 -080063
pierventre0f663b02022-02-02 22:33:41 +010064 // FIXME temporary solution will be removed when the
65 // transition to p4rt translation is completed
66 public static boolean readPortId = false;
67
Carmelo Casconec2be50a2019-04-10 00:15:39 -070068 public OpenConfigGnmiDeviceDescriptionDiscovery() {
69 super(GnmiController.class);
70 }
71
Yi Tseng27851e32018-11-01 18:30:04 -070072 @Override
73 public DeviceDescription discoverDeviceDetails() {
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080074 return new DefaultDeviceDescription(
75 data().deviceId().uri(),
76 Device.Type.SWITCH,
77 data().driver().manufacturer(),
78 data().driver().hwVersion(),
79 data().driver().swVersion(),
80 UNKNOWN,
81 new ChassisId(),
82 true,
83 DefaultAnnotations.builder()
84 .set(AnnotationKeys.PROTOCOL, "gNMI")
85 .build());
Yi Tseng27851e32018-11-01 18:30:04 -070086 }
87
88 @Override
89 public List<PortDescription> discoverPortDetails() {
Carmelo Casconec32976e2019-04-08 14:50:52 -070090 if (!setupBehaviour("discoverPortDetails()")) {
Yi Tseng27851e32018-11-01 18:30:04 -070091 return Collections.emptyList();
92 }
93 log.debug("Discovering port details on device {}", handler().data().deviceId());
94
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080095 final GetResponse response = Futures.getUnchecked(client.get(buildPortStateRequest()));
Yi Tseng27851e32018-11-01 18:30:04 -070096
Yi Tsengd7716482018-10-31 15:34:30 -070097 final Map<String, DefaultPortDescription.Builder> ports = Maps.newHashMap();
98 final Map<String, DefaultAnnotations.Builder> annotations = Maps.newHashMap();
pierventreda5ccab2022-01-31 13:14:46 +010099 final Map<String, PortNumber> portIds = Maps.newHashMap();
Yi Tseng27851e32018-11-01 18:30:04 -0700100
101 // Creates port descriptions with port name and port number
102 response.getNotificationList()
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800103 .forEach(notification -> {
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800104 notification.getUpdateList().forEach(update -> {
105 // /interfaces/interface[name=ifName]/state/...
106 final String ifName = update.getPath().getElem(1)
107 .getKeyMap().get("name");
108 if (!ports.containsKey(ifName)) {
109 ports.put(ifName, DefaultPortDescription.builder());
110 annotations.put(ifName, DefaultAnnotations.builder());
111 }
112 final DefaultPortDescription.Builder builder = ports.get(ifName);
113 final DefaultAnnotations.Builder annotationsBuilder = annotations.get(ifName);
pierventreda5ccab2022-01-31 13:14:46 +0100114 parseInterfaceInfo(update, ifName, builder, annotationsBuilder, portIds);
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800115 });
Yi Tseng27851e32018-11-01 18:30:04 -0700116 });
117
Yi Tsengd7716482018-10-31 15:34:30 -0700118 final List<PortDescription> portDescriptionList = Lists.newArrayList();
Yi Tseng27851e32018-11-01 18:30:04 -0700119 ports.forEach((key, value) -> {
pierventre50696a72021-06-01 12:26:36 +0200120 // For devices not providing last-change, we set it to 0
121 final DefaultAnnotations.Builder annotationsBuilder = annotations.get(key);
122 if (!annotationsBuilder.build().keys().contains(LAST_CHANGE)) {
123 annotationsBuilder.set(LAST_CHANGE, String.valueOf(0));
124 }
pierventre0f663b02022-02-02 22:33:41 +0100125 /* Override port number if read port-id is enabled
126 and /interfaces/interface/state/id is available */
127 if (readPortId && portIds.containsKey(key)) {
pierventreda5ccab2022-01-31 13:14:46 +0100128 value.withPortNumber(portIds.get(key));
129 }
Yi Tseng27851e32018-11-01 18:30:04 -0700130 DefaultAnnotations annotation = annotations.get(key).build();
131 portDescriptionList.add(value.annotations(annotation).build());
132 });
pierventreda5ccab2022-01-31 13:14:46 +0100133
Yi Tseng27851e32018-11-01 18:30:04 -0700134 return portDescriptionList;
135 }
136
137 private GetRequest buildPortStateRequest() {
138 Path path = Path.newBuilder()
139 .addElem(PathElem.newBuilder().setName("interfaces").build())
140 .addElem(PathElem.newBuilder().setName("interface").putKey("name", "...").build())
141 .addElem(PathElem.newBuilder().setName("state").build())
142 .build();
143 return GetRequest.newBuilder()
144 .addPath(path)
145 .setType(GetRequest.DataType.ALL)
146 .setEncoding(Gnmi.Encoding.PROTO)
147 .build();
148 }
149
150 /**
151 * Parses the interface information.
152 *
Yi Tsengd7716482018-10-31 15:34:30 -0700153 * @param update the update received
Yi Tseng27851e32018-11-01 18:30:04 -0700154 */
155 private void parseInterfaceInfo(Update update,
156 String ifName,
157 DefaultPortDescription.Builder builder,
pierventreda5ccab2022-01-31 13:14:46 +0100158 DefaultAnnotations.Builder annotationsBuilder,
159 Map<String, PortNumber> portIds) {
Yi Tseng27851e32018-11-01 18:30:04 -0700160
Yi Tsengd7716482018-10-31 15:34:30 -0700161 final Path path = update.getPath();
162 final List<PathElem> elems = path.getElemList();
163 final Gnmi.TypedValue val = update.getVal();
Yi Tseng27851e32018-11-01 18:30:04 -0700164 if (elems.size() == 4) {
pierventreda5ccab2022-01-31 13:14:46 +0100165 /* /interfaces/interface/state/ifindex
166 /interfaces/interface/state/oper-status
167 /interfaces/interface/state/last-change
168 /interfaces/interface/state/id */
Yi Tsengd7716482018-10-31 15:34:30 -0700169 final String pathElemName = elems.get(3).getName();
Yi Tseng27851e32018-11-01 18:30:04 -0700170 switch (pathElemName) {
171 case "ifindex": // port number
172 builder.withPortNumber(PortNumber.portNumber(val.getUintVal(), ifName));
Yi Tsengd7716482018-10-31 15:34:30 -0700173 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700174 case "oper-status":
175 builder.isEnabled(parseOperStatus(val.getStringVal()));
pierventre50696a72021-06-01 12:26:36 +0200176 return;
177 case "last-change":
178 annotationsBuilder.set(LAST_CHANGE, String.valueOf(val.getUintVal()));
Yi Tsengd7716482018-10-31 15:34:30 -0700179 return;
pierventreda5ccab2022-01-31 13:14:46 +0100180 case "id":
181 /* Temporary stored in portIds and eventually substituted
182 when all updates have been processed. This is done because
183 there is no guarantee about the order of the updates delivery */
184 portIds.put(ifName, PortNumber.portNumber(val.getUintVal(), ifName));
185 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700186 default:
Yi Tseng27851e32018-11-01 18:30:04 -0700187 break;
188 }
Yi Tsengd7716482018-10-31 15:34:30 -0700189 } else if (elems.size() == 5) {
Yi Tseng27851e32018-11-01 18:30:04 -0700190 // /interfaces/interface/ethernet/config/port-speed
Yi Tsengd7716482018-10-31 15:34:30 -0700191 final String pathElemName = elems.get(4).getName();
192 if (pathElemName.equals("port-speed")) {
193 builder.portSpeed(parsePortSpeed(val.getStringVal()));
194 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700195 }
196 }
Yi Tsengd7716482018-10-31 15:34:30 -0700197 log.debug("Unknown path when parsing interface info: {}", path);
Yi Tseng27851e32018-11-01 18:30:04 -0700198 }
199
200 private boolean parseOperStatus(String operStatus) {
201 switch (operStatus) {
202 case "UP":
203 return true;
204 case "DOWN":
205 default:
206 return false;
207 }
208 }
209
210 private long parsePortSpeed(String speed) {
211 log.debug("Speed from config {}", speed);
212 switch (speed) {
213 case "SPEED_10MB":
214 return 10;
215 case "SPEED_100MB":
216 return 100;
217 case "SPEED_1GB":
218 return 1000;
219 case "SPEED_10GB":
220 return 10000;
221 case "SPEED_25GB":
222 return 25000;
223 case "SPEED_40GB":
224 return 40000;
225 case "SPEED_50GB":
226 return 50000;
227 case "SPEED_100GB":
228 return 100000;
229 default:
Yi Tsengd7716482018-10-31 15:34:30 -0700230 log.warn("Unrecognized port speed string '{}'", speed);
Yi Tseng27851e32018-11-01 18:30:04 -0700231 return 1000;
232 }
233 }
234}