blob: d119cfaee127655a761d6c255eb4b22b588d6dc8 [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;
alessio70252912021-12-16 14:47:41 +010089 protected static final int LUMENTUM_ROADM20_MAX_CONNECTIONS = 130;
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);
alessioa9bcacc2021-09-24 17:32:57 +0200133 log.debug("Adding connection with selector {}", lumFlowRule.selector());
alessio1bf2a632019-06-04 15:47:39 +0200134 }
135 }
136
137 //Print out number of rules sent to the device (without receiving errors)
138 log.debug("Device {} applyFlowRules added {}", did(), added.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100139
140 return added;
141 }
142
143 @Override
144 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
alessio1bf2a632019-06-04 15:47:39 +0200145 NetconfSession session = getNetconfSession();
146 if (session == null) {
147 log.error("Device {} null session", did());
148 return ImmutableList.of();
149 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100150
alessio1bf2a632019-06-04 15:47:39 +0200151 // Remove the rules from the device and from the cache
152 List<FlowRule> removed = new ArrayList<>();
153 for (FlowRule r : rules) {
154 try {
155 LumentumFlowRule flowRule = new LumentumFlowRule(r, getLinePorts());
156 rpcDeleteConnection(flowRule);
157 getConnectionCache().remove(did(), r);
158 removed.add(r);
159 } catch (Exception e) {
160 log.error("Device {} Error {}", did(), e);
161 continue;
162 }
163 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100164
alessio1bf2a632019-06-04 15:47:39 +0200165 //Print out number of removed rules from the device (without receiving errors)
166 log.debug("Device {} removeFlowRules removed {}", did(), removed.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100167
168 return removed;
169 }
170
171 private List<PortNumber> getLinePorts() {
172 DeviceService deviceService = this.handler().get(DeviceService.class);
173 return deviceService.getPorts(data().deviceId()).stream()
174 .filter(p -> p.number().toLong() == LINE_PORT)
175 .map(p -> p.number())
176 .collect(Collectors.toList());
177 }
178
179 /**
180 * Fetches list of connections from device.
181 *
182 * @return list of connections as XML hierarchy
183 */
184 private List<HierarchicalConfiguration> fetchConnectionsFromDevice() {
185 String reply;
186
187 StringBuilder requestBuilder = new StringBuilder();
188 requestBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">");
189 requestBuilder.append("</connections>");
190
191 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100192 if (session == null) {
193 log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
194 return ImmutableList.of();
195 }
196
197 try {
198 reply = session.get(requestBuilder.toString(), null);
alessio1bf2a632019-06-04 15:47:39 +0200199 log.debug("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100200 } catch (NetconfException e) {
201 log.error("Failed to retrieve configuration details for device {}",
202 handler().data().deviceId(), e);
203 return ImmutableList.of();
204 }
205
206 HierarchicalConfiguration cfg =
207 XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
208
209 return cfg.configurationsAt(CONNECTIONS);
210 }
211
212 // Example input dn: ne=1;chassis=1;card=1;module=2;connection=89
213 private Pair<Short, Short> parseDn(String dn) {
214 Short module = null;
215 Short connection = null;
216 for (String entry : dn.split(SEMI_COLON)) {
217 String[] keyVal = entry.split(EQUAL);
218 if (keyVal.length != 2) {
219 continue;
220 }
221 if (keyVal[0].equals(MODULE)) {
222 module = Short.valueOf(keyVal[1]);
223 }
224 if (keyVal[0].equals(CONNECTION)) {
225 connection = Short.valueOf(keyVal[1]);
226 }
227 if (module != null && connection != null) {
228 return Pair.of(module, connection);
229 }
230 }
231
232 return null;
233 }
234
235 /**
236 * Builds a flow rule from a connection hierarchy.
237 *
238 * @param connection the connection hierarchy
239 * @return the flow rule
240 */
241 private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
242
243 String dn = connection.getString(DN);
244 Pair<Short, Short> pair = parseDn(dn);
alessio1bf2a632019-06-04 15:47:39 +0200245 short connId = pair.getRight();
246 short moduleId = pair.getLeft();
247
248 if (pair == null) {
249 log.error("Lumentum NETCONF - device {} error in retrieving DN field", did());
250 return null;
251 }
252
253 log.debug("Lumentum NETCONF - retrieved FlowRule module {} connection {}", moduleId, connId);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100254
255 HierarchicalConfiguration config = connection.configurationAt(CONFIG);
256 double startFreq = config.getDouble(START_FREQ);
257 double endFreq = config.getDouble(END_FREQ);
258 String inputPortReference = config.getString(INPUT_PORT_REFERENCE);
259 String outputPortReference = config.getString(OUTPUT_PORT_REFERENCE);
260
261 HierarchicalConfiguration state = connection.configurationAt(STATE);
262 double attenuation = state.getDouble(CHANNEL_ATTENUATION);
263 double inputPower = state.getDouble(CHANNEL_INPUT_POWER);
264 double outputPower = state.getDouble(CHANNEL_OUTPUT_POWER);
265
alessio1bf2a632019-06-04 15:47:39 +0200266 PortNumber portNumber = getPortNumber(moduleId, inputPortReference, outputPortReference);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100267
alessio1bf2a632019-06-04 15:47:39 +0200268 //If rule is on module 1 it means input port in the Flow rule is contained in portNumber.
269 //Otherwise the input port in the Flow rule must is the line port.
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100270 TrafficSelector selector = DefaultTrafficSelector.builder()
alessio1bf2a632019-06-04 15:47:39 +0200271 .matchInPort(moduleId == 1 ? portNumber : LINE_PORT_NUMBER)
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100272 .add(Criteria.matchOchSignalType(OchSignalType.FIXED_GRID))
273 .add(Criteria.matchLambda(toOchSignal(startFreq, endFreq)))
274 .build();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100275
276 log.debug("Lumentum NETCONF - retrieved FlowRule startFreq {} endFreq {}", startFreq, endFreq);
alessioa9bcacc2021-09-24 17:32:57 +0200277 log.debug("Lumentum NETCONF - retrieved FlowRule selector {}", selector);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100278
alessio1bf2a632019-06-04 15:47:39 +0200279 //Lookup of connection
280 //Retrieved rules, cached rules are considered equal if the selector is equal
281 FlowRule cacheRule = null;
282 if (getConnectionCache().size(did()) != 0) {
283 cacheRule = getConnectionCache().get(did()).stream()
284 .filter(r -> (r.selector().equals(selector)))
285 .findFirst()
286 .orElse(null);
alessiod4a2b842019-04-30 18:43:17 +0200287 }
288
alessio1bf2a632019-06-04 15:47:39 +0200289
290 if (cacheRule == null) {
291 //TODO consider a way to keep "external" FlowRules
alessioa9bcacc2021-09-24 17:32:57 +0200292 log.error("Lumentum NETCONF connection {} not in the cache", pair.getRight());
alessio1bf2a632019-06-04 15:47:39 +0200293 rpcDeleteExternalConnection(moduleId, connId);
294 return null;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100295 } else {
alessio1bf2a632019-06-04 15:47:39 +0200296 //Update monitored values
297 log.debug("Attenuation retrieved {} dB for connection {}",
298 attenuation, ((LumentumFlowRule) cacheRule).getConnectionId());
299 ((LumentumFlowRule) cacheRule).setAttenuation(attenuation);
300 ((LumentumFlowRule) cacheRule).setInputPower(inputPower);
301 ((LumentumFlowRule) cacheRule).setOutputPower(outputPower);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100302
alessio1bf2a632019-06-04 15:47:39 +0200303 return cacheRule;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100304 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100305 }
306
307 /**
308 * Get the port number.
309 * If this is a MUX connection return input-port. Outport is always MUX_OUT = 4201.
310 * If this is a DEMUX connection return output-port. Inport is always DEMUX_IN= 5101.
311 *
312 * @param module the module (1 for MUX/ADD, 2 for DEMUX/DROP)
313 * @return the add/drop port number
314 */
315 private PortNumber getPortNumber(short module, String inputPort, String outputPort) {
316 checkArgument(module == 1 || module == 2, "Module must be 1 (MUX/ADD) or 2 (DEMUX/DROP)");
317
318 if (module == 1) {
319 return PortNumber.portNumber(inputPort.split(DN_PORT)[1]);
320 } else {
321 return PortNumber.portNumber(outputPort.split(DN_PORT)[1]);
322 }
323 }
324
325 /**
326 * Converts cross connect flow rule to module and connection.
327 *
328 * Connection number is incremental within the class and associated to the rule hash.
329 *
330 * @param xc the cross connect flow rule
331 * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
332 */
alessio70252912021-12-16 14:47:41 +0100333 private Pair<Short, Short> setModuleIdConnection(LumentumFlowRule xc) {
334 int moduleId, connectionId;
335
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100336 if (xc.isAddRule()) {
alessio70252912021-12-16 14:47:41 +0100337 moduleId = 1;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100338 } else {
alessio70252912021-12-16 14:47:41 +0100339 moduleId = 2;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100340 }
alessio70252912021-12-16 14:47:41 +0100341
342 xc.setConnectionModule(moduleId);
343
344 connectionId = generateConnectionId(xc);
345
346 if (connectionId == 0) {
347 log.error("Max connections configured on device module {}", moduleId);
348 return null;
349 }
350
351 xc.setConnectionId(connectionId);
352
353 return Pair.of((short) moduleId, (short) connectionId);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100354 }
355
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100356 //Following Lumentum documentation rpc operation to configure a new connection
alessiod4a2b842019-04-30 18:43:17 +0200357 private boolean rpcAddConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100358
alessio70252912021-12-16 14:47:41 +0100359 Pair<Short, Short> pair = setModuleIdConnection(xc);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100360 String module = pair.getLeft().toString();
361 String connectionId = pair.getRight().toString();
362
alessio70252912021-12-16 14:47:41 +0100363 log.debug("Lumentum driver new connection sent moduleId {} connId {}",
364 xc.getConnectionModule(),
365 xc.getConnectionId());
366
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100367 //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
368 Frequency freqRadius = Frequency.ofHz(xc.ochSignal().channelSpacing().frequency().asHz() / 2);
369 Frequency center = xc.ochSignal().centralFrequency();
370 String startFreq = String.valueOf(center.subtract(freqRadius).asHz() / GHZ);
371 String endFreq = String.valueOf(center.add(freqRadius).asHz() / GHZ);
372
373 StringBuilder stringBuilder = new StringBuilder();
374 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
375 stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
376 stringBuilder.append(
377 "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
378 stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
379 stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
380 stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
381 stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
382 stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
383
384 if (xc.isAddRule()) {
385 stringBuilder.append(
386 "<input-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
387 stringBuilder.append(
388 "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
389 } else {
390 stringBuilder.append(
391 "<input-port-reference>" + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
392 stringBuilder.append(
393 "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
394 }
395 stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
396 stringBuilder.append("</add-connection>" + "\n");
397 stringBuilder.append("</rpc>" + "\n");
398
alessio1bf2a632019-06-04 15:47:39 +0200399 log.info("Lumentum ROADM20 - RPC add-connection sent to device {}", did());
400 log.debug("Lumentum ROADM20 - RPC add-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100401
402 return editCrossConnect(stringBuilder.toString());
403 }
404
405 //Following Lumentum documentation <edit-config> operation to edit connection parameter
406 //Currently only edit the "attenuation" parameter
407 private boolean editConnection(String moduleId, String connectionId, int attenuation) {
408
409 double attenuationDouble = ((double) attenuation) / 100;
410
411 StringBuilder stringBuilder = new StringBuilder();
412 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
413 stringBuilder.append("<edit-config>" + "\n");
414 stringBuilder.append("<target>" + "\n");
415 stringBuilder.append("<running/>" + "\n");
416 stringBuilder.append("</target>" + "\n");
417 stringBuilder.append("<config>" + "\n");
418 stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
419 stringBuilder.append("<connection>" + "\n");
420 stringBuilder.append("" +
421 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
422 //Other configurable parameters
423 //stringBuilder.append("<custom-name/>" + "\n");
424 //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
425 //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
426 //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
427 stringBuilder.append("<config>" + "\n");
428 stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
429 stringBuilder.append("</config>" + "\n");
430 stringBuilder.append("</connection>" + "\n");
431 stringBuilder.append("</connections>" + "\n");
432 stringBuilder.append("</config>" + "\n");
433 stringBuilder.append("</edit-config>" + "\n");
434 stringBuilder.append("</rpc>" + "\n");
435
alessio1bf2a632019-06-04 15:47:39 +0200436 log.info("Lumentum ROADM20 - edit-connection sent to device {}", did());
437 log.debug("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100438
439 return editCrossConnect(stringBuilder.toString());
440 }
441
442 //Following Lumentum documentation rpc operation to delete a new connection
alessiod4a2b842019-04-30 18:43:17 +0200443 private boolean rpcDeleteConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100444
alessio1bf2a632019-06-04 15:47:39 +0200445 //Look for corresponding rule into the cache
446 FlowRule cacheRule = getConnectionCache().get(did()).stream()
447 .filter(r -> (r.selector().equals(xc.selector()) && r.treatment().equals(xc.treatment())))
448 .findFirst()
449 .orElse(null);
450
451 if (cacheRule == null) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100452 log.error("Lumentum RPC delete-connection, connection not found on the local cache");
Ray Milkeycc7a1762018-06-18 08:54:51 -0700453 throw new IllegalStateException("Lumentum RPC delete-connection, connection not found on the local cache");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100454 }
455
alessio1bf2a632019-06-04 15:47:39 +0200456
457 int moduleId = ((LumentumFlowRule) cacheRule).getConnectionModule();
458 int connId = ((LumentumFlowRule) cacheRule).getConnectionId();
459
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100460
461 StringBuilder stringBuilder = new StringBuilder();
462 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
463 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
464 stringBuilder.append(
alessio1bf2a632019-06-04 15:47:39 +0200465 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100466 stringBuilder.append("</delete-connection>" + "\n");
467 stringBuilder.append("</rpc>" + " \n");
468
alessio1bf2a632019-06-04 15:47:39 +0200469 log.info("Lumentum ROADM20 - RPC delete-connection sent to device {}", did());
470 log.debug("Lumentum ROADM20 - - RPC delete-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100471
472 return editCrossConnect(stringBuilder.toString());
473 }
474
475 //Following Lumentum documentation rpc operation to delete a new connection
476 //Executed if for some reason a connection not in the cache is detected
alessio1bf2a632019-06-04 15:47:39 +0200477 private boolean rpcDeleteExternalConnection(short moduleId, short connectionId) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100478
479 StringBuilder stringBuilder = new StringBuilder();
480 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
481 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
alessio1bf2a632019-06-04 15:47:39 +0200482 stringBuilder.append("<dn>ne=1;chassis=1;card=1;module="
483 + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100484 stringBuilder.append("</delete-connection>" + "\n");
485 stringBuilder.append("</rpc>" + "\n");
486
alessio1bf2a632019-06-04 15:47:39 +0200487 log.info("Lumentum ROADM20 - RPC delete-external-connection sent to device {}", did());
488 log.debug("Lumentum ROADM20 - - RPC delete-external-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100489
490 return editCrossConnect(stringBuilder.toString());
491 }
492
493
494 private boolean editCrossConnect(String xcString) {
495 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100496 if (session == null) {
497 log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
498 return false;
499 }
500
501 try {
502 return session.editConfig(xcString);
503 } catch (NetconfException e) {
alessioa9bcacc2021-09-24 17:32:57 +0200504 log.error("Failed to edit the CrossConnect edit-cfg for device {}",
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100505 handler().data().deviceId(), e);
506 log.debug("Failed configuration {}", xcString);
507 return false;
508 }
509 }
510
alessio1bf2a632019-06-04 15:47:39 +0200511 /**
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100512 * Convert start and end frequencies to OCh signal.
513 *
514 * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
515 * FIXME: supports channel spacing 50 and 100
516 *
517 * @param start starting frequency as double in GHz
518 * @param end end frequency as double in GHz
519 * @return OCh signal
520 */
521 public static OchSignal toOchSignal(double start, double end) {
522 int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
523 int multiplier = 0;
524
525 //Conversion for 50 GHz slots
526 if (end - start == 50) {
527 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
528 / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
529
530 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
531 }
532
533 //Conversion for 100 GHz slots
534 if (end - start == 100) {
535 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
536 / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
537
538 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
539 }
540
541 return null;
542 }
543
544 /**
545 * Generate a valid connectionId, the connectionId is a field required by the device every time
546 * a connection is created/edited/removed.
547 *
alessio70252912021-12-16 14:47:41 +0100548 * Device only supports connection id < 130
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100549 */
alessio70252912021-12-16 14:47:41 +0100550 private int generateConnectionId(LumentumFlowRule xc) {
551
552 int moduleNew = xc.getConnectionModule();
553
554 //LUMENTUM_ROADM20_MAX_CONNECTIONS = 100, device only supports connection id < 130
alessio1bf2a632019-06-04 15:47:39 +0200555 for (int i = 1; i < LUMENTUM_ROADM20_MAX_CONNECTIONS; i++) {
556 Set<FlowRule> rulesForDevice = getConnectionCache().get(did());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100557
alessio1bf2a632019-06-04 15:47:39 +0200558 if (rulesForDevice == null) {
559 return 1;
560 } else {
561 Set<Integer> connIds = rulesForDevice.stream()
alessio70252912021-12-16 14:47:41 +0100562 .filter(flow -> ((LumentumFlowRule) flow).getConnectionModule() == moduleNew)
alessio1bf2a632019-06-04 15:47:39 +0200563 .map(flow -> ((LumentumFlowRule) flow).getConnectionId())
564 .collect(Collectors.toSet());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100565
alessio1bf2a632019-06-04 15:47:39 +0200566 if (!connIds.contains(i)) {
567 return i;
568 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100569 }
570 }
571 return 0;
572 }
alessio1bf2a632019-06-04 15:47:39 +0200573
574 private DeviceConnectionCache getConnectionCache() {
575 return DeviceConnectionCache.init();
576 }
577
578 /**
579 * Helper method to get the device id.
580 */
581 private DeviceId did() {
582 return data().deviceId();
583 }
584
585 /**
586 * Helper method to get the Netconf session.
587 */
588 private NetconfSession getNetconfSession() {
589 NetconfController controller =
590 checkNotNull(handler().get(NetconfController.class));
591 return controller.getNetconfDevice(did()).getSession();
592 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100593}