blob: d848379a4e29b8ebdc52514c78816b4ee24222a9 [file] [log] [blame]
Andrea Campanella99ab7102019-07-26 14:43:14 +02001/*
Andrea Campanella3a361452019-08-02 10:17:53 +02002 * Copyright 2019-present Open Networking Foundation
Andrea Campanella99ab7102019-07-26 14:43:14 +02003 *
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
Andrea Campanella7ebfe322019-08-29 11:46:57 -070020import org.apache.commons.configuration.ConfigurationException;
Andrea Campanella99ab7102019-07-26 14:43:14 +020021import org.apache.commons.configuration.HierarchicalConfiguration;
22import org.apache.commons.configuration.XMLConfiguration;
23import org.apache.commons.configuration.tree.xpath.XPathExpressionEngine;
24import org.onlab.osgi.DefaultServiceDirectory;
25import org.onosproject.drivers.utilities.XmlConfigParser;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.ModulationScheme;
28import org.onosproject.net.PortNumber;
29import org.onosproject.net.behaviour.ModulationConfig;
30import org.onosproject.net.device.DeviceService;
31import org.onosproject.net.driver.AbstractHandlerBehaviour;
Andrea Campanella3a361452019-08-02 10:17:53 +020032import org.onosproject.netconf.DatastoreId;
Andrea Campanella99ab7102019-07-26 14:43:14 +020033import org.onosproject.netconf.NetconfController;
34import org.onosproject.netconf.NetconfDevice;
35import org.onosproject.netconf.NetconfException;
36import org.onosproject.netconf.NetconfSession;
37import org.slf4j.Logger;
38import org.slf4j.LoggerFactory;
39
Andrea Campanella7ebfe322019-08-29 11:46:57 -070040import java.io.StringWriter;
Andrea Campanella99ab7102019-07-26 14:43:14 +020041import java.util.Optional;
42import java.util.concurrent.CompletableFuture;
43import java.util.concurrent.ExecutionException;
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 CassiniModulationOpenConfig<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(CassiniModulationOpenConfig.class);
59
60 private ComponentType state = ComponentType.DIRECTION;
61
62
63 enum BitRate {
Andrea Campanella3a361452019-08-02 10:17:53 +020064 GBPS_200(200), // 200 Gbps
65 GBPS_100(100), // 100 Gbps
66 GBPS_40(40), // 40 Gbps
67 GBPS_10(10); // 10 Gbps
Andrea Campanella99ab7102019-07-26 14:43:14 +020068
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) {
Andrea Campanella99ab7102019-07-26 14:43:14 +020089 NetconfController controller = handler().get(NetconfController.class);
90 NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
91 if (ncdev == null) {
92 log.trace("No netconf device, returning null session");
93 return null;
94 }
95 return ncdev.getSession();
96 }
97
98 /*
99 *
100 * Get the deviceId for which the methods apply.
101 *
102 * @return The deviceId as contained in the handler data
103 */
104
105
106 private DeviceId did() {
107 return handler().data().deviceId();
108 }
109
110 /**
111 * Execute RPC request.
112 *
113 * @param session Netconf session
114 * @param message Netconf message in XML format
115 * @return XMLConfiguration object
116 */
117
118 private XMLConfiguration executeRpc(NetconfSession session, String message) {
119 try {
Andrea Campanella7ebfe322019-08-29 11:46:57 -0700120 if (log.isDebugEnabled()) {
121 try {
122 StringWriter stringWriter = new StringWriter();
123 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(message);
124 xconf.setExpressionEngine(new XPathExpressionEngine());
125 xconf.save(stringWriter);
126 log.debug("Request {}", stringWriter.toString());
127 } catch (ConfigurationException e) {
128 log.error("XML Config Exception ", e);
129 }
130 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200131 CompletableFuture<String> fut = session.rpc(message);
132 String rpcReply = fut.get();
133 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReply);
134 xconf.setExpressionEngine(new XPathExpressionEngine());
Andrea Campanella7ebfe322019-08-29 11:46:57 -0700135 if (log.isDebugEnabled()) {
136 try {
137 StringWriter stringWriter = new StringWriter();
138 xconf.save(stringWriter);
139 log.debug("Response {}", stringWriter.toString());
140 } catch (ConfigurationException e) {
141 log.error("XML Config Exception ", e);
142 }
143 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200144 return xconf;
145 } catch (NetconfException ne) {
146 log.error("Exception on Netconf protocol: {}.", ne);
147 } catch (InterruptedException ie) {
148 log.error("Interrupted Exception: {}.", ie);
149 } catch (ExecutionException ee) {
150 log.error("Concurrent Exception while executing Netconf operation: {}.", ee);
151 }
152 return null;
153 }
154
155 /**
156 * Get the target Modulation Scheme on the component.
157 *
158 * @param port the port
159 * @param component the port component
160 * @return ModulationScheme as per bitRate value
161 **/
162 @Override
163 public Optional<ModulationScheme> getModulationScheme(PortNumber port, T component) {
164 checkState(component);
165 return state.getModulationScheme(port, component);
166 }
167
168 /**
169 * Set the target Modulation Scheme on the component.
170 *
171 * @param port the port
172 * @param component the port component
173 * @param bitRate bit rate in bps
174 **/
175 @Override
176 public void setModulationScheme(PortNumber port, T component, long bitRate) {
177 checkState(component);
178 state.setModulationScheme(port, component, bitRate);
179 }
180
181
182 /*
183 *
184 * Set the ComponentType to invoke proper methods for different template T.
185 * @param component the component.
186 */
187 void checkState(Object component) {
188 String clsName = component.getClass().getName();
189 switch (clsName) {
190 case "org.onosproject.net.Direction":
191 state = CassiniModulationOpenConfig.ComponentType.DIRECTION;
192 break;
193 case "org.onosproject.net.OchSignal":
194 state = CassiniModulationOpenConfig.ComponentType.OCHSIGNAL;
195 break;
196 default:
197 log.error("Cannot parse the component type {}.", clsName);
198 log.info("The component content is {}.", component.toString());
199 }
200
201 state.cassini = this;
Andrea Campanella99ab7102019-07-26 14:43:14 +0200202 }
203
204 /*
205 *
206 * Component type.
207 */
208
209
210 enum ComponentType {
211
212 /*
213 *
214 * Direction.
215 */
216
217
218 DIRECTION() {
219 @Override
220 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
221 return super.getModulationScheme(port, component);
222 }
223
224 @Override
225 void setModulationScheme(PortNumber port, Object component, long bitRate) {
226 log.info("Inside the enum setModulationScheme()");
227 super.setModulationScheme(port, component, bitRate);
228 }
229 },
230
231 /**
232 * OchSignal.
233 */
234
235
236 OCHSIGNAL() {
237 @Override
238 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
239 return super.getModulationScheme(port, component);
240 }
241
242 @Override
243 void setModulationScheme(PortNumber port, Object component, long bitRate) {
244 super.setModulationScheme(port, component, bitRate);
245 }
246 };
247
248
249 CassiniModulationOpenConfig cassini;
250
Andrea Campanella99ab7102019-07-26 14:43:14 +0200251 /*
252 * mirror method in the internal class.
253 * @param port port
254 * @param component component
255 * @return target modulation
256 */
257 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
258 NetconfSession session = cassini.getNetconfSession(cassini.did());
259 checkNotNull(session);
260 String filter = createModulationFilter(cassini, port);
261 StringBuilder rpcReq = new StringBuilder();
262 rpcReq.append(RPC_TAG_NETCONF_BASE)
Andrea Campanella3ccee482019-08-02 19:15:18 +0200263 .append("<get-config>")
264 .append("<source>")
265 .append("<" + DatastoreId.RUNNING + "/>")
266 .append("</source>")
Andrea Campanella99ab7102019-07-26 14:43:14 +0200267 .append("<filter type='subtree'>")
268 .append(filter)
269 .append("</filter>")
Andrea Campanella3ccee482019-08-02 19:15:18 +0200270 .append("</get-config>")
Andrea Campanella99ab7102019-07-26 14:43:14 +0200271 .append(RPC_CLOSE_TAG);
Andrea Campanella99ab7102019-07-26 14:43:14 +0200272 XMLConfiguration xconf = cassini.executeRpc(session, rpcReq.toString());
Andrea Campanella3ccee482019-08-02 19:15:18 +0200273 if (xconf == null) {
274 log.error("Error in executingRpc");
275 return Optional.empty();
276 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200277 try {
278 HierarchicalConfiguration config =
Andrea Campanella3ccee482019-08-02 19:15:18 +0200279 xconf.configurationAt("data/components/component/optical-channel/config");
Andrea Campanella99ab7102019-07-26 14:43:14 +0200280
Andrea Campanella3ccee482019-08-02 19:15:18 +0200281 String modulationScheme = String.valueOf(config.getString("modulation-format"));
Andrea Campanella99ab7102019-07-26 14:43:14 +0200282 /*Used for Internal Testing */
283 //String modulationScheme="DP16QAM";
Andrea Campanella3ccee482019-08-02 19:15:18 +0200284 ModulationScheme modulation;
285 if (modulationScheme.equalsIgnoreCase("dp-16-qam")) {
286 modulation = ModulationScheme.DP_16QAM;
287 } else if (modulationScheme.equalsIgnoreCase("dp-8-qam")) {
288 modulation = ModulationScheme.DP_8QAM;
289 } else {
290 modulation = ModulationScheme.DP_QPSK;
291 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200292 return Optional.of(modulation);
293 } catch (IllegalArgumentException e) {
Andrea Campanella3ccee482019-08-02 19:15:18 +0200294 log.error("Error in parsing config", e);
Andrea Campanella99ab7102019-07-26 14:43:14 +0200295 return Optional.empty();
296 }
297 }
298
299 /*
300 * mirror method in the internal class.
301 * @param port port
302 * @param component component
303 * @param power target value
304 */
305 void setModulationScheme(PortNumber port, Object component, long bitRate) {
306
307 ModulationScheme modulation = null;
308 String editConfig = null;
309
310 //check if bitrate is less than equal to 100 Gig
311 if (bitRate <= BitRate.GBPS_100.value) {
312 modulation = ModulationScheme.DP_QPSK;
313 editConfig = modulationEditConfig(cassini, port, component, bitRate, modulation.name());
314 //setting the modulation by calling rpc
315 setModulationRpc(port, component, editConfig);
316 } else { // check if bitrate is greater than 100 Gig
317 modulation = ModulationScheme.DP_16QAM;
318 editConfig = modulationEditConfig(cassini, port, component, bitRate, modulation.name());
319 //setting the modulation by calling rpc
320 setModulationRpc(port, component, editConfig);
321 }
322
323
324 }
325
326 private static String createModulationFilter(CassiniModulationOpenConfig modulationConfig,
327 PortNumber portNumber) {
328 String name = ocName(modulationConfig, portNumber);
329 StringBuilder sb = new StringBuilder("<components xmlns=\"http://openconfig.net/yang/platform\">");
330 sb.append("<component>").append("<name>").append(name).append("</name>");
331 sb.append("</component>").append("</components>");
332 return sb.toString();
333 }
334
335 /**
336 * Extract component name from portNumber's annotations.
337 *
338 * @param pc modulation config instance
339 * @param portNumber the port number
340 * @return the component name
341 */
342
343
344 private static String ocName(CassiniModulationOpenConfig pc, PortNumber portNumber) {
345 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
346 DeviceId deviceId = pc.handler().data().deviceId();
347 return deviceService.getPort(deviceId, portNumber).annotations().value("oc-name");
348 }
349
350 /*
351 *
352 * Parse filtering string from port and component.
353 * @param portNumber Port Number
354 * @param component port component (optical-channel)
355 * @param bitRate bitRate in bps
356 * @return filtering string in xml format
357
358 */
359 private String modulationEditConfig(CassiniModulationOpenConfig modulationConfig, PortNumber portNumber,
360 Object component, long bitRate, String modulation) {
361 /*
362 <components xmlns="http://openconfig.net/yang/platform">
363 <component>
364 <name>oc1/0</name>
365 <config>
366 <name>oc1/0</name>
367 </config>
368 <optical-channel xmlns="http://openconfig.net/yang/terminal-device">
369 <config>
370 <modulation-format>dp-16-qam</modulation-format>
371 </config>
372 </optical-channel>
373 </component>
374 </components>
375 */
376 String modulationOcNos = convertToOcNosModulation(modulation);
377 if (component != null) {
378 // This is an edit-config operation.
379 String portName = ocName(modulationConfig, portNumber); //oc1/0
380 StringBuilder sb = new StringBuilder("<components xmlns=\"http://openconfig.net/yang/platform\">");
381 sb.append("<component>");
382 sb.append("<name>").append(portName).append("</name>");
383 sb.append("<config>");
384 sb.append("<name>").append(portName).append("</name>");
385 sb.append("</config>");
386 sb.append("<optical-channel xmlns=\"http://openconfig.net/yang/terminal-device\">")
387 .append("<config>")
388 .append("<modulation-format>")
389 .append(modulationOcNos)
390 .append("</modulation-format>")
391 .append("</config>")
392 .append("</optical-channel>");
393 sb.append("</component>");
394 sb.append("</components>");
395 return sb.toString();
396 } else {
397 log.error("Cannot process the component {}.", component.getClass());
398 return null;
399 }
400 }
401
402 private String convertToOcNosModulation(String modulation) {
403 if (modulation.equals(ModulationScheme.DP_QPSK.name())) {
404 return "dp-qpsk";
405 } else {
406 return "dp-16-qam";
407 }
408 }
409
410 private boolean setModulationRpc(PortNumber port, Object component, String editConfig) {
411 NetconfSession session = cassini.getNetconfSession(cassini.did());
412 checkNotNull(session);
413 boolean response = true;
414 StringBuilder rpcReq = new StringBuilder();
415 rpcReq.append(RPC_TAG_NETCONF_BASE)
416 .append("<edit-config>")
Andrea Campanella3a361452019-08-02 10:17:53 +0200417 .append("<target><" + DatastoreId.CANDIDATE + "/></target>")
Andrea Campanella99ab7102019-07-26 14:43:14 +0200418 .append("<config>")
419 .append(editConfig)
420 .append("</config>")
421 .append("</edit-config>")
422 .append(RPC_CLOSE_TAG);
423 log.info("RPC call for Setting Modulation : {}", rpcReq.toString());
424 XMLConfiguration xconf = cassini.executeRpc(session, rpcReq.toString());
Andrea Campanella3ccee482019-08-02 19:15:18 +0200425 if (xconf == null) {
426 log.error("The <edit-config> operation to set target-modulation of Port({}:{}) is failed.",
427 port.toString(), component.toString());
428 } else if (!xconf.getRoot().getChild(0).getName().equals("ok")) {
429 // The successful reply should be "<rpc-reply ...><ok /></rpc-reply>"
Andrea Campanella99ab7102019-07-26 14:43:14 +0200430 response = false;
431 log.error("The <edit-config> operation to set target-modulation of Port({}:{}) is failed.",
432 port.toString(), component.toString());
433 }
Andrea Campanella3a361452019-08-02 10:17:53 +0200434 try {
435 session.commit();
436 } catch (NetconfException e) {
437 response = false;
438 log.error("error committing modulation changes");
439 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200440 return response;
441 }
442 }
Andrea Campanella99ab7102019-07-26 14:43:14 +0200443}