blob: 0b7cde8d22869d806879b65d9ac27bea8153ff42 [file] [log] [blame]
Andrea Campanellaa9cdaf62019-09-08 10:30:16 -07001/*
2 * Copyright 2019-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 * This work was partially supported by EC H2020 project METRO-HAUL (761727).
17 */
18package org.onosproject.drivers.odtn;
19
20import org.apache.commons.configuration.HierarchicalConfiguration;
21import org.apache.commons.configuration.XMLConfiguration;
22import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
23import org.onlab.osgi.DefaultServiceDirectory;
24import org.onosproject.drivers.utilities.XmlConfigParser;
25import org.onosproject.net.DeviceId;
26import org.onosproject.net.ModulationScheme;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.behaviour.ModulationConfig;
29import org.onosproject.net.device.DeviceService;
30import org.onosproject.net.driver.AbstractHandlerBehaviour;
31import org.onosproject.netconf.DatastoreId;
32import org.onosproject.netconf.NetconfController;
33import org.onosproject.netconf.NetconfDevice;
34import org.onosproject.netconf.NetconfException;
35import org.onosproject.netconf.NetconfSession;
36import org.slf4j.Logger;
37import org.slf4j.LoggerFactory;
38
39import java.util.Optional;
40import java.util.concurrent.CompletableFuture;
41import java.util.concurrent.ExecutionException;
42import java.util.regex.Matcher;
43import java.util.regex.Pattern;
44
45import static com.google.common.base.Preconditions.checkNotNull;
46
47/*
48 * Driver Implementation of the ModulationConfig for OcNos standard open config based terminal devices.
49 */
50public class GrooveModulationOpenConfig<T> extends AbstractHandlerBehaviour implements ModulationConfig<T> {
51
52
53 private static final String RPC_TAG_NETCONF_BASE =
54 "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
55
56 private static final String RPC_CLOSE_TAG = "</rpc>";
57
58 private static final Logger log = LoggerFactory.getLogger(GrooveModulationOpenConfig.class);
59
60 private ComponentType state = ComponentType.DIRECTION;
61
62
63 enum BitRate {
64 GBPS_200(200), // 200 Gbps
65 GBPS_100(100), // 100 Gbps
66 GBPS_40(40), // 40 Gbps
67 GBPS_10(10); // 10 Gbps
68
69 private final long value;
70
71 public long getValue() {
72 return value;
73 }
74
75 BitRate(long value) {
76 this.value = value;
77 }
78 }
79
80 /**
81 * Returns the NetconfSession with the device for which the method was called.
82 *
83 * @param deviceId device indetifier
84 * @return The netconf session or null
85 */
86
87
88 private NetconfSession getNetconfSession(DeviceId deviceId) {
89 log.info("Inside getNetconfSession () method for device : {}", deviceId);
90 NetconfController controller = handler().get(NetconfController.class);
91 NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
92 if (ncdev == null) {
93 log.trace("No netconf device, returning null session");
94 return null;
95 }
96 return ncdev.getSession();
97 }
98
99 /*
100 *
101 * Get the deviceId for which the methods apply.
102 *
103 * @return The deviceId as contained in the handler data
104 */
105
106
107 private DeviceId did() {
108 return handler().data().deviceId();
109 }
110
111 /**
112 * Execute RPC request.
113 *
114 * @param session Netconf session
115 * @param message Netconf message in XML format
116 * @return XMLConfiguration object
117 */
118
119 private XMLConfiguration executeRpc(NetconfSession session, String message) {
120 try {
121 CompletableFuture<String> fut = session.rpc(message);
122 String rpcReply = fut.get();
123 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReply);
124 xconf.setExpressionEngine(new XPathExpressionEngine());
125 return xconf;
126 } catch (NetconfException ne) {
127 log.error("Exception on Netconf protocol: {}.", ne);
128 } catch (InterruptedException ie) {
129 log.error("Interrupted Exception: {}.", ie);
130 } catch (ExecutionException ee) {
131 log.error("Concurrent Exception while executing Netconf operation: {}.", ee);
132 }
133 return null;
134 }
135
136 /**
137 * Get the target Modulation Scheme on the component.
138 *
139 * @param port the port
140 * @param component the port component
141 * @return ModulationScheme as per bitRate value
142 **/
143 @Override
144 public Optional<ModulationScheme> getModulationScheme(PortNumber port, T component) {
145 checkState(component);
146 return state.getModulationScheme(port, component);
147 }
148
149 /**
150 * Set the target Modulation Scheme on the component.
151 *
152 * @param port the port
153 * @param component the port component
154 * @param bitRate bit rate in bps
155 **/
156 @Override
157 public void setModulationScheme(PortNumber port, T component, long bitRate) {
158 checkState(component);
159 state.setModulationScheme(port, component, bitRate);
160 }
161
162
163 /*
164 *
165 * Set the ComponentType to invoke proper methods for different template T.
166 * @param component the component.
167 */
168 void checkState(Object component) {
169 String clsName = component.getClass().getName();
170 switch (clsName) {
171 case "org.onosproject.net.Direction":
172 state = ComponentType.DIRECTION;
173 break;
174 case "org.onosproject.net.OchSignal":
175 state = ComponentType.OCHSIGNAL;
176 break;
177 default:
178 log.error("Cannot parse the component type {}.", clsName);
179 log.info("The component content is {}.", component.toString());
180 }
181
182 state.groove = this;
183 log.info("Setting the state with clsName :{} ", clsName);
184 }
185
186 /*
187 *
188 * Component type.
189 */
190 enum ComponentType {
191
192 /*
193 *
194 * Direction.
195 */
196 DIRECTION() {
197 @Override
198 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
199 return super.getModulationScheme(port, component);
200 }
201
202 @Override
203 void setModulationScheme(PortNumber port, Object component, long bitRate) {
204 log.info("Inside the enum setModulationScheme()");
205 super.setModulationScheme(port, component, bitRate);
206 }
207 },
208
209 /**
210 * OchSignal.
211 */
212 OCHSIGNAL() {
213 @Override
214 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
215 return super.getModulationScheme(port, component);
216 }
217
218 @Override
219 void setModulationScheme(PortNumber port, Object component, long bitRate) {
220 super.setModulationScheme(port, component, bitRate);
221 }
222 };
223
224
225 GrooveModulationOpenConfig groove;
226
227 /*
228 * mirror method in the internal class.
229 * @param port port
230 * @param component component
231 * @return target modulation
232 */
233 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
234 NetconfSession session = groove.getNetconfSession(groove.did());
235 checkNotNull(session);
236 String filter = createModulationFilter(groove, port);
237 StringBuilder rpcReq = new StringBuilder();
238 rpcReq.append(RPC_TAG_NETCONF_BASE)
239 .append("<get-config>")
240 .append("<source>")
241 .append("<" + DatastoreId.RUNNING + "/>")
242 .append("</source>")
243 .append("<filter type='subtree'>")
244 .append(filter)
245 .append("</filter>")
246 .append("</get-config>")
247 .append(RPC_CLOSE_TAG);
248 XMLConfiguration xconf = groove.executeRpc(session, rpcReq.toString());
249 if (xconf == null) {
250 log.error("Error in executingRpc");
251 return Optional.empty();
252 }
253 try {
254 HierarchicalConfiguration config =
255 xconf.configurationAt("data/components/component/optical-channel/config");
256
257 String operationalMode = String.valueOf(config.getString("operational-mode"));
258 /*Used for Internal Testing */
259 //String modulationScheme="DP16QAM";
260 ModulationScheme modulation;
261 if (operationalMode.equalsIgnoreCase("62") ||
262 operationalMode.equalsIgnoreCase("68")) {
263 modulation = ModulationScheme.DP_16QAM;
264 } else {
265 modulation = ModulationScheme.DP_QPSK;
266 }
267 return Optional.of(modulation);
268 } catch (IllegalArgumentException e) {
269 log.error("Error in parsing config", e);
270 return Optional.empty();
271 }
272 }
273
274 /*
275 * mirror method in the internal class.
276 * @param port port
277 * @param component component
278 * @param power target value
279 */
280 void setModulationScheme(PortNumber port, Object component, long bitRate) {
281
282 ModulationScheme modulation = null;
283 if (bitRate <= BitRate.GBPS_100.value) {
284 modulation = ModulationScheme.DP_QPSK;
285 } else {
286 modulation = ModulationScheme.DP_16QAM;
287
288 }
289 // TODO: Groove doesn't support to change the modulation format via OpenConfig directly
290 // without recommissioning the Optical Channel
291 // Workaround: use Groove native model via port-mode change
292 // String editConfig = modulationEditConfig(groove, port, component, bitRate, modulation.name());
293 String editConfig = groovePortModeEditConfig(groove, port, component, bitRate, modulation.name());
294
295 //setting the modulation by calling rpc
296 setModulationRpc(port, component, editConfig);
297
298 }
299
300 private static String createModulationFilter(GrooveModulationOpenConfig modulationConfig,
301 PortNumber portNumber) {
302 String name = ocName(modulationConfig, portNumber);
303 StringBuilder sb = new StringBuilder("<components xmlns=\"http://openconfig.net/yang/platform\">");
304 sb.append("<component>").append("<name>").append(name).append("</name>");
305 sb.append("</component>").append("</components>");
306 return sb.toString();
307 }
308
309 /**
310 * Extract component name from portNumber's annotations.
311 *
312 * @param pc modulation config instance
313 * @param portNumber the port number
314 * @return the component name
315 */
316
317
318 private static String ocName(GrooveModulationOpenConfig pc, PortNumber portNumber) {
319 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
320 DeviceId deviceId = pc.handler().data().deviceId();
321 return deviceService.getPort(deviceId, portNumber).annotations().value("oc-name");
322 }
323
324 /*
325 *
326 * Parse filtering string from port and component.
327 * @param portNumber Port Number
328 * @param component port component (optical-channel)
329 * @param bitRate bitRate in bps
330 * @return filtering string in xml format
331 */
332 private String modulationEditConfig(GrooveModulationOpenConfig modulationConfig, PortNumber portNumber,
333 Object component, long bitRate, String modulation) {
334 /*
335 <components xmlns="http://openconfig.net/yang/platform">
336 <component>
337 <name>oc1/0</name>
338 <config>
339 <name>oc1/0</name>
340 </config>
341 <optical-channel xmlns="http://openconfig.net/yang/terminal-device">
342 <config>
343 <operational-mode>62</operational-mode>
344 </config>
345 </optical-channel>
346 </component>
347 </components>
348 */
349 String operationalMode = convertToGrooveOperationalMode(modulation);
350 if (component != null) {
351 // This is an edit-config operation.
352 String portName = ocName(modulationConfig, portNumber); //oc1/0
353 StringBuilder sb = new StringBuilder("<components xmlns=\"http://openconfig.net/yang/platform\">");
354 sb.append("<component>");
355 sb.append("<name>").append(portName).append("</name>");
356 sb.append("<config>");
357 sb.append("<name>").append(portName).append("</name>");
358 sb.append("</config>");
359 sb.append("<optical-channel xmlns=\"http://openconfig.net/yang/terminal-device\">")
360 .append("<config>")
361 .append("<operational-mode>")
362 .append(operationalMode)
363 .append("</operational-mode>")
364 .append("</config>")
365 .append("</optical-channel>");
366 sb.append("</component>");
367 sb.append("</components>");
368 return sb.toString();
369 } else {
370 log.error("Cannot process the component {}.", component.getClass());
371 return null;
372 }
373 }
374
375 /*
376 *
377 * Parse filtering string from port and component.
378 * @param portNumber Port Number
379 * @param component port component (optical-channel)
380 * @param bitRate bitRate in bps
381 * @return filtering string in xml format
382 */
383 private String groovePortModeEditConfig(GrooveModulationOpenConfig modulationConfig, PortNumber portNumber,
384 Object component, long bitRate, String modulation) {
385 if (component != null) {
386 // This is an edit-config operation.
387 String portMode = modulation.equals("DP_QPSK") ? "QPSK_100G" : "16QAM_200G";
388 String portName = ocName(modulationConfig, portNumber);
389 Pattern portPattern = Pattern.compile(".*-[1]-[1-9][0-4]?-L[1-2]$"); // e.g. TRANSCEIVER-1-1-L1
390 Matcher lineMatch = portPattern.matcher(portName);
391 lineMatch.find();
392 final String[] split = lineMatch.group(0).split("-");
393
394 return "<ne xmlns=\"http://coriant.com/yang/os/ne\">" +
395 " <shelf>" +
396 " <shelf-id>" + split[1] + "</shelf-id>" +
397 " <slot>" +
398 " <slot-id>" + split[2] + "</slot-id>" +
399 " <card>" +
400 " <port>" +
401 " <port-id>" + split[3].replace("L", "") + "</port-id>" +
402 " <port-mode>" + portMode + "</port-mode>" +
403 " </port>" +
404 " </card>" +
405 " </slot>" +
406 " </shelf>" +
407 " </ne>";
408 } else {
409 log.error("Cannot process the component {}.", component.getClass());
410 return null;
411 }
412 }
413
414
415 private String convertToGrooveOperationalMode(String modulation) {
416 if (modulation.equals(ModulationScheme.DP_QPSK.name())) {
417 return "22"; // Symbol rate 100G; modulation DP-QPSK; FEC mode SDFEC25
418 } else {
419 return "62"; // Symbol rate 200G; modulation DP-16QAM; FEC mode SDFEC25
420 }
421 }
422
423 private boolean setModulationRpc(PortNumber port, Object component, String editConfig) {
424 // NOTE: this doesn't work in current Groove version.
425 NetconfSession session = groove.getNetconfSession(groove.did());
426 checkNotNull(session);
427 boolean response = true;
428 StringBuilder rpcReq = new StringBuilder();
429 rpcReq.append(RPC_TAG_NETCONF_BASE)
430 .append("<edit-config>")
431 .append("<target><" + DatastoreId.RUNNING + "/></target>")
432 .append("<config>")
433 .append(editConfig)
434 .append("</config>")
435 .append("</edit-config>")
436 .append(RPC_CLOSE_TAG);
437 log.info("RPC call for Setting Modulation : {}", rpcReq.toString());
438 XMLConfiguration xconf = groove.executeRpc(session, rpcReq.toString());
439 if (xconf == null) {
440 log.error("The <edit-config> operation to set target-modulation of Port({}:{}) is failed.",
441 port.toString(), component.toString());
442 } else if (!xconf.getRoot().getChild(0).getName().equals("ok")) {
443 // The successful reply should be "<rpc-reply ...><ok /></rpc-reply>"
444 response = false;
445 log.error("The <edit-config> operation to set target-modulation of Port({}:{}) is failed.",
446 port.toString(), component.toString());
447 }
448 return response;
449 }
450 }
451}