blob: eea8b5906c960d35234555ceeb2e12e908a3af63 [file] [log] [blame]
Alessio Giorgetti648b5382018-02-15 18:35:45 +01001/*
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.lumentum;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
21
22import org.apache.commons.configuration.HierarchicalConfiguration;
23import org.apache.commons.configuration.XMLConfiguration;
24import org.apache.commons.lang3.StringUtils;
25
alessiod4a2b842019-04-30 18:43:17 +020026import org.onlab.packet.ChassisId;
27import org.onlab.util.Frequency;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010028import org.onosproject.drivers.utilities.XmlConfigParser;
alessiod4a2b842019-04-30 18:43:17 +020029import org.onosproject.net.ChannelSpacing;
30import org.onosproject.net.SparseAnnotations;
31import org.onosproject.net.DefaultAnnotations;
32import org.onosproject.net.DeviceId;
33import org.onosproject.net.Device;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010034import org.onosproject.net.Port;
35import org.onosproject.net.PortNumber;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010036import org.onosproject.net.AnnotationKeys;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010037import org.onosproject.net.device.DefaultDeviceDescription;
38import org.onosproject.net.device.DefaultPortDescription;
39import org.onosproject.net.device.DeviceDescription;
40import org.onosproject.net.device.DeviceDescriptionDiscovery;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.device.PortDescription;
43import org.onosproject.net.driver.AbstractHandlerBehaviour;
alessiod4a2b842019-04-30 18:43:17 +020044import org.onosproject.net.intent.OpticalPathIntent;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010045import org.onosproject.netconf.NetconfController;
46import org.onosproject.netconf.NetconfException;
47import org.onosproject.netconf.NetconfSession;
48import org.onosproject.netconf.NetconfDevice;
49
50import org.slf4j.Logger;
51
52import java.io.ByteArrayInputStream;
53import java.util.List;
54
55import static com.google.common.base.Preconditions.checkNotNull;
alessiod4a2b842019-04-30 18:43:17 +020056import static org.onosproject.net.optical.device.OmsPortHelper.omsPortDescription;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010057import static org.slf4j.LoggerFactory.getLogger;
58
59/**
60 * Device description behaviour for Lumentum ROADM-A Whitebox devices using NETCONF.
61 */
62public class LumentumNetconfRoadmDiscovery
63 extends AbstractHandlerBehaviour implements DeviceDescriptionDiscovery {
64
65 private static final String PHYSICAL_PORT = "data.physical-ports.physical-port";
66
67 private static final String DN = "dn";
68 private static final String DN_PORT = "port=";
69 private static final String PORT_EXTENSION = "port-extension";
70 protected static final String OPTICAL_INPUT = "port-optical-input";
71 protected static final String OPTICAL_OUTPUT = "port-optical-output";
72 private static final String PORT_PLUGGABLE = "port-pluggable";
73 private static final String PORT_ETHERNET = "port-ethernet";
74
75 private static final String MAINTENANCE_STATE = "config.maintenance-state";
76 private static final String PORT_SPEED = "config.loteeth:port-speed";
77 private static final String IN_SERVICE = "in-service";
78 private static final String PORT_NAME = "entity-description";
79
alessiod4a2b842019-04-30 18:43:17 +020080 public static final ChannelSpacing CHANNEL_SPACING_50 = ChannelSpacing.CHL_50GHZ;
81 public static final Frequency START_CENTER_FREQ_50 = Frequency.ofGHz(191_350);
82 public static final Frequency END_CENTER_FREQ_50 = Frequency.ofGHz(196_100);
83
84 private static final int MIN_MUX_PORT = 4101;
85 private static final int MAX_MUX_PORT = 4120;
86 private static final int MIN_DEM_PORT = 5201;
87 private static final int MAX_DEM_PORT = 5220;
88 private static final int DELTA_MUX_DEM_PORT = MIN_DEM_PORT - MIN_MUX_PORT;
89
Alessio Giorgetti648b5382018-02-15 18:35:45 +010090 private final Logger log = getLogger(getClass());
91
92 @Override
93 public DeviceDescription discoverDeviceDetails() {
alessiod4a2b842019-04-30 18:43:17 +020094 SparseAnnotations annotations = DefaultAnnotations.builder().build();
95
96 log.info("Lumentum NETCONF - starting discoverDeviceDetails");
Alessio Giorgetti648b5382018-02-15 18:35:45 +010097
98 // Some defaults values
99 String vendor = "Lumentum";
100 String hwVersion = "not loaded";
101 String swVersion = "not loaded";
102 String serialNumber = "not loaded";
alessiod4a2b842019-04-30 18:43:17 +0200103 String chassisData = "ne=1;chassis=10";
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100104
alessiod4a2b842019-04-30 18:43:17 +0200105 ChassisId chassisId = null;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100106 DeviceId deviceId = handler().data().deviceId();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100107
108 NetconfSession session = getNetconfSession();
109
110 if (session == null) {
111 log.error("Lumentum NETCONF - session not found for {}", deviceId);
112 return null;
113 }
114
115 //Retrieve system information from ietf-system
116 StringBuilder systemRequestBuilder = new StringBuilder();
117 systemRequestBuilder.append("<system-state xmlns=\"urn:ietf:params:xml:ns:yang:ietf-system\">");
118 systemRequestBuilder.append("</system-state>");
119
120 try {
121 String reply = session.get(systemRequestBuilder.toString(), null);
122 log.info("Lumentum NETCONF - session.get reply {}", reply);
123
124 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
125
126 vendor = xconf.getString("data.system-state.platform.machine", vendor);
127 swVersion = xconf.getString("data.system-state.platform.os-version", swVersion);
128 } catch (NetconfException e) {
129 log.error("Lumentum NETCONF error in session.get with filter <system-state>", e);
130 }
131
132 //Retrieve system information
133 StringBuilder chassisRequestBuilder = new StringBuilder();
134 chassisRequestBuilder.append("<chassis-list xmlns=\"http://www.lumentum.com/lumentum-ote-equipment\">");
135 chassisRequestBuilder.append("</chassis-list>");
136
137 try {
138 String reply = session.get(chassisRequestBuilder.toString(), null);
139 log.info("Lumentum NETCONF - session.get reply {}", reply);
140
141 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(reply);
142
143 hwVersion = xconf.getString("data.chassis-list.chassis.state.loteq:hardware-rev", hwVersion);
144 serialNumber = xconf.getString("data.chassis-list.chassis.state.loteq:serial-no", serialNumber);
alessiod4a2b842019-04-30 18:43:17 +0200145 chassisData = xconf.getString("data.chassis-list.chassis.dn", chassisData);
146
147 String[] parts = chassisData.split("chassis=");
148 chassisId = new ChassisId(Long.valueOf(parts[1], 10));
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100149
150 } catch (NetconfException e) {
151 log.error("Lumentum NETCONF error in session.get", e);
152 }
153
154 //Upon connection of a new devices all pre-configured connections are removed
155 //TODO: do not cancel and import already configured connections
156 rpcRemoveAllConnections("1");
157 rpcRemoveAllConnections("2");
158
159 log.info("TYPE {}", Device.Type.ROADM);
160 log.info("VENDOR {}", vendor);
161 log.info("HWVERSION {}", hwVersion);
162 log.info("SWVERSION {}", swVersion);
163 log.info("SERIAL {}", serialNumber);
164 log.info("CHASSISID {}", chassisId);
165
166 //Return the Device Description
167 return new DefaultDeviceDescription(deviceId.uri(), Device.Type.ROADM,
alessiod4a2b842019-04-30 18:43:17 +0200168 vendor, hwVersion, swVersion, serialNumber, chassisId, annotations);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100169 }
170
171 @Override
172 public List<PortDescription> discoverPortDetails() {
173 String reply;
174 DeviceId deviceId = handler().data().deviceId();
175 DeviceService deviceService = checkNotNull(handler().get(DeviceService.class));
176 Device device = deviceService.getDevice(deviceId);
177
178 //Get the configuration from the device
179 if (device == null) {
180 log.error("Lumentum NETCONF - device object not found for {}", deviceId);
181 return ImmutableList.of();
182 }
183
184 NetconfSession session = getNetconfSession();
185
186 if (session == null) {
187 log.error("Lumentum NETCONF - session not found for {}", deviceId);
188 return ImmutableList.of();
189 }
190
191 StringBuilder requestBuilder = new StringBuilder();
192 requestBuilder.append("<physical-ports xmlns=\"http://www.lumentum.com/lumentum-ote-port\" ");
193 requestBuilder.append("xmlns:lotep=\"http://www.lumentum.com/lumentum-ote-port\" ");
194 requestBuilder.append("xmlns:lotepopt=\"http://www.lumentum.com/lumentum-ote-port-optical\" ");
195 requestBuilder.append("xmlns:loteeth=\"http://www.lumentum.com/lumentum-ote-port-ethernet\">");
196 requestBuilder.append("</physical-ports>");
197
198 try {
199 reply = session.get(requestBuilder.toString(), null);
200 } catch (NetconfException e) {
201 log.error("Lumentum NETCONF - " +
202 "discoverPortDetails failed to retrieve port details {}", handler().data().deviceId(), e);
203 return ImmutableList.of();
204 }
205
206 List<PortDescription> descriptions = parseLumentumRoadmPorts(XmlConfigParser.
207 loadXml(new ByteArrayInputStream(reply.getBytes())));
208
209 return ImmutableList.copyOf(descriptions);
210 }
211
212 /**
213 * Parses a configuration and returns a set of ports.
214 *
215 * @param cfg a hierarchical configuration
216 * @return a list of port descriptions
217 */
218 protected List<PortDescription> parseLumentumRoadmPorts(HierarchicalConfiguration cfg) {
219 List<PortDescription> portDescriptions = Lists.newArrayList();
220 List<HierarchicalConfiguration> ports = cfg.configurationsAt(PHYSICAL_PORT);
221
222 ports.stream().forEach(pcfg -> {
223
224 DefaultAnnotations.Builder annotations = DefaultAnnotations.builder();
225
226 //Load port number
227 PortNumber portNum = PortNumber.portNumber(
228 pcfg.getString(DN).substring(pcfg.getString(DN).lastIndexOf(DN_PORT) + 5));
229
230 //Load port state
231 String maintenanceState = pcfg.getString(MAINTENANCE_STATE);
232 boolean isEnabled = ((maintenanceState != null) && (maintenanceState).equals(IN_SERVICE));
233
234 //Load port type (FIBER/COPPER)
235 Port.Type type = null;
236 for (Object o : pcfg.getList(PORT_EXTENSION)) {
237 String s = (String) o;
238 if (s.equals(OPTICAL_INPUT) || s.equals(OPTICAL_OUTPUT)) {
239 type = Port.Type.FIBER;
240
241 } else if (s.equals(PORT_ETHERNET) || s.equals(PORT_PLUGGABLE)) {
242 type = Port.Type.COPPER;
243 }
244 }
245
246 //Load port speed of Ethernet interface, expressed in Mb/s
247 Long speed = 0L; //should be the speed of optical port
248 if (type != null) {
249 if (type.equals(Port.Type.COPPER)) {
250 String speedString = pcfg.getString(PORT_SPEED);
251 if (speedString != null) {
252 speed = Long.parseLong(speedString.substring(speedString.lastIndexOf("speed_") + 6,
253 speedString.lastIndexOf("Mb")));
254 } else {
255 log.error("Lumentum NETCONF - Port speed of Ethernet port not correctly loaded");
256 }
257 }
258 } else {
259 log.error("Port Type not correctly loaded");
260 }
261
alessiod4a2b842019-04-30 18:43:17 +0200262 //Store reverse port index in the annotations
263 Long reversePortId;
264
265 /**
266 * Setting the reverse port value for the unidirectional ports.
267 *
268 * In this device each port includes an input fiber and an output fiber.
269 * The 20 input fibers are numbered from MIN_MUX_PORT = 4101 to MAX_MUX_PORT = 4120.
270 * The 20 output fibers are numbered from MIN_DEM_PORT = 5201 to MAX_DEM_PORT = 5220.
271 *
272 * Where port 520x is always the reverse of 410x.
273 */
274 if ((portNum.toLong() >= MIN_MUX_PORT) && (portNum.toLong() <= MAX_MUX_PORT)) {
275 reversePortId = portNum.toLong() + DELTA_MUX_DEM_PORT;
276 annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
277 }
278 if ((portNum.toLong() >= MIN_DEM_PORT) && (portNum.toLong() <= MAX_DEM_PORT)) {
279 reversePortId = portNum.toLong() - DELTA_MUX_DEM_PORT;
280 annotations.set(OpticalPathIntent.REVERSE_PORT_ANNOTATION_KEY, reversePortId.toString());
281 }
282
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100283 //Load other information
284 pcfg.getKeys().forEachRemaining(k -> {
285 if (!k.contains(DN) && !k.contains(PORT_SPEED) && !k.contains(PORT_EXTENSION)
286 && !k.contains(MAINTENANCE_STATE)) {
287 String value = pcfg.getString(k);
288 if (!value.isEmpty()) {
289 k = StringUtils.replaceEach(k, new String[]{"loteeth:", "lotep:",
290 "lotepopt:", "config.", "=", ":",
291 "state."},
292 new String[]{"", "", "", "", "", "", ""});
293
294 annotations.set(k, value);
295
296 //To visualize port name in the ROADM app GUI
297 if (k.equals(PORT_NAME)) {
298 annotations.set(AnnotationKeys.PORT_NAME, value);
299 }
300
301 }
302 }
303 });
304
305 log.debug("Lumentum NETCONF - retrieved port {},{},{},{},{}",
306 portNum, isEnabled, type, speed, annotations.build());
307
alessiod4a2b842019-04-30 18:43:17 +0200308 if (type == Port.Type.FIBER) {
309 portDescriptions.add(omsPortDescription(portNum,
310 isEnabled,
311 START_CENTER_FREQ_50,
312 END_CENTER_FREQ_50,
313 CHANNEL_SPACING_50.frequency(),
314 annotations.build()));
315 } else {
316 DefaultPortDescription.Builder portDescriptionBuilder = DefaultPortDescription.builder();
317 portDescriptionBuilder.withPortNumber(portNum)
318 .isEnabled(isEnabled)
319 .type(type)
320 .portSpeed(speed)
321 .annotations(annotations.build());
Ray Milkeyf031e302018-09-07 10:07:24 -0700322
alessiod4a2b842019-04-30 18:43:17 +0200323 portDescriptions.add(portDescriptionBuilder.build());
324 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100325 });
326
327 return portDescriptions;
328 }
329
330 //Following Lumentum documentation rpc operation to delete all connections
331 private boolean rpcRemoveAllConnections(String module) {
332 StringBuilder stringBuilder = new StringBuilder();
333 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
334 stringBuilder.append(
335 "<remove-all-connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
336 stringBuilder.append("<dn>ne=1;chassis=1;card=1;module=" + module + "</dn>" + "\n");
337 stringBuilder.append("</remove-all-connections>" + "\n");
338 stringBuilder.append("</rpc>" + "\n");
339
340 return editCrossConnect(stringBuilder.toString());
341 }
342
343 private boolean editCrossConnect(String xcString) {
344 NetconfSession session = getNetconfSession();
345
346 if (session == null) {
347 log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
348 return false;
349 }
350
351 try {
352 return session.editConfig(xcString);
353 } catch (NetconfException e) {
354 log.error("Failed to edit the CrossConnect edid-cfg for device {}",
355 handler().data().deviceId(), e);
356 log.debug("Failed configuration {}", xcString);
357 return false;
358 }
359 }
360
361 private NetconfSession getNetconfSession() {
362 NetconfController controller = checkNotNull(handler().get(NetconfController.class));
363 NetconfDevice ncDevice = controller.getNetconfDevice(handler().data().deviceId());
364
365 if (ncDevice == null) {
366 log.error("Lumentum NETCONF - device not found for {}", handler().data().deviceId());
367 return null;
368 }
369
370 return ncDevice.getSession();
371 }
372}