blob: 1fe8148c1babf5872fd36f41824c7388023b7f3a [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);
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 */
alessiod4a2b842019-04-30 18:43:17 +0200333 private Pair<Short, Short> setModuleConnection(LumentumFlowRule xc, Integer id) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100334 if (xc.isAddRule()) {
alessio1bf2a632019-06-04 15:47:39 +0200335 xc.setConnectionModule((short) 1);
336 xc.setConnectionId(id.shortValue());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100337 return Pair.of((short) 1, id.shortValue());
338 } else {
alessio1bf2a632019-06-04 15:47:39 +0200339 xc.setConnectionModule((short) 2);
340 xc.setConnectionId(id.shortValue());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100341 return Pair.of((short) 2, id.shortValue());
342 }
343 }
344
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100345 //Following Lumentum documentation rpc operation to configure a new connection
alessiod4a2b842019-04-30 18:43:17 +0200346 private boolean rpcAddConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100347
348 int currentConnectionId = generateConnectionId();
349
350 if (currentConnectionId == 0) {
351 log.error("Lumentum driver - 100 connections are already configured on the device");
352 return false;
353 }
354
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100355 Pair<Short, Short> pair = setModuleConnection(xc, currentConnectionId);
356 String module = pair.getLeft().toString();
357 String connectionId = pair.getRight().toString();
358
359 //Conversion of ochSignal format (center frequency + diameter) to Lumentum frequency slot format (start - end)
360 Frequency freqRadius = Frequency.ofHz(xc.ochSignal().channelSpacing().frequency().asHz() / 2);
361 Frequency center = xc.ochSignal().centralFrequency();
362 String startFreq = String.valueOf(center.subtract(freqRadius).asHz() / GHZ);
363 String endFreq = String.valueOf(center.add(freqRadius).asHz() / GHZ);
364
365 StringBuilder stringBuilder = new StringBuilder();
366 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
367 stringBuilder.append("<add-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
368 stringBuilder.append(
369 "<dn>ne=1;chassis=1;card=1;module=" + module + ";connection=" + connectionId + "</dn>" + "\n");
370 stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
371 stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
372 stringBuilder.append("<attenuation>" + "0.0" + "</attenuation>" + "\n");
373 stringBuilder.append("<blocked>" + "false" + "</blocked>" + "\n");
374 stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
375
376 if (xc.isAddRule()) {
377 stringBuilder.append(
378 "<input-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</input-port-reference>" + "\n");
379 stringBuilder.append(
380 "<output-port-reference>" + DN_CARD1 + MUX_OUT + "</output-port-reference>" + "\n");
381 } else {
382 stringBuilder.append(
383 "<input-port-reference>" + DN_CARD1 + DEMUX_IN + "</input-port-reference>" + "\n");
384 stringBuilder.append(
385 "<output-port-reference>" + DN_CARD1 + xc.addDrop().toString() + "</output-port-reference>" + "\n");
386 }
387 stringBuilder.append("<custom-name>" + "onos-connection" + "</custom-name>" + "\n");
388 stringBuilder.append("</add-connection>" + "\n");
389 stringBuilder.append("</rpc>" + "\n");
390
alessio1bf2a632019-06-04 15:47:39 +0200391 log.info("Lumentum ROADM20 - RPC add-connection sent to device {}", did());
392 log.debug("Lumentum ROADM20 - RPC add-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100393
394 return editCrossConnect(stringBuilder.toString());
395 }
396
397 //Following Lumentum documentation <edit-config> operation to edit connection parameter
398 //Currently only edit the "attenuation" parameter
399 private boolean editConnection(String moduleId, String connectionId, int attenuation) {
400
401 double attenuationDouble = ((double) attenuation) / 100;
402
403 StringBuilder stringBuilder = new StringBuilder();
404 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
405 stringBuilder.append("<edit-config>" + "\n");
406 stringBuilder.append("<target>" + "\n");
407 stringBuilder.append("<running/>" + "\n");
408 stringBuilder.append("</target>" + "\n");
409 stringBuilder.append("<config>" + "\n");
410 stringBuilder.append("<connections xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
411 stringBuilder.append("<connection>" + "\n");
412 stringBuilder.append("" +
413 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
414 //Other configurable parameters
415 //stringBuilder.append("<custom-name/>" + "\n");
416 //stringBuilder.append("<maintenance-state>" + "in-service" + "</maintenance-state>" + "\n");
417 //stringBuilder.append("<start-freq>" + startFreq + "</start-freq>" + "\n");
418 //stringBuilder.append("<end-freq>" + endFreq + "</end-freq>" + "\n");
419 stringBuilder.append("<config>" + "\n");
420 stringBuilder.append("<attenuation>" + attenuationDouble + "</attenuation>" + "\n");
421 stringBuilder.append("</config>" + "\n");
422 stringBuilder.append("</connection>" + "\n");
423 stringBuilder.append("</connections>" + "\n");
424 stringBuilder.append("</config>" + "\n");
425 stringBuilder.append("</edit-config>" + "\n");
426 stringBuilder.append("</rpc>" + "\n");
427
alessio1bf2a632019-06-04 15:47:39 +0200428 log.info("Lumentum ROADM20 - edit-connection sent to device {}", did());
429 log.debug("Lumentum ROADM20 - edit-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100430
431 return editCrossConnect(stringBuilder.toString());
432 }
433
434 //Following Lumentum documentation rpc operation to delete a new connection
alessiod4a2b842019-04-30 18:43:17 +0200435 private boolean rpcDeleteConnection(LumentumFlowRule xc) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100436
alessio1bf2a632019-06-04 15:47:39 +0200437 //Look for corresponding rule into the cache
438 FlowRule cacheRule = getConnectionCache().get(did()).stream()
439 .filter(r -> (r.selector().equals(xc.selector()) && r.treatment().equals(xc.treatment())))
440 .findFirst()
441 .orElse(null);
442
443 if (cacheRule == null) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100444 log.error("Lumentum RPC delete-connection, connection not found on the local cache");
Ray Milkeycc7a1762018-06-18 08:54:51 -0700445 throw new IllegalStateException("Lumentum RPC delete-connection, connection not found on the local cache");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100446 }
447
alessio1bf2a632019-06-04 15:47:39 +0200448
449 int moduleId = ((LumentumFlowRule) cacheRule).getConnectionModule();
450 int connId = ((LumentumFlowRule) cacheRule).getConnectionId();
451
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100452
453 StringBuilder stringBuilder = new StringBuilder();
454 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
455 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
456 stringBuilder.append(
alessio1bf2a632019-06-04 15:47:39 +0200457 "<dn>ne=1;chassis=1;card=1;module=" + moduleId + ";connection=" + connId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100458 stringBuilder.append("</delete-connection>" + "\n");
459 stringBuilder.append("</rpc>" + " \n");
460
alessio1bf2a632019-06-04 15:47:39 +0200461 log.info("Lumentum ROADM20 - RPC delete-connection sent to device {}", did());
462 log.debug("Lumentum ROADM20 - - RPC delete-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100463
464 return editCrossConnect(stringBuilder.toString());
465 }
466
467 //Following Lumentum documentation rpc operation to delete a new connection
468 //Executed if for some reason a connection not in the cache is detected
alessio1bf2a632019-06-04 15:47:39 +0200469 private boolean rpcDeleteExternalConnection(short moduleId, short connectionId) {
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100470
471 StringBuilder stringBuilder = new StringBuilder();
472 stringBuilder.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">" + "\n");
473 stringBuilder.append("<delete-connection xmlns=\"http://www.lumentum.com/lumentum-ote-connection\">" + "\n");
alessio1bf2a632019-06-04 15:47:39 +0200474 stringBuilder.append("<dn>ne=1;chassis=1;card=1;module="
475 + moduleId + ";connection=" + connectionId + "</dn>" + "\n");
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100476 stringBuilder.append("</delete-connection>" + "\n");
477 stringBuilder.append("</rpc>" + "\n");
478
alessio1bf2a632019-06-04 15:47:39 +0200479 log.info("Lumentum ROADM20 - RPC delete-external-connection sent to device {}", did());
480 log.debug("Lumentum ROADM20 - - RPC delete-external-connection sent to device {} {}", did(), stringBuilder);
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100481
482 return editCrossConnect(stringBuilder.toString());
483 }
484
485
486 private boolean editCrossConnect(String xcString) {
487 NetconfSession session = getNetconfSession();
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100488 if (session == null) {
489 log.error("Lumentum NETCONF - session not found for device {}", handler().data().deviceId());
490 return false;
491 }
492
493 try {
494 return session.editConfig(xcString);
495 } catch (NetconfException e) {
alessioa9bcacc2021-09-24 17:32:57 +0200496 log.error("Failed to edit the CrossConnect edit-cfg for device {}",
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100497 handler().data().deviceId(), e);
498 log.debug("Failed configuration {}", xcString);
499 return false;
500 }
501 }
502
alessio1bf2a632019-06-04 15:47:39 +0200503 /**
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100504 * Convert start and end frequencies to OCh signal.
505 *
506 * FIXME: assumes slots of 12.5 GHz while devices allows granularity 6.25 GHz
507 * FIXME: supports channel spacing 50 and 100
508 *
509 * @param start starting frequency as double in GHz
510 * @param end end frequency as double in GHz
511 * @return OCh signal
512 */
513 public static OchSignal toOchSignal(double start, double end) {
514 int slots = (int) ((end - start) / ChannelSpacing.CHL_12P5GHZ.frequency().asGHz());
515 int multiplier = 0;
516
517 //Conversion for 50 GHz slots
518 if (end - start == 50) {
519 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
520 / ChannelSpacing.CHL_50GHZ.frequency().asGHz());
521
522 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_50GHZ, multiplier, slots);
523 }
524
525 //Conversion for 100 GHz slots
526 if (end - start == 100) {
527 multiplier = (int) (((end - start) / 2 + start - Spectrum.CENTER_FREQUENCY.asGHz())
528 / ChannelSpacing.CHL_100GHZ.frequency().asGHz());
529
530 return new OchSignal(GridType.DWDM, ChannelSpacing.CHL_100GHZ, multiplier, slots);
531 }
532
533 return null;
534 }
535
536 /**
537 * Generate a valid connectionId, the connectionId is a field required by the device every time
538 * a connection is created/edited/removed.
539 *
540 *
541 * Device only supports connection id < 100
542 */
alessio1bf2a632019-06-04 15:47:39 +0200543 private int generateConnectionId() {
544 //LUMENTUM_ROADM20_MAX_CONNECTIONS = 100, device only supports connection id < 100
545 for (int i = 1; i < LUMENTUM_ROADM20_MAX_CONNECTIONS; i++) {
546 Set<FlowRule> rulesForDevice = getConnectionCache().get(did());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100547
alessio1bf2a632019-06-04 15:47:39 +0200548 if (rulesForDevice == null) {
549 return 1;
550 } else {
551 Set<Integer> connIds = rulesForDevice.stream()
552 .map(flow -> ((LumentumFlowRule) flow).getConnectionId())
553 .collect(Collectors.toSet());
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100554
alessio1bf2a632019-06-04 15:47:39 +0200555 if (!connIds.contains(i)) {
556 return i;
557 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100558 }
559 }
560 return 0;
561 }
alessio1bf2a632019-06-04 15:47:39 +0200562
563 private DeviceConnectionCache getConnectionCache() {
564 return DeviceConnectionCache.init();
565 }
566
567 /**
568 * Helper method to get the device id.
569 */
570 private DeviceId did() {
571 return data().deviceId();
572 }
573
574 /**
575 * Helper method to get the Netconf session.
576 */
577 private NetconfSession getNetconfSession() {
578 NetconfController controller =
579 checkNotNull(handler().get(NetconfController.class));
580 return controller.getNetconfDevice(did()).getSession();
581 }
Alessio Giorgetti648b5382018-02-15 18:35:45 +0100582}