blob: 940eac8a435b6e98586eb786171341840673a838 [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;
alessio1bf2a632019-06-04 15:47:39 +020028import org.onosproject.net.DeviceId;
29import org.onosproject.net.ChannelSpacing;
30import org.onosproject.net.GridType;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010031import org.onosproject.net.device.DeviceService;
32import org.onosproject.net.driver.AbstractHandlerBehaviour;
33import org.onosproject.net.flow.DefaultFlowEntry;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010034import org.onosproject.net.flow.FlowEntry;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010035import org.onosproject.net.flow.FlowRule;
36import org.onosproject.net.flow.FlowRuleProgrammable;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010037import org.onosproject.netconf.NetconfController;
38import org.onosproject.netconf.NetconfException;
39import org.onosproject.netconf.NetconfSession;
40import org.slf4j.Logger;
41import org.slf4j.LoggerFactory;
42
43import java.io.ByteArrayInputStream;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010044import java.util.Collection;
alessio1bf2a632019-06-04 15:47:39 +020045import java.util.ArrayList;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010046import java.util.List;
alessio1bf2a632019-06-04 15:47:39 +020047import java.util.Set;
48import java.util.stream.Collectors;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010049
50import static com.google.common.base.Preconditions.checkArgument;
51import static com.google.common.base.Preconditions.checkNotNull;
52
53/**
54 * Implementation of FlowRuleProgrammable interface for Lumentum ROADM-A Whitebox devices using NETCONF.
55 */
56public class LumentumNetconfRoadmFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable {
57
58 private static final Logger log =
59 LoggerFactory.getLogger(LumentumNetconfRoadmFlowRuleProgrammable.class);
60
61 private static final String DN = "dn";
62 private static final String DN_PORT = "port=";
63 private static final String DN_CARD1 = "ne=1;chassis=1;card=1;port=";
64 private static final String CONNECTION = "connection";
65 private static final String CONNECTIONS = "data.connections.connection";
66 private static final String CONFIG = "config";
67 private static final String STATE = "state";
68 private static final String START_FREQ = "start-freq";
69 private static final String END_FREQ = "end-freq";
70 private static final String MODULE = "module";
71 private static final String SEMI_COLON = ";";
72 private static final String EQUAL = "=";
73 private static final String INPUT_PORT_REFERENCE = "input-port-reference";
74 private static final String OUTPUT_PORT_REFERENCE = "output-port-reference";
75
76 private static final String CHANNEL_ATTENUATION = "attenuation";
77 private static final String CHANNEL_INPUT_POWER = "input-channel-attributes.power";
78 private static final String CHANNEL_OUTPUT_POWER = "output-channel-attributes.power";
79
80 protected static final long LINE_PORT = 3001;
81 protected static final PortNumber LINE_PORT_NUMBER = PortNumber.portNumber(LINE_PORT);
82 protected static final long MUX_OUT = 4201;
83 protected static final long DEMUX_IN = 5101;
84 protected static final long GHZ = 1_000_000_000L;
alessio70252912021-12-16 14:47:41 +010085 protected static final int LUMENTUM_ROADM20_MAX_CONNECTIONS = 130;
Alessio Giorgetti648b5382018-02-15 18:35:45 +010086
87 /**Get the flow entries that are present on the Lumentum device, called by FlowRuleDriverProvider.
88 *
89 * The flow entries must match exactly the FlowRule entries in the ONOS store. If they are not an
90 * exact match the device will be requested to remove those flows.
91 *
92 * @return A collection of Flow Entries
93 */
94 @Override
95 public Collection<FlowEntry> getFlowEntries() {
alessio1bf2a632019-06-04 15:47:39 +020096 Collection<FlowEntry> fetched = fetchConnectionsFromDevice().stream()
97 .map(conn -> buildFlowrule(conn))
98 .map(fr -> new DefaultFlowEntry(fr, FlowEntry.FlowEntryState.ADDED, 0, 0, 0))
99 .collect(Collectors.toList());
100
101 //Print out number of rules actually found on the device that are also included in the cache
alessioa280f032024-01-30 02:52:01 +0100102 log.info("Device {} getFlowEntries fetched connections {}", did(), fetched.size());
alessio1bf2a632019-06-04 15:47:39 +0200103
104 return fetched;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100105 }
106
107 /**Apply the flow entries specified in the collection rules.
108 *
109 * @param rules A collection of Flow Rules to be applied to the Lumentum device
110 * @return The collection of added Flow Entries
111 */
112 @Override
113 public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) {
alessio1bf2a632019-06-04 15:47:39 +0200114 //Check NETCONF session
115 NetconfSession session = getNetconfSession();
116 if (session == null) {
117 log.error("Device {} null session", did());
118 return ImmutableList.of();
119 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100120
alessio1bf2a632019-06-04 15:47:39 +0200121 // Apply the rules on the device and add to cache if success
122 Collection<FlowRule> added = new ArrayList<>();
123 for (FlowRule flowRule : rules) {
124 LumentumFlowRule lumFlowRule = new LumentumFlowRule(flowRule, getLinePorts());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100125
alessio1bf2a632019-06-04 15:47:39 +0200126 if (rpcAddConnection(lumFlowRule)) {
127 added.add(lumFlowRule);
128 getConnectionCache().add(did(), lumFlowRule.getConnectionName(), lumFlowRule);
alessioa280f032024-01-30 02:52:01 +0100129 log.info("Adding connection with selector {}", lumFlowRule.selector());
130 log.info("Adding connection with name {}", lumFlowRule.getConnectionName());
alessio1bf2a632019-06-04 15:47:39 +0200131 }
132 }
133
134 //Print out number of rules sent to the device (without receiving errors)
alessioa280f032024-01-30 02:52:01 +0100135 log.info("Device {} applyFlowRules added {}", did(), added.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100136
137 return added;
138 }
139
140 @Override
141 public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) {
alessio1bf2a632019-06-04 15:47:39 +0200142 NetconfSession session = getNetconfSession();
143 if (session == null) {
144 log.error("Device {} null session", did());
145 return ImmutableList.of();
146 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100147
alessio1bf2a632019-06-04 15:47:39 +0200148 // Remove the rules from the device and from the cache
149 List<FlowRule> removed = new ArrayList<>();
150 for (FlowRule r : rules) {
151 try {
152 LumentumFlowRule flowRule = new LumentumFlowRule(r, getLinePorts());
153 rpcDeleteConnection(flowRule);
154 getConnectionCache().remove(did(), r);
155 removed.add(r);
156 } catch (Exception e) {
157 log.error("Device {} Error {}", did(), e);
158 continue;
159 }
160 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100161
alessio1bf2a632019-06-04 15:47:39 +0200162 //Print out number of removed rules from the device (without receiving errors)
alessioa280f032024-01-30 02:52:01 +0100163 log.info("Device {} removeFlowRules removed {}", did(), removed.size());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100164
165 return removed;
166 }
167
168 private List<PortNumber> getLinePorts() {
169 DeviceService deviceService = this.handler().get(DeviceService.class);
170 return deviceService.getPorts(data().deviceId()).stream()
171 .filter(p -> p.number().toLong() == LINE_PORT)
172 .map(p -> p.number())
173 .collect(Collectors.toList());
174 }
175
176 /**
177 * Fetches list of connections from device.
178 *
179 * @return list of connections as XML hierarchy
180 */
181 private List<HierarchicalConfiguration> fetchConnectionsFromDevice() {
182 String reply;
183
184 StringBuilder requestBuilder = new StringBuilder();
185 requestBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">");
186 requestBuilder.append("</connections>");
187
188 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100189 if (session == null) {
190 log.error("Lumentum NETCONF - session not found for {}", handler().data().deviceId());
191 return ImmutableList.of();
192 }
193
194 try {
195 reply = session.get(requestBuilder.toString(), null);
alessio1bf2a632019-06-04 15:47:39 +0200196 log.debug("Lumentum NETCONF - fetchConnectionsFromDevice reply {}", reply);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100197 } catch (NetconfException e) {
198 log.error("Failed to retrieve configuration details for device {}",
199 handler().data().deviceId(), e);
200 return ImmutableList.of();
201 }
202
203 HierarchicalConfiguration cfg =
204 XmlConfigParser.loadXml(new ByteArrayInputStream(reply.getBytes()));
205
206 return cfg.configurationsAt(CONNECTIONS);
207 }
208
209 // Example input dn: ne=1;chassis=1;card=1;module=2;connection=89
210 private Pair<Short, Short> parseDn(String dn) {
211 Short module = null;
212 Short connection = null;
213 for (String entry : dn.split(SEMI_COLON)) {
214 String[] keyVal = entry.split(EQUAL);
215 if (keyVal.length != 2) {
216 continue;
217 }
218 if (keyVal[0].equals(MODULE)) {
219 module = Short.valueOf(keyVal[1]);
220 }
221 if (keyVal[0].equals(CONNECTION)) {
222 connection = Short.valueOf(keyVal[1]);
223 }
224 if (module != null && connection != null) {
225 return Pair.of(module, connection);
226 }
227 }
228
229 return null;
230 }
231
232 /**
233 * Builds a flow rule from a connection hierarchy.
234 *
235 * @param connection the connection hierarchy
236 * @return the flow rule
237 */
238 private FlowRule buildFlowrule(HierarchicalConfiguration connection) {
239
240 String dn = connection.getString(DN);
241 Pair<Short, Short> pair = parseDn(dn);
alessio1bf2a632019-06-04 15:47:39 +0200242 short connId = pair.getRight();
243 short moduleId = pair.getLeft();
244
245 if (pair == null) {
246 log.error("Lumentum NETCONF - device {} error in retrieving DN field", did());
247 return null;
248 }
249
alessioa280f032024-01-30 02:52:01 +0100250 log.info("Lumentum NETCONF - retrieved FlowRule module {} connection {}", moduleId, connId);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100251
252 HierarchicalConfiguration config = connection.configurationAt(CONFIG);
253 double startFreq = config.getDouble(START_FREQ);
254 double endFreq = config.getDouble(END_FREQ);
alessioa280f032024-01-30 02:52:01 +0100255 String inputPort = config.getString(INPUT_PORT_REFERENCE);
256 String outputPort = config.getString(OUTPUT_PORT_REFERENCE);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100257
258 HierarchicalConfiguration state = connection.configurationAt(STATE);
259 double attenuation = state.getDouble(CHANNEL_ATTENUATION);
260 double inputPower = state.getDouble(CHANNEL_INPUT_POWER);
261 double outputPower = state.getDouble(CHANNEL_OUTPUT_POWER);
262
alessioa280f032024-01-30 02:52:01 +0100263 OchSignal signal = toOchSignal(startFreq, endFreq);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100264
alessioa280f032024-01-30 02:52:01 +0100265 PortNumber portNumber = getPortNumber(moduleId, inputPort, outputPort);
266 double central = signal.centralFrequency().asGHz();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100267
alessioa280f032024-01-30 02:52:01 +0100268 String connectionName = "inPort-" + portNumber.toStringWithoutName() + "-centralFreq-" + central;
269 log.info("Lumentum NETCONF - retrieved connection with name {}", connectionName);
270 log.info("Lumentum NETCONF - retrieved connection startFreq {} endFreq {}", startFreq, endFreq);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100271
alessio1bf2a632019-06-04 15:47:39 +0200272 FlowRule cacheRule = null;
alessioa280f032024-01-30 02:52:01 +0100273 cacheRule = getConnectionCache().get(did(), connectionName);
alessio1bf2a632019-06-04 15:47:39 +0200274
275 if (cacheRule == null) {
276 //TODO consider a way to keep "external" FlowRules
alessioa9bcacc2021-09-24 17:32:57 +0200277 log.error("Lumentum NETCONF connection {} not in the cache", pair.getRight());
alessio1bf2a632019-06-04 15:47:39 +0200278 rpcDeleteExternalConnection(moduleId, connId);
279 return null;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100280 } else {
alessio1bf2a632019-06-04 15:47:39 +0200281 //Update monitored values
alessioa280f032024-01-30 02:52:01 +0100282 log.info("Attenuation retrieved {} dB for connection {}",
alessio1bf2a632019-06-04 15:47:39 +0200283 attenuation, ((LumentumFlowRule) cacheRule).getConnectionId());
284 ((LumentumFlowRule) cacheRule).setAttenuation(attenuation);
285 ((LumentumFlowRule) cacheRule).setInputPower(inputPower);
286 ((LumentumFlowRule) cacheRule).setOutputPower(outputPower);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100287
alessio1bf2a632019-06-04 15:47:39 +0200288 return cacheRule;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100289 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100290 }
291
292 /**
293 * Get the port number.
294 * If this is a MUX connection return input-port. Outport is always MUX_OUT = 4201.
295 * If this is a DEMUX connection return output-port. Inport is always DEMUX_IN= 5101.
296 *
297 * @param module the module (1 for MUX/ADD, 2 for DEMUX/DROP)
298 * @return the add/drop port number
299 */
300 private PortNumber getPortNumber(short module, String inputPort, String outputPort) {
301 checkArgument(module == 1 || module == 2, "Module must be 1 (MUX/ADD) or 2 (DEMUX/DROP)");
302
303 if (module == 1) {
304 return PortNumber.portNumber(inputPort.split(DN_PORT)[1]);
305 } else {
306 return PortNumber.portNumber(outputPort.split(DN_PORT)[1]);
307 }
308 }
309
310 /**
311 * Converts cross connect flow rule to module and connection.
312 *
313 * Connection number is incremental within the class and associated to the rule hash.
314 *
315 * @param xc the cross connect flow rule
316 * @return pair of module (1 for MUX/ADD, 2 for DEMUX/DROP) and connection number
317 */
alessio70252912021-12-16 14:47:41 +0100318 private Pair<Short, Short> setModuleIdConnection(LumentumFlowRule xc) {
319 int moduleId, connectionId;
320
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100321 if (xc.isAddRule()) {
alessio70252912021-12-16 14:47:41 +0100322 moduleId = 1;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100323 } else {
alessio70252912021-12-16 14:47:41 +0100324 moduleId = 2;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100325 }
alessio70252912021-12-16 14:47:41 +0100326
327 xc.setConnectionModule(moduleId);
328
329 connectionId = generateConnectionId(xc);
330
331 if (connectionId == 0) {
332 log.error("Max connections configured on device module {}", moduleId);
333 return null;
334 }
335
336 xc.setConnectionId(connectionId);
337
338 return Pair.of((short) moduleId, (short) connectionId);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100339 }
340
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100341 //Following Lumentum documentation rpc operation to configure a new connection
alessiod4a2b842019-04-30 18:43:17 +0200342 private boolean rpcAddConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100343
alessio70252912021-12-16 14:47:41 +0100344 Pair<Short, Short> pair = setModuleIdConnection(xc);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100345 String module = pair.getLeft().toString();
346 String connectionId = pair.getRight().toString();
347
alessioa280f032024-01-30 02:52:01 +0100348 log.info("Lumentum driver new connection sent moduleId {} connId {}",
alessio70252912021-12-16 14:47:41 +0100349 xc.getConnectionModule(),
350 xc.getConnectionId());
351
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100352 //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
alessioa280f032024-01-30 02:52:01 +0100353 Frequency freqRadius = Frequency.ofGHz(xc.ochSignal().slotWidth().asGHz() / 2);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100354 Frequency center = xc.ochSignal().centralFrequency();
alessioa280f032024-01-30 02:52:01 +0100355 String startFreq = String.valueOf(center.subtract(freqRadius).asGHz());
356 String endFreq = String.valueOf(center.add(freqRadius).asGHz());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100357
358 StringBuilder stringBuilder = new StringBuilder();
359 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
360 stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
361 stringBuilder.append(
362 "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
363 stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
364 stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
365 stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
366 stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
367 stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
368
369 if (xc.isAddRule()) {
370 stringBuilder.append(
371 "<input-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
372 stringBuilder.append(
373 "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
374 } else {
375 stringBuilder.append(
376 "<input-port-reference>" + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
377 stringBuilder.append(
378 "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
379 }
380 stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
381 stringBuilder.append("</add-connection>" + "\n");
382 stringBuilder.append("</rpc>" + "\n");
383
alessio1bf2a632019-06-04 15:47:39 +0200384 log.info("Lumentum ROADM20 - RPC add-connection sent to device {}", did());
alessioa280f032024-01-30 02:52:01 +0100385 log.info("Lumentum ROADM20 - RPC add-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100386
387 return editCrossConnect(stringBuilder.toString());
388 }
389
390 //Following Lumentum documentation <edit-config> operation to edit connection parameter
391 //Currently only edit the "attenuation" parameter
392 private boolean editConnection(String moduleId, String connectionId, int attenuation) {
393
394 double attenuationDouble = ((double) attenuation) / 100;
395
396 StringBuilder stringBuilder = new StringBuilder();
397 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
398 stringBuilder.append("<edit-config>" + "\n");
399 stringBuilder.append("<target>" + "\n");
400 stringBuilder.append("<running/>" + "\n");
401 stringBuilder.append("</target>" + "\n");
402 stringBuilder.append("<config>" + "\n");
403 stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
404 stringBuilder.append("<connection>" + "\n");
405 stringBuilder.append("" +
406 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
407 //Other configurable parameters
408 //stringBuilder.append("<custom-name/>" + "\n");
409 //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
410 //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
411 //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
412 stringBuilder.append("<config>" + "\n");
413 stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
414 stringBuilder.append("</config>" + "\n");
415 stringBuilder.append("</connection>" + "\n");
416 stringBuilder.append("</connections>" + "\n");
417 stringBuilder.append("</config>" + "\n");
418 stringBuilder.append("</edit-config>" + "\n");
419 stringBuilder.append("</rpc>" + "\n");
420
alessio1bf2a632019-06-04 15:47:39 +0200421 log.info("Lumentum ROADM20 - edit-connection sent to device {}", did());
alessioa280f032024-01-30 02:52:01 +0100422 log.info("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100423
424 return editCrossConnect(stringBuilder.toString());
425 }
426
427 //Following Lumentum documentation rpc operation to delete a new connection
alessiod4a2b842019-04-30 18:43:17 +0200428 private boolean rpcDeleteConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100429
alessio1bf2a632019-06-04 15:47:39 +0200430 //Look for corresponding rule into the cache
431 FlowRule cacheRule = getConnectionCache().get(did()).stream()
432 .filter(r -> (r.selector().equals(xc.selector()) && r.treatment().equals(xc.treatment())))
433 .findFirst()
434 .orElse(null);
435
436 if (cacheRule == null) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100437 log.error("Lumentum RPC delete-connection, connection not found on the local cache");
Ray Milkeycc7a1762018-06-18 08:54:51 -0700438 throw new IllegalStateException("Lumentum RPC delete-connection, connection not found on the local cache");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100439 }
440
alessio1bf2a632019-06-04 15:47:39 +0200441
442 int moduleId = ((LumentumFlowRule) cacheRule).getConnectionModule();
443 int connId = ((LumentumFlowRule) cacheRule).getConnectionId();
444
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100445
446 StringBuilder stringBuilder = new StringBuilder();
447 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
448 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
449 stringBuilder.append(
alessio1bf2a632019-06-04 15:47:39 +0200450 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100451 stringBuilder.append("</delete-connection>" + "\n");
452 stringBuilder.append("</rpc>" + " \n");
453
alessio1bf2a632019-06-04 15:47:39 +0200454 log.info("Lumentum ROADM20 - RPC delete-connection sent to device {}", did());
alessioa280f032024-01-30 02:52:01 +0100455 log.info("Lumentum ROADM20 - - RPC delete-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100456
457 return editCrossConnect(stringBuilder.toString());
458 }
459
460 //Following Lumentum documentation rpc operation to delete a new connection
461 //Executed if for some reason a connection not in the cache is detected
alessio1bf2a632019-06-04 15:47:39 +0200462 private boolean rpcDeleteExternalConnection(short moduleId, short connectionId) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100463
464 StringBuilder stringBuilder = new StringBuilder();
465 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
466 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
alessio1bf2a632019-06-04 15:47:39 +0200467 stringBuilder.append("<dn>ne=1;chassis=1;card=1;module="
468 + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100469 stringBuilder.append("</delete-connection>" + "\n");
470 stringBuilder.append("</rpc>" + "\n");
471
alessio1bf2a632019-06-04 15:47:39 +0200472 log.info("Lumentum ROADM20 - RPC delete-external-connection sent to device {}", did());
alessioa280f032024-01-30 02:52:01 +0100473 log.info("Lumentum ROADM20 - - RPC delete-external-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100474
475 return editCrossConnect(stringBuilder.toString());
476 }
477
478
479 private boolean editCrossConnect(String xcString) {
480 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100481 if (session == null) {
482 log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
483 return false;
484 }
485
486 try {
487 return session.editConfig(xcString);
488 } catch (NetconfException e) {
alessioa9bcacc2021-09-24 17:32:57 +0200489 log.error("Failed to edit the CrossConnect edit-cfg for device {}",
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100490 handler().data().deviceId(), e);
alessioa280f032024-01-30 02:52:01 +0100491 log.info("Failed configuration {}", xcString);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100492 return false;
493 }
494 }
495
alessio1bf2a632019-06-04 15:47:39 +0200496 /**
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100497 * Convert start and end frequencies to OCh signal.
498 *
499 * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
500 * FIXME: supports channel spacing 50 and 100
501 *
502 * @param start starting frequency as double in GHz
503 * @param end end frequency as double in GHz
504 * @return OCh signal
505 */
506 public static OchSignal toOchSignal(double start, double end) {
507 int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
508 int multiplier = 0;
509
alessioa280f032024-01-30 02:52:01 +0100510 /*//Conversion for 50 GHz slots
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100511 if (end - start == 50) {
512 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
513 / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
514
515 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
516 }
517
518 //Conversion for 100 GHz slots
519 if (end - start == 100) {
520 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
521 / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
522
523 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
alessioa280f032024-01-30 02:52:01 +0100524 }*/
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100525
alessioa280f032024-01-30 02:52:01 +0100526 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
527 / ChannelSpacing.CHL_6P25GHZ.frequency().asGHz());
528
529 return new OchSignal(GridType.FLEX, ChannelSpacing.CHL_6P25GHZ, multiplier, slots);
530
531
532 //return null;
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100533 }
534
535 /**
536 * Generate a valid connectionId, the connectionId is a field required by the device every time
537 * a connection is created/edited/removed.
538 *
alessio70252912021-12-16 14:47:41 +0100539 * Device only supports connection id < 130
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100540 */
alessio70252912021-12-16 14:47:41 +0100541 private int generateConnectionId(LumentumFlowRule xc) {
542
543 int moduleNew = xc.getConnectionModule();
544
545 //LUMENTUM_ROADM20_MAX_CONNECTIONS = 100, device only supports connection id < 130
alessio1bf2a632019-06-04 15:47:39 +0200546 for (int i = 1; i < LUMENTUM_ROADM20_MAX_CONNECTIONS; i++) {
547 Set<FlowRule> rulesForDevice = getConnectionCache().get(did());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100548
alessio1bf2a632019-06-04 15:47:39 +0200549 if (rulesForDevice == null) {
550 return 1;
551 } else {
552 Set<Integer> connIds = rulesForDevice.stream()
alessio70252912021-12-16 14:47:41 +0100553 .filter(flow -> ((LumentumFlowRule) flow).getConnectionModule() == moduleNew)
alessio1bf2a632019-06-04 15:47:39 +0200554 .map(flow -> ((LumentumFlowRule) flow).getConnectionId())
555 .collect(Collectors.toSet());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100556
alessio1bf2a632019-06-04 15:47:39 +0200557 if (!connIds.contains(i)) {
558 return i;
559 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100560 }
561 }
562 return 0;
563 }
alessio1bf2a632019-06-04 15:47:39 +0200564
565 private DeviceConnectionCache getConnectionCache() {
566 return DeviceConnectionCache.init();
567 }
568
569 /**
570 * Helper method to get the device id.
571 */
572 private DeviceId did() {
573 return data().deviceId();
574 }
575
576 /**
577 * Helper method to get the Netconf session.
578 */
579 private NetconfSession getNetconfSession() {
580 NetconfController controller =
581 checkNotNull(handler().get(NetconfController.class));
582 return controller.getNetconfDevice(did()).getSession();
583 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100584}