blob: 53b56db35c75b93c19c65852642b9c5873fa54cb [file] [log] [blame]
Andrea Campanella99ab7102019-07-26 14:43:14 +02001/*
2 * Copyright 2018-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.NetconfController;
32import org.onosproject.netconf.NetconfDevice;
33import org.onosproject.netconf.NetconfException;
34import org.onosproject.netconf.NetconfSession;
35import org.slf4j.Logger;
36import org.slf4j.LoggerFactory;
37
Andrea Campanella99ab7102019-07-26 14:43:14 +020038import java.util.Optional;
39import java.util.concurrent.CompletableFuture;
40import java.util.concurrent.ExecutionException;
41
42import static com.google.common.base.Preconditions.checkNotNull;
43
44/*
45 * Driver Implementation of the ModulationConfig for OcNos based terminal devices.
46 */
47public class CassiniModulationOcNos<T> extends AbstractHandlerBehaviour implements ModulationConfig<T> {
48
49
50 private static final String RPC_TAG_NETCONF_BASE =
51 "<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">";
52
53 private static final String RPC_CLOSE_TAG = "</rpc>";
54
55 private static final Logger log = LoggerFactory.getLogger(CassiniModulationOcNos.class);
56
57 private ComponentType state = ComponentType.DIRECTION;
58
59
60 enum BitRate {
Andrea Campanella3a361452019-08-02 10:17:53 +020061 GBPS_200(200), // 200 Gbps
62 GBPS_100(100), // 100 Gbps
63 GBPS_40(40), // 40 Gbps
64 GBPS_10(10); // 10 Gbps
Andrea Campanella99ab7102019-07-26 14:43:14 +020065
66 private final long value;
67
68 public long getValue() {
69 return value;
70 }
71
72 BitRate(long value) {
73 this.value = value;
74 }
75 }
76
77 /**
78 * Returns the NetconfSession with the device for which the method was called.
79 *
80 * @param deviceId device indetifier
81 * @return The netconf session or null
82 */
83
84
85 private NetconfSession getNetconfSession(DeviceId deviceId) {
86 log.info("Inside getNetconfSession () method for device : {}", deviceId);
87 NetconfController controller = handler().get(NetconfController.class);
88 NetconfDevice ncdev = controller.getDevicesMap().get(deviceId);
89 if (ncdev == null) {
90 log.trace("No netconf device, returning null session");
91 return null;
92 }
93 return ncdev.getSession();
94 }
95
96 /*
97 *
98 * Get the deviceId for which the methods apply.
99 *
100 * @return The deviceId as contained in the handler data
101 */
102
103
104 private DeviceId did() {
105 return handler().data().deviceId();
106 }
107
108 /**
109 * Execute RPC request.
110 *
111 * @param session Netconf session
112 * @param message Netconf message in XML format
113 * @return XMLConfiguration object
114 */
115
116 private XMLConfiguration executeRpc(NetconfSession session, String message) {
117 try {
118 CompletableFuture<String> fut = session.rpc(message);
119 String rpcReply = fut.get();
120 XMLConfiguration xconf = (XMLConfiguration) XmlConfigParser.loadXmlString(rpcReply);
121 xconf.setExpressionEngine(new XPathExpressionEngine());
122 return xconf;
123 } catch (NetconfException ne) {
124 log.error("Exception on Netconf protocol: {}.", ne);
125 } catch (InterruptedException ie) {
126 log.error("Interrupted Exception: {}.", ie);
127 } catch (ExecutionException ee) {
128 log.error("Concurrent Exception while executing Netconf operation: {}.", ee);
129 }
130 return null;
131 }
132
133 /**
134 * Get the target Modulation Scheme on the component.
135 *
136 * @param port the port
137 * @param component the port component
138 * @return ModulationScheme as per bitRate value
139 **/
140 @Override
141 public Optional<ModulationScheme> getModulationScheme(PortNumber port, T component) {
142 checkState(component);
143 return state.getModulationScheme(port, component);
144 }
145
146 /**
147 * Set the target Modulation Scheme on the component.
148 *
149 * @param port the port
150 * @param component the port component
151 * @param bitRate bit rate in bps
152 **/
153 @Override
154 public void setModulationScheme(PortNumber port, T component, long bitRate) {
155 checkState(component);
156 state.setModulationScheme(port, component, bitRate);
157 }
158
159 /*
160 *
161 * Set the ComponentType to invoke proper methods for different template T.
162 * @param component the component.
163 */
164 void checkState(Object component) {
165 String clsName = component.getClass().getName();
166 switch (clsName) {
167 case "org.onosproject.net.Direction":
168 state = CassiniModulationOcNos.ComponentType.DIRECTION;
169 break;
170 case "org.onosproject.net.OchSignal":
171 state = CassiniModulationOcNos.ComponentType.OCHSIGNAL;
172 break;
173 default:
174 log.error("Cannot parse the component type {}.", clsName);
175 log.info("The component content is {}.", component.toString());
176 }
177
178 state.cassini = this;
179 log.info("Setting the state with clsName :{} ", clsName);
180 }
181
182 /*
183 *
184 * Component type.
185 */
186
187
188 enum ComponentType {
189
190 /*
191 *
192 * Direction.
193 */
194
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
213
214 OCHSIGNAL() {
215 @Override
216 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
217 return super.getModulationScheme(port, component);
218 }
219
220 @Override
221 void setModulationScheme(PortNumber port, Object component, long bitRate) {
222 super.setModulationScheme(port, component, bitRate);
223 }
224 };
225
226
227 CassiniModulationOcNos cassini;
228
Andrea Campanella99ab7102019-07-26 14:43:14 +0200229 /*
230 * mirror method in the internal class.
231 * @param port port
232 * @param component component
233 * @return target modulation
234 */
235 Optional<ModulationScheme> getModulationScheme(PortNumber port, Object component) {
236 NetconfSession session = cassini.getNetconfSession(cassini.did());
237 checkNotNull(session);
238 String filter = createModulationFilter(cassini, port);
239 StringBuilder rpcReq = new StringBuilder();
240 rpcReq.append(RPC_TAG_NETCONF_BASE)
241 .append("<get>")
242 .append("<filter type='subtree'>")
243 .append(filter)
244 .append("</filter>")
245 .append("</get>")
246 .append(RPC_CLOSE_TAG);
247 log.info("RPC Call for Getting Modulation : \n {}", rpcReq.toString());
248 XMLConfiguration xconf = cassini.executeRpc(session, rpcReq.toString());
249 try {
250 HierarchicalConfiguration config =
251 xconf.configurationAt("data/components/component/optical-channel/config");
252
253 String modulationScheme = String.valueOf(config.getString("modulation"));
254 /*Used for Internal Testing */
255 //String modulationScheme="DP16QAM";
256 ModulationScheme modulation = ModulationScheme.valueOf(modulationScheme);
257 return Optional.of(modulation);
258 } catch (IllegalArgumentException e) {
259 return Optional.empty();
260 }
261 }
262
263 /*
264 * mirror method in the internal class.
265 * @param port port
266 * @param component component
267 * @param power target value
268 */
269 void setModulationScheme(PortNumber port, Object component, long bitRate) {
270
271 ModulationScheme modulation = null;
272 String editConfig = null;
273
274 //check if bitrate is less than equal to 100 Gig
275 if (bitRate <= BitRate.GBPS_100.value) {
276 modulation = ModulationScheme.DP_QPSK;
277 editConfig = modulationEditConfig(cassini, port, component, bitRate, modulation.name());
278 //setting the modulation by calling rpc
279 setModulationRpc(port, component, editConfig);
280 } else { // check if bitrate is greater than 100 Gig
281 modulation = ModulationScheme.DP_16QAM;
282 editConfig = modulationEditConfig(cassini, port, component, bitRate, modulation.name());
283 //setting the modulation by calling rpc
284 setModulationRpc(port, component, editConfig);
285 }
286
287
288 }
289
290 private static String createModulationFilter(CassiniModulationOcNos modulationConfig, PortNumber portNumber) {
291 String name = ocName(modulationConfig, portNumber);
292 StringBuilder sb = new StringBuilder("<components xmlns=\"http://openconfig.net/yang/platform\">");
293 sb.append("<component>").append("<name>").append(name).append("</name>");
294 sb.append("</component>").append("</components>");
295 return sb.toString();
296 }
297
298 /**
299 * Extract component name from portNumber's annotations.
300 *
301 * @param pc modulation config instance
302 * @param portNumber the port number
303 * @return the component name
304 */
305
306
307 private static String ocName(CassiniModulationOcNos pc, PortNumber portNumber) {
308 DeviceService deviceService = DefaultServiceDirectory.getService(DeviceService.class);
309 DeviceId deviceId = pc.handler().data().deviceId();
310 return deviceService.getPort(deviceId, portNumber).annotations().value("oc-name");
311 }
312
313 /*
314 *
315 * Parse filtering string from port and component.
316 * @param portNumber Port Number
317 * @param component port component (optical-channel)
318 * @param bitRate bitRate in bps
319 * @return filtering string in xml format
320
321 */
322 private String modulationEditConfig(CassiniModulationOcNos modulationConfig, PortNumber portNumber,
323 Object component, long bitRate, String modulation) {
324 /*
325 <cmmOpticalControllerTable xmlns="http://www.ipinfusion.com/CMLSchema/zebos">
326 <slotIndex>1</slotIndex> ==============>> optical-module-slot
327 <cmmOpticalChannelInterfaceTable>
328 <netIndex>0</netIndex> =============>> optical-channel-number (Reference to line-port)
329 <modulationFormat>dp-qpsk</modulationFormat>
330 </cmmOpticalNetworkInterfaceTable>
331 </cmmOpticalControllerTable>
332 */
333 String modulationOcNos = convertToOcNosModulation(modulation);
334 if (component != null) {
335 String portName = ocName(modulationConfig, portNumber); //oc1/1
336 String slotName = portName.split("/")[0]; //get oc1
337 String slotIndex = slotName.substring(slotName.length() - 1); //then just 1
338 String netIndex = portName.split("/")[1]; //get 1 after the /
339 StringBuilder sb = new StringBuilder("<cmmOpticalControllerTable " +
340 "xmlns=\"http://www.ipinfusion.com/CMLSchema/zebos\">");
341 sb.append("<slotIndex>").append(slotIndex).append("</slotIndex>");
342 if (modulation != null) {
343 // This is an edit-config operation.
344 sb.append("<cmmOpticalChannelInterfaceTable>")
345 .append("<netIndex>").append(netIndex).append("<netIndex>")
346 .append("<modulationFormat>")
347 .append(modulationOcNos)
348 .append("</modulationFormat>")
349 .append("</cmmOpticalNetworkInterfaceTable>");
350 }
351 sb.append("</cmmOpticalControllerTable>");
352 return sb.toString();
353 } else {
354 log.error("Cannot process the component {}.", component.getClass());
355 return null;
356 }
357 }
358
359 private String convertToOcNosModulation(String modulation) {
360 if (modulation.equals(ModulationScheme.DP_QPSK.name())) {
361 return "dp_qpsk";
362 } else {
363 return "dp_16qam";
364 }
365 }
366
367 private boolean setModulationRpc(PortNumber port, Object component, String editConfig) {
368 NetconfSession session = cassini.getNetconfSession(cassini.did());
369 checkNotNull(session);
370 boolean response = true;
371 StringBuilder rpcReq = new StringBuilder();
372 rpcReq.append(RPC_TAG_NETCONF_BASE)
373 .append("<edit-config>")
374 .append("<target><running/></target>")
375 .append("<config>")
376 .append(editConfig)
377 .append("</config>")
378 .append("</edit-config>")
379 .append(RPC_CLOSE_TAG);
380 log.info("RPC call for Setting Modulation : {}", rpcReq.toString());
381 XMLConfiguration xconf = cassini.executeRpc(session, rpcReq.toString());
382
383 // The successful reply should be "<rpc-reply ...><ok /></rpc-reply>"
384 if (!xconf.getRoot().getChild(0).getName().equals("ok")) {
385 response = false;
386 log.error("The <edit-config> operation to set target-modulation of Port({}:{}) is failed.",
387 port.toString(), component.toString());
388 }
389 return response;
390 }
391 }
392
393
394}