blob: 57cc0256f2bee318d404c5c07a36baa8e6ae3a70 [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 org.apache.commons.configuration.HierarchicalConfiguration;
21import org.apache.commons.lang3.tuple.Pair;
22import org.onlab.util.Frequency;
23import org.onlab.util.Spectrum;
alessio1bf2a632019-06-04 15:47:39 +020024import org.onosproject.drivers.odtn.impl.DeviceConnectionCache;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010025import org.onosproject.drivers.utilities.XmlConfigParser;
alessio1bf2a632019-06-04 15:47:39 +020026import org.onosproject.net.PortNumber;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010027import org.onosproject.net.OchSignal;
28import org.onosproject.net.OchSignalType;
alessio1bf2a632019-06-04 15:47:39 +020029import org.onosproject.net.DeviceId;
30import org.onosproject.net.ChannelSpacing;
31import org.onosproject.net.GridType;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010032import org.onosproject.net.device.DeviceService;
33import org.onosproject.net.driver.AbstractHandlerBehaviour;
34import org.onosproject.net.flow.DefaultFlowEntry;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010035import org.onosproject.net.flow.DefaultTrafficSelector;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010036import org.onosproject.net.flow.FlowEntry;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010037import org.onosproject.net.flow.FlowRule;
38import org.onosproject.net.flow.FlowRuleProgrammable;
39import org.onosproject.net.flow.TrafficSelector;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010040import org.onosproject.net.flow.criteria.Criteria;
41import org.onosproject.netconf.NetconfController;
42import org.onosproject.netconf.NetconfException;
43import org.onosproject.netconf.NetconfSession;
44import org.slf4j.Logger;
45import org.slf4j.LoggerFactory;
46
47import java.io.ByteArrayInputStream;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010048import java.util.Collection;
alessio1bf2a632019-06-04 15:47:39 +020049import java.util.ArrayList;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010050import java.util.List;
alessio1bf2a632019-06-04 15:47:39 +020051import java.util.Set;
52import java.util.stream.Collectors;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010053
54import static com.google.common.base.Preconditions.checkArgument;
55import static com.google.common.base.Preconditions.checkNotNull;
56
57/**
58 * Implementation of FlowRuleProgrammable interface for Lumentum ROADM-A Whitebox devices using NETCONF.
59 */
60public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
61
62 private static final Logger log =
63 LoggerFactory.getLogger(LumentumNetconfRoadmFlowRuleProgrammable.class);
64
65 private static final String DN = "dn";
66 private static final String DN_PORT = "port=";
67 private static final String DN_CARD1 = "ne=1;chassis=1;card=1;port=";
68 private static final String CONNECTION = "connection";
69 private static final String CONNECTIONS = "data.connections.connection";
70 private static final String CONFIG = "config";
71 private static final String STATE = "state";
72 private static final String START_FREQ = "start-freq";
73 private static final String END_FREQ = "end-freq";
74 private static final String MODULE = "module";
75 private static final String SEMI_COLON = ";";
76 private static final String EQUAL = "=";
77 private static final String INPUT_PORT_REFERENCE = "input-port-reference";
78 private static final String OUTPUT_PORT_REFERENCE = "output-port-reference";
79
80 private static final String CHANNEL_ATTENUATION = "attenuation";
81 private static final String CHANNEL_INPUT_POWER = "input-channel-attributes.power";
82 private static final String CHANNEL_OUTPUT_POWER = "output-channel-attributes.power";
83
84 protected static final long LINE_PORT = 3001;
85 protected static final PortNumber LINE_PORT_NUMBER = PortNumber.portNumber(LINE_PORT);
86 protected static final long MUX_OUT = 4201;
87 protected static final long DEMUX_IN = 5101;
88 protected static final long GHZ = 1_000_000_000L;
alessio1bf2a632019-06-04 15:47:39 +020089 protected static final int LUMENTUM_ROADM20_MAX_CONNECTIONS = 100;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010090
91 /**Get the flow entries that are present on the Lumentum device, called by FlowRuleDriverProvider.
92 *
93 * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an
94 * exact match the device will be requested to remove those flows.
95 *
96 * @return A collection of Flow Entries
97 */
98 @Override
99 public Collection<FlowEntry> getFlowEntries() {
alessio1bf2a632019-06-04 15:47:39 +0200100 Collection<FlowEntry> fetched = fetchConnectionsFromDevice().stream()
101 .map(conn -> buildFlowrule(conn))
102 .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
103 .collect(Collectors.toList());
104
105 //Print out number of rules actually found on the device that are also included in the cache
106 log.debug("Device {} getFlowEntries fetched connections {}", did(), fetched.size());
107
108 return fetched;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100109 }
110
111 /**Apply the flow entries specified in the collection rules.
112 *
113 * @param rules A collection of Flow Rules to be applied to the Lumentum device
114 * @return The collection of added Flow Entries
115 */
116 @Override
117 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
alessio1bf2a632019-06-04 15:47:39 +0200118 //Check NETCONF session
119 NetconfSession session = getNetconfSession();
120 if (session == null) {
121 log.error("Device {} null session", did());
122 return ImmutableList.of();
123 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100124
alessio1bf2a632019-06-04 15:47:39 +0200125 // Apply the rules on the device and add to cache if success
126 Collection<FlowRule> added = new ArrayList<>();
127 for (FlowRule flowRule : rules) {
128 LumentumFlowRule lumFlowRule = new LumentumFlowRule(flowRule, getLinePorts());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100129
alessio1bf2a632019-06-04 15:47:39 +0200130 if (rpcAddConnection(lumFlowRule)) {
131 added.add(lumFlowRule);
132 getConnectionCache().add(did(), lumFlowRule.getConnectionName(), lumFlowRule);
133 }
134 }
135
136 //Print out number of rules sent to the device (without receiving errors)
137 log.debug("Device {} applyFlowRules added {}", did(), added.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100138
139 return added;
140 }
141
142 @Override
143 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
alessio1bf2a632019-06-04 15:47:39 +0200144 NetconfSession session = getNetconfSession();
145 if (session == null) {
146 log.error("Device {} null session", did());
147 return ImmutableList.of();
148 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100149
alessio1bf2a632019-06-04 15:47:39 +0200150 // Remove the rules from the device and from the cache
151 List<FlowRule> removed = new ArrayList<>();
152 for (FlowRule r : rules) {
153 try {
154 LumentumFlowRule flowRule = new LumentumFlowRule(r, getLinePorts());
155 rpcDeleteConnection(flowRule);
156 getConnectionCache().remove(did(), r);
157 removed.add(r);
158 } catch (Exception e) {
159 log.error("Device {} Error {}", did(), e);
160 continue;
161 }
162 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100163
alessio1bf2a632019-06-04 15:47:39 +0200164 //Print out number of removed rules from the device (without receiving errors)
165 log.debug("Device {} removeFlowRules removed {}", did(), removed.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100166
167 return removed;
168 }
169
170 private List<PortNumber> getLinePorts() {
171 DeviceService deviceService = this.handler().get(DeviceService.class);
172 return deviceService.getPorts(data().deviceId()).stream()
173 .filter(p -> p.number().toLong() == LINE_PORT)
174 .map(p -> p.number())
175 .collect(Collectors.toList());
176 }
177
178 /**
179 * Fetches list of connections from device.
180 *
181 * @return list of connections as XML hierarchy
182 */
183 private List<HierarchicalConfiguration> fetchConnectionsFromDevice() {
184 String reply;
185
186 StringBuilder requestBuilder = new StringBuilder();
187 requestBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">");
188 requestBuilder.append("</connections>");
189
190 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100191 if (session == null) {
192 log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
193 return ImmutableList.of();
194 }
195
196 try {
197 reply = session.get(requestBuilder.toString(), null);
alessio1bf2a632019-06-04 15:47:39 +0200198 log.debug("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100199 } catch (NetconfException e) {
200 log.error("Failed to retrieve configuration details for device {}",
201 handler().data().deviceId(), e);
202 return ImmutableList.of();
203 }
204
205 HierarchicalConfiguration cfg =
206 XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
207
208 return cfg.configurationsAt(CONNECTIONS);
209 }
210
211 // Example input dn: ne=1;chassis=1;card=1;module=2;connection=89
212 private Pair<Short, Short> parseDn(String dn) {
213 Short module = null;
214 Short connection = null;
215 for (String entry : dn.split(SEMI_COLON)) {
216 String[] keyVal = entry.split(EQUAL);
217 if (keyVal.length != 2) {
218 continue;
219 }
220 if (keyVal[0].equals(MODULE)) {
221 module = Short.valueOf(keyVal[1]);
222 }
223 if (keyVal[0].equals(CONNECTION)) {
224 connection = Short.valueOf(keyVal[1]);
225 }
226 if (module != null && connection != null) {
227 return Pair.of(module, connection);
228 }
229 }
230
231 return null;
232 }
233
234 /**
235 * Builds a flow rule from a connection hierarchy.
236 *
237 * @param connection the connection hierarchy
238 * @return the flow rule
239 */
240 private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
241
242 String dn = connection.getString(DN);
243 Pair<Short, Short> pair = parseDn(dn);
alessio1bf2a632019-06-04 15:47:39 +0200244 short connId = pair.getRight();
245 short moduleId = pair.getLeft();
246
247 if (pair == null) {
248 log.error("Lumentum NETCONF - device {} error in retrieving DN field", did());
249 return null;
250 }
251
252 log.debug("Lumentum NETCONF - retrieved FlowRule module {} connection {}", moduleId, connId);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100253
254 HierarchicalConfiguration config = connection.configurationAt(CONFIG);
255 double startFreq = config.getDouble(START_FREQ);
256 double endFreq = config.getDouble(END_FREQ);
257 String inputPortReference = config.getString(INPUT_PORT_REFERENCE);
258 String outputPortReference = config.getString(OUTPUT_PORT_REFERENCE);
259
260 HierarchicalConfiguration state = connection.configurationAt(STATE);
261 double attenuation = state.getDouble(CHANNEL_ATTENUATION);
262 double inputPower = state.getDouble(CHANNEL_INPUT_POWER);
263 double outputPower = state.getDouble(CHANNEL_OUTPUT_POWER);
264
alessio1bf2a632019-06-04 15:47:39 +0200265 PortNumber portNumber = getPortNumber(moduleId, inputPortReference, outputPortReference);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100266
alessio1bf2a632019-06-04 15:47:39 +0200267 //If rule is on module 1 it means input port in the Flow rule is contained in portNumber.
268 //Otherwise the input port in the Flow rule must is the line port.
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100269 TrafficSelector selector = DefaultTrafficSelector.builder()
alessio1bf2a632019-06-04 15:47:39 +0200270 .matchInPort(moduleId == 1 ? portNumber : LINE_PORT_NUMBER)
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100271 .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
272 .add(Criteria.matchLambda(toOchSignal(startFreq, endFreq)))
273 .build();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100274
275 log.debug("Lumentum NETCONF - retrieved FlowRule startFreq {} endFreq {}", startFreq, endFreq);
276
alessio1bf2a632019-06-04 15:47:39 +0200277 //Lookup of connection
278 //Retrieved rules, cached rules are considered equal if the selector is equal
279 FlowRule cacheRule = null;
280 if (getConnectionCache().size(did()) != 0) {
281 cacheRule = getConnectionCache().get(did()).stream()
282 .filter(r -> (r.selector().equals(selector)))
283 .findFirst()
284 .orElse(null);
alessiod4a2b842019-04-30 18:43:17 +0200285 }
286
alessio1bf2a632019-06-04 15:47:39 +0200287
288 if (cacheRule == null) {
289 //TODO consider a way to keep "external" FlowRules
290 log.error("Lumentum NETCONF connection not in the cache {}", pair.getRight());
291 rpcDeleteExternalConnection(moduleId, connId);
292 return null;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100293 } else {
alessio1bf2a632019-06-04 15:47:39 +0200294 //Update monitored values
295 log.debug("Attenuation retrieved {} dB for connection {}",
296 attenuation, ((LumentumFlowRule) cacheRule).getConnectionId());
297 ((LumentumFlowRule) cacheRule).setAttenuation(attenuation);
298 ((LumentumFlowRule) cacheRule).setInputPower(inputPower);
299 ((LumentumFlowRule) cacheRule).setOutputPower(outputPower);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100300
alessio1bf2a632019-06-04 15:47:39 +0200301 return cacheRule;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100302 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100303 }
304
305 /**
306 * Get the port number.
307 * If this is a MUX connection return input-port. Outport is always MUX_OUT = 4201.
308 * If this is a DEMUX connection return output-port. Inport is always DEMUX_IN= 5101.
309 *
310 * @param module the module (1 for MUX/ADD, 2 for DEMUX/DROP)
311 * @return the add/drop port number
312 */
313 private PortNumber getPortNumber(short module, String inputPort, String outputPort) {
314 checkArgument(module == 1 || module == 2, "Module must be 1 (MUX/ADD) or 2 (DEMUX/DROP)");
315
316 if (module == 1) {
317 return PortNumber.portNumber(inputPort.split(DN_PORT)[1]);
318 } else {
319 return PortNumber.portNumber(outputPort.split(DN_PORT)[1]);
320 }
321 }
322
323 /**
324 * Converts cross connect flow rule to module and connection.
325 *
326 * Connection number is incremental within the class and associated to the rule hash.
327 *
328 * @param xc the cross connect flow rule
329 * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
330 */
alessiod4a2b842019-04-30 18:43:17 +0200331 private Pair<Short, Short> setModuleConnection(LumentumFlowRule xc, Integer id) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100332 if (xc.isAddRule()) {
alessio1bf2a632019-06-04 15:47:39 +0200333 xc.setConnectionModule((short) 1);
334 xc.setConnectionId(id.shortValue());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100335 return Pair.of((short) 1, id.shortValue());
336 } else {
alessio1bf2a632019-06-04 15:47:39 +0200337 xc.setConnectionModule((short) 2);
338 xc.setConnectionId(id.shortValue());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100339 return Pair.of((short) 2, id.shortValue());
340 }
341 }
342
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100343 //Following Lumentum documentation rpc operation to configure a new connection
alessiod4a2b842019-04-30 18:43:17 +0200344 private boolean rpcAddConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100345
346 int currentConnectionId = generateConnectionId();
347
348 if (currentConnectionId == 0) {
349 log.error("Lumentum driver - 100 connections are already configured on the device");
350 return false;
351 }
352
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100353 Pair<Short, Short> pair = setModuleConnection(xc, currentConnectionId);
354 String module = pair.getLeft().toString();
355 String connectionId = pair.getRight().toString();
356
357 //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
358 Frequency freqRadius = Frequency.ofHz(xc.ochSignal().channelSpacing().frequency().asHz() / 2);
359 Frequency center = xc.ochSignal().centralFrequency();
360 String startFreq = String.valueOf(center.subtract(freqRadius).asHz() / GHZ);
361 String endFreq = String.valueOf(center.add(freqRadius).asHz() / GHZ);
362
363 StringBuilder stringBuilder = new StringBuilder();
364 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
365 stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
366 stringBuilder.append(
367 "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
368 stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
369 stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
370 stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
371 stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
372 stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
373
374 if (xc.isAddRule()) {
375 stringBuilder.append(
376 "<input-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
377 stringBuilder.append(
378 "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
379 } else {
380 stringBuilder.append(
381 "<input-port-reference>" + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
382 stringBuilder.append(
383 "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
384 }
385 stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
386 stringBuilder.append("</add-connection>" + "\n");
387 stringBuilder.append("</rpc>" + "\n");
388
alessio1bf2a632019-06-04 15:47:39 +0200389 log.info("Lumentum ROADM20 - RPC add-connection sent to device {}", did());
390 log.debug("Lumentum ROADM20 - RPC add-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100391
392 return editCrossConnect(stringBuilder.toString());
393 }
394
395 //Following Lumentum documentation <edit-config> operation to edit connection parameter
396 //Currently only edit the "attenuation" parameter
397 private boolean editConnection(String moduleId, String connectionId, int attenuation) {
398
399 double attenuationDouble = ((double) attenuation) / 100;
400
401 StringBuilder stringBuilder = new StringBuilder();
402 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
403 stringBuilder.append("<edit-config>" + "\n");
404 stringBuilder.append("<target>" + "\n");
405 stringBuilder.append("<running/>" + "\n");
406 stringBuilder.append("</target>" + "\n");
407 stringBuilder.append("<config>" + "\n");
408 stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
409 stringBuilder.append("<connection>" + "\n");
410 stringBuilder.append("" +
411 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
412 //Other configurable parameters
413 //stringBuilder.append("<custom-name/>" + "\n");
414 //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
415 //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
416 //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
417 stringBuilder.append("<config>" + "\n");
418 stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
419 stringBuilder.append("</config>" + "\n");
420 stringBuilder.append("</connection>" + "\n");
421 stringBuilder.append("</connections>" + "\n");
422 stringBuilder.append("</config>" + "\n");
423 stringBuilder.append("</edit-config>" + "\n");
424 stringBuilder.append("</rpc>" + "\n");
425
alessio1bf2a632019-06-04 15:47:39 +0200426 log.info("Lumentum ROADM20 - edit-connection sent to device {}", did());
427 log.debug("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100428
429 return editCrossConnect(stringBuilder.toString());
430 }
431
432 //Following Lumentum documentation rpc operation to delete a new connection
alessiod4a2b842019-04-30 18:43:17 +0200433 private boolean rpcDeleteConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100434
alessio1bf2a632019-06-04 15:47:39 +0200435 //Look for corresponding rule into the cache
436 FlowRule cacheRule = getConnectionCache().get(did()).stream()
437 .filter(r -> (r.selector().equals(xc.selector()) && r.treatment().equals(xc.treatment())))
438 .findFirst()
439 .orElse(null);
440
441 if (cacheRule == null) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100442 log.error("Lumentum RPC delete-connection, connection not found on the local cache");
Ray Milkeycc7a1762018-06-18 08:54:51 -0700443 throw new IllegalStateException("Lumentum RPC delete-connection, connection not found on the local cache");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100444 }
445
alessio1bf2a632019-06-04 15:47:39 +0200446
447 int moduleId = ((LumentumFlowRule) cacheRule).getConnectionModule();
448 int connId = ((LumentumFlowRule) cacheRule).getConnectionId();
449
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100450
451 StringBuilder stringBuilder = new StringBuilder();
452 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
453 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
454 stringBuilder.append(
alessio1bf2a632019-06-04 15:47:39 +0200455 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100456 stringBuilder.append("</delete-connection>" + "\n");
457 stringBuilder.append("</rpc>" + " \n");
458
alessio1bf2a632019-06-04 15:47:39 +0200459 log.info("Lumentum ROADM20 - RPC delete-connection sent to device {}", did());
460 log.debug("Lumentum ROADM20 - - RPC delete-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100461
462 return editCrossConnect(stringBuilder.toString());
463 }
464
465 //Following Lumentum documentation rpc operation to delete a new connection
466 //Executed if for some reason a connection not in the cache is detected
alessio1bf2a632019-06-04 15:47:39 +0200467 private boolean rpcDeleteExternalConnection(short moduleId, short connectionId) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100468
469 StringBuilder stringBuilder = new StringBuilder();
470 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
471 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
alessio1bf2a632019-06-04 15:47:39 +0200472 stringBuilder.append("<dn>ne=1;chassis=1;card=1;module="
473 + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100474 stringBuilder.append("</delete-connection>" + "\n");
475 stringBuilder.append("</rpc>" + "\n");
476
alessio1bf2a632019-06-04 15:47:39 +0200477 log.info("Lumentum ROADM20 - RPC delete-external-connection sent to device {}", did());
478 log.debug("Lumentum ROADM20 - - RPC delete-external-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100479
480 return editCrossConnect(stringBuilder.toString());
481 }
482
483
484 private boolean editCrossConnect(String xcString) {
485 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100486 if (session == null) {
487 log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
488 return false;
489 }
490
491 try {
492 return session.editConfig(xcString);
493 } catch (NetconfException e) {
494 log.error("Failed to edit the CrossConnect edid-cfg for device {}",
495 handler().data().deviceId(), e);
496 log.debug("Failed configuration {}", xcString);
497 return false;
498 }
499 }
500
alessio1bf2a632019-06-04 15:47:39 +0200501 /**
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100502 * Convert start and end frequencies to OCh signal.
503 *
504 * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
505 * FIXME: supports channel spacing 50 and 100
506 *
507 * @param start starting frequency as double in GHz
508 * @param end end frequency as double in GHz
509 * @return OCh signal
510 */
511 public static OchSignal toOchSignal(double start, double end) {
512 int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
513 int multiplier = 0;
514
515 //Conversion for 50 GHz slots
516 if (end - start == 50) {
517 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
518 / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
519
520 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
521 }
522
523 //Conversion for 100 GHz slots
524 if (end - start == 100) {
525 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
526 / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
527
528 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
529 }
530
531 return null;
532 }
533
534 /**
535 * Generate a valid connectionId, the connectionId is a field required by the device every time
536 * a connection is created/edited/removed.
537 *
538 *
539 * Device only supports connection id < 100
540 */
alessio1bf2a632019-06-04 15:47:39 +0200541 private int generateConnectionId() {
542 //LUMENTUM_ROADM20_MAX_CONNECTIONS = 100, device only supports connection id < 100
543 for (int i = 1; i < LUMENTUM_ROADM20_MAX_CONNECTIONS; i++) {
544 Set<FlowRule> rulesForDevice = getConnectionCache().get(did());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100545
alessio1bf2a632019-06-04 15:47:39 +0200546 if (rulesForDevice == null) {
547 return 1;
548 } else {
549 Set<Integer> connIds = rulesForDevice.stream()
550 .map(flow -> ((LumentumFlowRule) flow).getConnectionId())
551 .collect(Collectors.toSet());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100552
alessio1bf2a632019-06-04 15:47:39 +0200553 if (!connIds.contains(i)) {
554 return i;
555 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100556 }
557 }
558 return 0;
559 }
alessio1bf2a632019-06-04 15:47:39 +0200560
561 private DeviceConnectionCache getConnectionCache() {
562 return DeviceConnectionCache.init();
563 }
564
565 /**
566 * Helper method to get the device id.
567 */
568 private DeviceId did() {
569 return data().deviceId();
570 }
571
572 /**
573 * Helper method to get the Netconf session.
574 */
575 private NetconfSession getNetconfSession() {
576 NetconfController controller =
577 checkNotNull(handler().get(NetconfController.class));
578 return controller.getNetconfDevice(did()).getSession();
579 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100580}