blob: 81c59b4e0fd571d1feefc0e7092c6c1e246bd1e8 [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;
pierventref497d132022-02-15 12:25:27 +010028import org.onosproject.gnmi.ctl.GnmiControllerImpl;
Carmelo Casconec2be50a2019-04-10 00:15:39 -070029import org.onosproject.grpc.utils.AbstractGrpcHandlerBehaviour;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080030import org.onosproject.net.AnnotationKeys;
Yi Tseng27851e32018-11-01 18:30:04 -070031import org.onosproject.net.DefaultAnnotations;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080032import org.onosproject.net.Device;
Yi Tseng27851e32018-11-01 18:30:04 -070033import org.onosproject.net.PortNumber;
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080034import org.onosproject.net.device.DefaultDeviceDescription;
Yi Tseng27851e32018-11-01 18:30:04 -070035import org.onosproject.net.device.DefaultPortDescription;
36import org.onosproject.net.device.DeviceDescription;
37import org.onosproject.net.device.DeviceDescriptionDiscovery;
38import org.onosproject.net.device.PortDescription;
39import org.slf4j.Logger;
40import org.slf4j.LoggerFactory;
41
Yi Tseng27851e32018-11-01 18:30:04 -070042import java.util.Collections;
43import java.util.List;
44import java.util.Map;
Yi Tseng27851e32018-11-01 18:30:04 -070045
46import static gnmi.Gnmi.Path;
47import static gnmi.Gnmi.PathElem;
48import static gnmi.Gnmi.Update;
49
50/**
51 * Class that discovers the device description and ports of a device that
52 * supports the gNMI protocol and Openconfig models.
53 */
54public class OpenConfigGnmiDeviceDescriptionDiscovery
Carmelo Casconec2be50a2019-04-10 00:15:39 -070055 extends AbstractGrpcHandlerBehaviour<GnmiClient, GnmiController>
Yi Tseng27851e32018-11-01 18:30:04 -070056 implements DeviceDescriptionDiscovery {
57
Yi Tseng27851e32018-11-01 18:30:04 -070058 private static final Logger log = LoggerFactory
59 .getLogger(OpenConfigGnmiDeviceDescriptionDiscovery.class);
60
pierventred03459c2021-06-01 12:26:36 +020061 private static final String LAST_CHANGE = "last-change";
Yi Tsengca5cfe72022-02-07 11:42:12 -080062 private static final String SDK_PORT = "sdk-port";
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080063
64 private static final String UNKNOWN = "unknown";
Yi Tseng59d5f3e2018-11-27 23:09:41 -080065
pierventref497d132022-02-15 12:25:27 +010066 private GnmiController gnmiController;
pierventrec19294d2022-02-02 22:33:41 +010067
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
pierventref497d132022-02-15 12:25:27 +010073 protected boolean setupBehaviour(String opName) {
74 if (!super.setupBehaviour(opName)) {
75 return false;
76 }
77
78 gnmiController = handler().get(GnmiController.class);
79 return true;
80 }
81
82 @Override
Yi Tseng27851e32018-11-01 18:30:04 -070083 public DeviceDescription discoverDeviceDetails() {
Carmelo Casconeab5d41e2019-03-06 18:02:34 -080084 return new DefaultDeviceDescription(
85 data().deviceId().uri(),
86 Device.Type.SWITCH,
87 data().driver().manufacturer(),
88 data().driver().hwVersion(),
89 data().driver().swVersion(),
90 UNKNOWN,
91 new ChassisId(),
92 true,
93 DefaultAnnotations.builder()
94 .set(AnnotationKeys.PROTOCOL, "gNMI")
95 .build());
Yi Tseng27851e32018-11-01 18:30:04 -070096 }
97
98 @Override
99 public List<PortDescription> discoverPortDetails() {
Carmelo Casconec32976e2019-04-08 14:50:52 -0700100 if (!setupBehaviour("discoverPortDetails()")) {
Yi Tseng27851e32018-11-01 18:30:04 -0700101 return Collections.emptyList();
102 }
103 log.debug("Discovering port details on device {}", handler().data().deviceId());
104
Carmelo Casconeab5d41e2019-03-06 18:02:34 -0800105 final GetResponse response = Futures.getUnchecked(client.get(buildPortStateRequest()));
Yi Tseng27851e32018-11-01 18:30:04 -0700106
Yi Tsengd7716482018-10-31 15:34:30 -0700107 final Map<String, DefaultPortDescription.Builder> ports = Maps.newHashMap();
108 final Map<String, DefaultAnnotations.Builder> annotations = Maps.newHashMap();
pierventre374e2612022-01-31 13:14:46 +0100109 final Map<String, PortNumber> portIds = Maps.newHashMap();
Yi Tseng27851e32018-11-01 18:30:04 -0700110
111 // Creates port descriptions with port name and port number
112 response.getNotificationList()
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800113 .forEach(notification -> {
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800114 notification.getUpdateList().forEach(update -> {
115 // /interfaces/interface[name=ifName]/state/...
116 final String ifName = update.getPath().getElem(1)
117 .getKeyMap().get("name");
118 if (!ports.containsKey(ifName)) {
119 ports.put(ifName, DefaultPortDescription.builder());
120 annotations.put(ifName, DefaultAnnotations.builder());
121 }
122 final DefaultPortDescription.Builder builder = ports.get(ifName);
123 final DefaultAnnotations.Builder annotationsBuilder = annotations.get(ifName);
pierventre374e2612022-01-31 13:14:46 +0100124 parseInterfaceInfo(update, ifName, builder, annotationsBuilder, portIds);
Yi Tseng59d5f3e2018-11-27 23:09:41 -0800125 });
Yi Tseng27851e32018-11-01 18:30:04 -0700126 });
127
Yi Tsengd7716482018-10-31 15:34:30 -0700128 final List<PortDescription> portDescriptionList = Lists.newArrayList();
Yi Tseng27851e32018-11-01 18:30:04 -0700129 ports.forEach((key, value) -> {
pierventred03459c2021-06-01 12:26:36 +0200130 // For devices not providing last-change, we set it to 0
131 final DefaultAnnotations.Builder annotationsBuilder = annotations.get(key);
132 if (!annotationsBuilder.build().keys().contains(LAST_CHANGE)) {
133 annotationsBuilder.set(LAST_CHANGE, String.valueOf(0));
134 }
pierventrec19294d2022-02-02 22:33:41 +0100135 /* Override port number if read port-id is enabled
136 and /interfaces/interface/state/id is available */
pierventref497d132022-02-15 12:25:27 +0100137 if (readPortId() && portIds.containsKey(key)) {
pierventre374e2612022-01-31 13:14:46 +0100138 value.withPortNumber(portIds.get(key));
139 }
Yi Tseng27851e32018-11-01 18:30:04 -0700140 DefaultAnnotations annotation = annotations.get(key).build();
141 portDescriptionList.add(value.annotations(annotation).build());
142 });
pierventre374e2612022-01-31 13:14:46 +0100143
Yi Tseng27851e32018-11-01 18:30:04 -0700144 return portDescriptionList;
145 }
146
pierventref497d132022-02-15 12:25:27 +0100147 private boolean readPortId() {
148 // FIXME temporary solution will be substituted by
149 // an XML driver property when the transition to
150 // p4rt translation is completed
151 return ((GnmiControllerImpl) gnmiController).readPortId();
152 }
153
Yi Tseng27851e32018-11-01 18:30:04 -0700154 private GetRequest buildPortStateRequest() {
155 Path path = Path.newBuilder()
156 .addElem(PathElem.newBuilder().setName("interfaces").build())
157 .addElem(PathElem.newBuilder().setName("interface").putKey("name", "...").build())
158 .addElem(PathElem.newBuilder().setName("state").build())
159 .build();
160 return GetRequest.newBuilder()
161 .addPath(path)
162 .setType(GetRequest.DataType.ALL)
163 .setEncoding(Gnmi.Encoding.PROTO)
164 .build();
165 }
166
167 /**
168 * Parses the interface information.
169 *
Yi Tsengd7716482018-10-31 15:34:30 -0700170 * @param update the update received
Yi Tseng27851e32018-11-01 18:30:04 -0700171 */
172 private void parseInterfaceInfo(Update update,
173 String ifName,
174 DefaultPortDescription.Builder builder,
pierventre374e2612022-01-31 13:14:46 +0100175 DefaultAnnotations.Builder annotationsBuilder,
176 Map<String, PortNumber> portIds) {
Yi Tseng27851e32018-11-01 18:30:04 -0700177
Yi Tsengd7716482018-10-31 15:34:30 -0700178 final Path path = update.getPath();
179 final List<PathElem> elems = path.getElemList();
180 final Gnmi.TypedValue val = update.getVal();
Yi Tseng27851e32018-11-01 18:30:04 -0700181 if (elems.size() == 4) {
pierventre374e2612022-01-31 13:14:46 +0100182 /* /interfaces/interface/state/ifindex
183 /interfaces/interface/state/oper-status
184 /interfaces/interface/state/last-change
185 /interfaces/interface/state/id */
Yi Tsengd7716482018-10-31 15:34:30 -0700186 final String pathElemName = elems.get(3).getName();
Yi Tseng27851e32018-11-01 18:30:04 -0700187 switch (pathElemName) {
188 case "ifindex": // port number
189 builder.withPortNumber(PortNumber.portNumber(val.getUintVal(), ifName));
Yi Tsengca5cfe72022-02-07 11:42:12 -0800190 annotationsBuilder.set(SDK_PORT, String.valueOf(val.getUintVal()));
Yi Tsengd7716482018-10-31 15:34:30 -0700191 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700192 case "oper-status":
193 builder.isEnabled(parseOperStatus(val.getStringVal()));
pierventred03459c2021-06-01 12:26:36 +0200194 return;
195 case "last-change":
196 annotationsBuilder.set(LAST_CHANGE, String.valueOf(val.getUintVal()));
Yi Tsengd7716482018-10-31 15:34:30 -0700197 return;
pierventre374e2612022-01-31 13:14:46 +0100198 case "id":
199 /* Temporary stored in portIds and eventually substituted
200 when all updates have been processed. This is done because
201 there is no guarantee about the order of the updates delivery */
202 portIds.put(ifName, PortNumber.portNumber(val.getUintVal(), ifName));
203 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700204 default:
Yi Tseng27851e32018-11-01 18:30:04 -0700205 break;
206 }
Yi Tsengd7716482018-10-31 15:34:30 -0700207 } else if (elems.size() == 5) {
Yi Tseng27851e32018-11-01 18:30:04 -0700208 // /interfaces/interface/ethernet/config/port-speed
Yi Tsengd7716482018-10-31 15:34:30 -0700209 final String pathElemName = elems.get(4).getName();
210 if (pathElemName.equals("port-speed")) {
211 builder.portSpeed(parsePortSpeed(val.getStringVal()));
212 return;
Yi Tseng27851e32018-11-01 18:30:04 -0700213 }
214 }
Yi Tsengd7716482018-10-31 15:34:30 -0700215 log.debug("Unknown path when parsing interface info: {}", path);
Yi Tseng27851e32018-11-01 18:30:04 -0700216 }
217
218 private boolean parseOperStatus(String operStatus) {
219 switch (operStatus) {
220 case "UP":
221 return true;
222 case "DOWN":
223 default:
224 return false;
225 }
226 }
227
228 private long parsePortSpeed(String speed) {
229 log.debug("Speed from config {}", speed);
230 switch (speed) {
231 case "SPEED_10MB":
232 return 10;
233 case "SPEED_100MB":
234 return 100;
235 case "SPEED_1GB":
236 return 1000;
237 case "SPEED_10GB":
238 return 10000;
239 case "SPEED_25GB":
240 return 25000;
241 case "SPEED_40GB":
242 return 40000;
243 case "SPEED_50GB":
244 return 50000;
245 case "SPEED_100GB":
246 return 100000;
247 default:
Yi Tsengd7716482018-10-31 15:34:30 -0700248 log.warn("Unrecognized port speed string '{}'", speed);
Yi Tseng27851e32018-11-01 18:30:04 -0700249 return 1000;
250 }
251 }
252}