Jan Kundrát | 981fe47 | 2019-10-15 22:44:19 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2019-2020 Jan Kundrát, CESNET, <jan.kundrat@cesnet.cz> and 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 | |
| 17 | package org.onosproject.drivers.czechlight; |
| 18 | |
| 19 | import org.apache.commons.configuration.HierarchicalConfiguration; |
| 20 | import org.onlab.util.Spectrum; |
| 21 | import org.onosproject.core.CoreService; |
| 22 | import org.onosproject.drivers.odtn.impl.DeviceConnectionCache; |
| 23 | import org.onosproject.net.ChannelSpacing; |
| 24 | import org.onosproject.net.GridType; |
| 25 | import org.onosproject.net.Lambda; |
| 26 | import org.onosproject.net.OchSignal; |
| 27 | import org.onosproject.net.OchSignalType; |
| 28 | import org.onosproject.net.PortNumber; |
| 29 | import org.onosproject.net.device.DeviceService; |
| 30 | import org.onosproject.net.driver.AbstractHandlerBehaviour; |
| 31 | import org.onosproject.net.flow.DefaultFlowEntry; |
| 32 | import org.onosproject.net.flow.DefaultFlowRule; |
| 33 | import org.onosproject.net.flow.DefaultTrafficSelector; |
| 34 | import org.onosproject.net.flow.DefaultTrafficTreatment; |
| 35 | import org.onosproject.net.flow.FlowEntry; |
| 36 | import org.onosproject.net.flow.FlowRule; |
| 37 | import org.onosproject.net.flow.FlowRuleProgrammable; |
| 38 | import org.onosproject.net.flow.FlowRuleService; |
| 39 | import org.onosproject.net.flow.TrafficSelector; |
| 40 | import org.onosproject.net.flow.TrafficTreatment; |
| 41 | import org.onosproject.net.flow.criteria.Criteria; |
| 42 | import org.onosproject.net.flow.criteria.OchSignalCriterion; |
| 43 | import org.onosproject.net.flow.criteria.PortCriterion; |
| 44 | import org.onosproject.net.flow.instructions.Instructions; |
| 45 | import org.onosproject.net.flow.instructions.L0ModificationInstruction; |
| 46 | import org.onosproject.netconf.DatastoreId; |
| 47 | import org.onosproject.netconf.NetconfController; |
| 48 | import org.onosproject.netconf.NetconfException; |
| 49 | import org.onosproject.netconf.NetconfSession; |
| 50 | import org.slf4j.Logger; |
| 51 | import org.slf4j.LoggerFactory; |
| 52 | |
| 53 | import java.util.ArrayList; |
| 54 | import java.util.Collection; |
| 55 | import java.util.Map; |
| 56 | import java.util.Objects; |
| 57 | import java.util.TreeMap; |
| 58 | import java.util.stream.Collectors; |
| 59 | |
| 60 | import static com.google.common.base.Preconditions.checkNotNull; |
| 61 | |
| 62 | /** Modification of the MC by a ROADM device. |
| 63 | * The signal might be either attenuated by a specified amount of dB, or its target power can be set |
| 64 | * to a specified power in dBm. |
| 65 | */ |
| 66 | class MCManipulation { |
| 67 | Double attenuation; |
| 68 | Double targetPower; |
| 69 | |
| 70 | public MCManipulation(final Double attenuation, final Double targetPower) { |
| 71 | this.attenuation = attenuation; |
| 72 | this.targetPower = targetPower; |
| 73 | } |
| 74 | |
| 75 | public String toString() { |
| 76 | if (attenuation != null) { |
| 77 | return "attenuation: " + String.valueOf(attenuation); |
| 78 | } |
| 79 | |
| 80 | if (targetPower != null) { |
| 81 | return "targetPower: " + String.valueOf(targetPower); |
| 82 | } |
| 83 | |
| 84 | return "none"; |
| 85 | } |
| 86 | }; |
| 87 | |
| 88 | /** Representation of a ROADM configuration for a given Media Channel. |
| 89 | * This contains frequency (`channel`), routing (`leafPort`) and attenuation or power set point (`manipulation`). |
| 90 | * */ |
| 91 | class CzechLightRouting { |
| 92 | MediaChannelDefinition channel; |
| 93 | int leafPort; |
| 94 | MCManipulation manipulation; |
| 95 | |
| 96 | public CzechLightRouting(final MediaChannelDefinition channel, final int leafPort, final MCManipulation manip) { |
| 97 | this.channel = channel; |
| 98 | this.leafPort = leafPort; |
| 99 | this.manipulation = manip; |
| 100 | } |
| 101 | |
| 102 | public String toString() { |
| 103 | return channel.toString() + " -> " + String.valueOf(leafPort) + " (" + manipulation.toString() + ")"; |
| 104 | } |
| 105 | }; |
| 106 | |
| 107 | /** |
| 108 | * Implementation of FlowRuleProgrammable interface for CzechLight SDN ROADMs. |
| 109 | */ |
| 110 | public class CzechLightFlowRuleProgrammable extends AbstractHandlerBehaviour implements FlowRuleProgrammable { |
| 111 | |
| 112 | private final Logger log = |
| 113 | LoggerFactory.getLogger(getClass()); |
| 114 | |
| 115 | private static final String NETCONF_OP_MERGE = "merge"; |
| 116 | private static final String NETCONF_OP_NONE = "none"; |
| 117 | private static final String ELEMENT_ADD = "add"; |
| 118 | private static final String ELEMENT_DROP = "drop"; |
| 119 | |
| 120 | private enum Direction { |
| 121 | ADD, |
| 122 | DROP, |
| 123 | }; |
| 124 | // FIXME: can we get this programmaticaly? |
| 125 | private static final String DEFAULT_APP = "org.onosproject.drivers.czechlight"; |
| 126 | |
| 127 | @Override |
| 128 | public Collection<FlowEntry> getFlowEntries() { |
| 129 | if (deviceType() == CzechLightDiscovery.DeviceType.INLINE_AMP |
| 130 | || deviceType() == CzechLightDiscovery.DeviceType.COHERENT_ADD_DROP) { |
| 131 | final var data = getConnectionCache().get(data().deviceId()); |
| 132 | if (data == null) { |
| 133 | return new ArrayList<>(); |
| 134 | } |
| 135 | return data.stream() |
| 136 | .map(rule -> new DefaultFlowEntry(rule)) |
| 137 | .collect(Collectors.toList()); |
| 138 | } |
| 139 | |
| 140 | HierarchicalConfiguration xml; |
| 141 | try { |
| 142 | xml = doGetSubtree(CzechLightDiscovery.CHANNEL_DEFS_FILTER + CzechLightDiscovery.MC_ROUTING_FILTER); |
| 143 | } catch (NetconfException e) { |
| 144 | log.error("Cannot read data from NETCONF: {}", e); |
| 145 | return new ArrayList<>(); |
| 146 | } |
| 147 | final var allChannels = MediaChannelDefinition.parseChannelDefinitions(xml); |
| 148 | |
| 149 | Collection<FlowEntry> list = new ArrayList<>(); |
| 150 | |
| 151 | final var allMCs = xml.configurationsAt("data.media-channels"); |
| 152 | allMCs.stream() |
| 153 | .map(cfg -> confToMCRouting(ELEMENT_ADD, allChannels, cfg)) |
| 154 | .filter(Objects::nonNull) |
| 155 | .forEach(flow -> { |
| 156 | log.debug("{}: found ADD: {}", data().deviceId(), flow.toString()); |
| 157 | list.add(new DefaultFlowEntry(asFlowRule(Direction.ADD, flow), FlowEntry.FlowEntryState.ADDED)); |
| 158 | }); |
| 159 | allMCs.stream() |
| 160 | .map(cfg -> confToMCRouting(ELEMENT_DROP, allChannels, cfg)) |
| 161 | .filter(Objects::nonNull) |
| 162 | .forEach(flow -> { |
| 163 | log.debug("{}: found DROP: {}", data().deviceId(), flow.toString()); |
| 164 | list.add(new DefaultFlowEntry(asFlowRule(Direction.DROP, flow), FlowEntry.FlowEntryState.ADDED)); |
| 165 | }); |
| 166 | return list; |
| 167 | } |
| 168 | |
| 169 | @Override |
| 170 | public Collection<FlowRule> applyFlowRules(Collection<FlowRule> rules) { |
| 171 | if (deviceType() == CzechLightDiscovery.DeviceType.INLINE_AMP |
| 172 | || deviceType() == CzechLightDiscovery.DeviceType.COHERENT_ADD_DROP) { |
| 173 | rules.forEach( |
| 174 | rule -> { |
| 175 | log.debug("{}: asked for {} (whole C-band is always forwarded by the HW)", |
| 176 | data().deviceId(), rule); |
| 177 | getConnectionCache().add(data().deviceId(), rule.toString(), rule); |
| 178 | } |
| 179 | ); |
| 180 | return rules; |
| 181 | } |
| 182 | |
| 183 | HierarchicalConfiguration xml; |
| 184 | try { |
| 185 | xml = doGetSubtree(CzechLightDiscovery.CHANNEL_DEFS_FILTER + CzechLightDiscovery.MC_ROUTING_FILTER); |
| 186 | } catch (NetconfException e) { |
| 187 | log.error("Cannot read data from NETCONF: {}", e); |
| 188 | return new ArrayList<>(); |
| 189 | } |
| 190 | final var allChannels = MediaChannelDefinition.parseChannelDefinitions(xml); |
| 191 | var hopefullyAdded = new ArrayList<FlowRule>(); |
| 192 | |
| 193 | // temporary store because both ADD and DROP must go into the same <media-channel> list item |
| 194 | var changes = new TreeMap<String, String>(); |
| 195 | rules.forEach( |
| 196 | rule -> { |
| 197 | log.debug("{}: asked to INSERT rule for:", data().deviceId()); |
| 198 | rule.selector().criteria().forEach( |
| 199 | criteria -> log.debug(" criteria {}", criteria.toString()) |
| 200 | ); |
| 201 | rule.treatment().allInstructions().forEach( |
| 202 | instruction -> log.debug(" instruction {}", instruction.toString()) |
| 203 | ); |
| 204 | |
| 205 | String element; |
| 206 | long leafPort; |
| 207 | if (inputPortFromFlow(rule).toLong() == CzechLightDiscovery.PORT_COMMON) { |
| 208 | element = ELEMENT_DROP; |
| 209 | leafPort = outputPortFromFlow(rule).toLong(); |
| 210 | } else { |
| 211 | element = ELEMENT_ADD; |
| 212 | leafPort = inputPortFromFlow(rule).toLong(); |
| 213 | } |
| 214 | final var och = ochSignalFromFlow(rule); |
| 215 | final var channel = allChannels.entrySet().stream() |
| 216 | .filter(entry -> MediaChannelDefinition.mcMatches(entry, och)) |
| 217 | .findAny() |
| 218 | .orElse(null); |
| 219 | if (channel == null) { |
| 220 | log.error("No matching channel definition available for the following rule at {}:", |
| 221 | data().deviceId()); |
| 222 | rule.selector().criteria().forEach( |
| 223 | criteria -> log.error(" criteria {}", criteria.toString()) |
| 224 | ); |
| 225 | rule.treatment().allInstructions().forEach( |
| 226 | instruction -> log.error(" instruction {}", instruction.toString()) |
| 227 | ); |
| 228 | } else { |
| 229 | log.info("{}: Creating \"{}\" MC {}: leaf {}", data().deviceId(), |
| 230 | element, channel.getKey(), leafPort); |
| 231 | var sb = new StringBuilder(); |
| 232 | sb.append("<"); |
| 233 | sb.append(element); |
| 234 | sb.append(">"); |
| 235 | sb.append("<port>"); |
| 236 | if (deviceType() == CzechLightDiscovery.DeviceType.LINE_DEGREE) { |
| 237 | sb.append(CzechLightDiscovery.LINE_EXPRESS_PREFIX); |
| 238 | } |
| 239 | sb.append(String.valueOf(leafPort)); |
| 240 | sb.append("</port>"); |
| 241 | // FIXME: propagate attenuation or power target |
| 242 | if (deviceType() == CzechLightDiscovery.DeviceType.LINE_DEGREE) { |
| 243 | if (outputPortFromFlow(rule).toLong() == CzechLightDiscovery.PORT_COMMON) { |
| 244 | sb.append("<power>-5.0</power>"); |
| 245 | } else { |
| 246 | sb.append("<power>-12.0</power>"); |
| 247 | } |
| 248 | } else { |
| 249 | if (outputPortFromFlow(rule).toLong() == CzechLightDiscovery.PORT_COMMON) { |
| 250 | sb.append("<power>-12.0</power>"); |
| 251 | } else { |
| 252 | sb.append("<power>-5.0</power>"); |
| 253 | } |
| 254 | } |
| 255 | sb.append("</"); |
| 256 | sb.append(element); |
| 257 | sb.append(">"); |
| 258 | changes.put(channel.getKey(), |
| 259 | changes.getOrDefault(channel.getKey(), "") + sb.toString()); |
| 260 | hopefullyAdded.add(rule); |
| 261 | } |
| 262 | }); |
| 263 | |
| 264 | if (!hopefullyAdded.isEmpty()) { |
| 265 | var sb = new StringBuilder(); |
| 266 | changes.forEach( |
| 267 | (channel, data) -> { |
| 268 | sb.append(CzechLightDiscovery.XML_MC_OPEN); |
| 269 | sb.append("<channel>"); |
| 270 | sb.append(channel); |
| 271 | sb.append("</channel>"); |
| 272 | sb.append(data); |
| 273 | sb.append(CzechLightDiscovery.XML_MC_CLOSE); |
| 274 | }); |
| 275 | doEditConfig(NETCONF_OP_MERGE, sb.toString()); |
| 276 | } |
| 277 | return hopefullyAdded; |
| 278 | } |
| 279 | |
| 280 | @Override |
| 281 | public Collection<FlowRule> removeFlowRules(Collection<FlowRule> rules) { |
| 282 | if (deviceType() == CzechLightDiscovery.DeviceType.INLINE_AMP |
| 283 | || deviceType() == CzechLightDiscovery.DeviceType.COHERENT_ADD_DROP) { |
| 284 | rules.forEach( |
| 285 | rule -> { |
| 286 | log.debug("{}: asked to remove {} (whole C-band is always forwarded by the HW)", |
| 287 | data().deviceId(), rule); |
| 288 | getConnectionCache().remove(data().deviceId(), rule); |
| 289 | } |
| 290 | ); |
| 291 | return rules; |
| 292 | } |
| 293 | HierarchicalConfiguration xml; |
| 294 | try { |
| 295 | xml = doGetSubtree(CzechLightDiscovery.CHANNEL_DEFS_FILTER + CzechLightDiscovery.MC_ROUTING_FILTER); |
| 296 | } catch (NetconfException e) { |
| 297 | log.error("Cannot read data from NETCONF: {}", e); |
| 298 | return new ArrayList<>(); |
| 299 | } |
| 300 | final var allChannels = MediaChannelDefinition.parseChannelDefinitions(xml); |
| 301 | |
| 302 | var hopefullyRemoved = new ArrayList<FlowRule>(); |
| 303 | |
| 304 | // temporary store because both ADD and DROP must go into the same <media-channel> list item |
| 305 | var changes = new TreeMap<String, String>(); |
| 306 | |
| 307 | rules.forEach( |
| 308 | rule -> { |
| 309 | final String element = inputPortFromFlow(rule).toLong() == CzechLightDiscovery.PORT_COMMON ? |
| 310 | ELEMENT_DROP : ELEMENT_ADD; |
| 311 | final var och = ochSignalFromFlow(rule); |
| 312 | final var channel = allChannels.entrySet().stream() |
| 313 | .filter(entry -> MediaChannelDefinition.mcMatches(entry, och)) |
| 314 | .findAny() |
| 315 | .orElse(null); |
| 316 | if (channel == null) { |
| 317 | log.error("Cannot find what channel to remove for the following flow rule at {}:", |
| 318 | data().deviceId()); |
| 319 | rule.selector().criteria().forEach( |
| 320 | criteria -> log.error(" criteria {}", criteria.toString()) |
| 321 | ); |
| 322 | rule.treatment().allInstructions().forEach( |
| 323 | instruction -> log.error(" instruction {}", instruction.toString()) |
| 324 | ); |
| 325 | } else { |
| 326 | log.info("{}: Removing {} MC {}", data().deviceId(), element, channel.getKey()); |
| 327 | changes.put(channel.getKey(), |
| 328 | changes.getOrDefault(channel.getKey(), "") |
| 329 | + "<" + element + " nc:operation=\"remove\"/>"); |
| 330 | hopefullyRemoved.add(rule); |
| 331 | } |
| 332 | }); |
| 333 | |
| 334 | if (!hopefullyRemoved.isEmpty()) { |
| 335 | var sb = new StringBuilder(); |
| 336 | changes.forEach( |
| 337 | (channel, data) -> { |
| 338 | sb.append(CzechLightDiscovery.XML_MC_OPEN); |
| 339 | sb.append("<channel>"); |
| 340 | sb.append(channel); |
| 341 | sb.append("</channel>"); |
| 342 | sb.append(data); |
| 343 | sb.append(CzechLightDiscovery.XML_MC_CLOSE); |
| 344 | }); |
| 345 | doEditConfig(NETCONF_OP_NONE, sb.toString()); |
| 346 | } |
| 347 | return hopefullyRemoved; |
| 348 | } |
| 349 | |
| 350 | private static CzechLightRouting confToMCRouting(final String keyPrefix, |
| 351 | final Map<String, MediaChannelDefinition> allChannels, |
| 352 | final HierarchicalConfiguration item) { |
| 353 | if (!item.containsKey(keyPrefix + ".port")) { |
| 354 | return null; |
| 355 | } |
| 356 | // the leaf port is either just a number, or a number prefixed by "E" |
| 357 | final var portStr = item.getString(keyPrefix + ".port"); |
| 358 | final int leafPort = Integer.parseInt(portStr.startsWith(CzechLightDiscovery.LINE_EXPRESS_PREFIX) ? |
| 359 | portStr.substring(1) : portStr); |
| 360 | return new CzechLightRouting( |
| 361 | allChannels.get(item.getString("channel")), |
| 362 | leafPort, |
| 363 | new MCManipulation( |
| 364 | item.getDouble(keyPrefix + ".attenuation", null), |
| 365 | item.getDouble(keyPrefix + ".power", null) |
| 366 | ) |
| 367 | ); |
| 368 | } |
| 369 | |
| 370 | private FlowRule asFlowRule(final Direction direction, final CzechLightRouting routing) { |
| 371 | FlowRuleService service = handler().get(FlowRuleService.class); |
| 372 | Iterable<FlowEntry> entries = service.getFlowEntries(data().deviceId()); |
| 373 | |
| 374 | final var portIn = PortNumber.portNumber(direction == Direction.DROP ? |
| 375 | CzechLightDiscovery.PORT_COMMON : routing.leafPort); |
| 376 | final var portOut = PortNumber.portNumber(direction == Direction.ADD ? |
| 377 | CzechLightDiscovery.PORT_COMMON : routing.leafPort); |
| 378 | |
| 379 | final var channelWidth = routing.channel.highMHz - routing.channel.lowMHz; |
| 380 | final var channelCentralFreq = (int) (routing.channel.lowMHz + channelWidth / 2); |
| 381 | |
| 382 | for (FlowEntry entry : entries) { |
| 383 | final var och = ochSignalFromFlow(entry); |
| 384 | if (och.centralFrequency().asMHz() == channelCentralFreq |
| 385 | && och.slotWidth().asMHz() == channelWidth |
| 386 | && portIn.equals(inputPortFromFlow(entry)) |
| 387 | && portOut.equals(outputPortFromFlow(entry))) { |
| 388 | return entry; |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | final var channelSlotWidth = (int) (channelWidth / ChannelSpacing.CHL_12P5GHZ.frequency().asMHz()); |
| 393 | final var channelMultiplier = (int) ((channelCentralFreq - Spectrum.CENTER_FREQUENCY.asMHz()) |
| 394 | / ChannelSpacing.CHL_6P25GHZ.frequency().asMHz()); |
| 395 | |
| 396 | TrafficSelector selector = DefaultTrafficSelector.builder() |
| 397 | .matchInPort(portIn) |
| 398 | .add(Criteria.matchOchSignalType(OchSignalType.FLEX_GRID)) |
| 399 | .add(Criteria.matchLambda(Lambda.ochSignal(GridType.FLEX, ChannelSpacing.CHL_6P25GHZ, |
| 400 | channelMultiplier, channelSlotWidth))) |
| 401 | .build(); |
| 402 | TrafficTreatment treatment = DefaultTrafficTreatment.builder() |
| 403 | .setOutput(portOut) |
| 404 | .build(); |
| 405 | return DefaultFlowRule.builder() |
| 406 | .forDevice(data().deviceId()) |
| 407 | .withSelector(selector) |
| 408 | .withTreatment(treatment) |
| 409 | // the concept of priorities does not make sense for a ROADM MC configuration, |
| 410 | // but it's mandatory nonetheless |
| 411 | .withPriority(666) |
| 412 | .makePermanent() |
| 413 | .fromApp(handler().get(CoreService.class).getAppId(DEFAULT_APP)) |
| 414 | .build(); |
| 415 | } |
| 416 | |
| 417 | private static PortNumber inputPortFromFlow(final Object flow) { |
| 418 | return ((flow instanceof FlowEntry) ? |
| 419 | ((FlowEntry) flow).selector() : ((FlowRule) flow).selector()).criteria().stream() |
| 420 | .filter(c -> c instanceof PortCriterion) |
| 421 | .map(c -> ((PortCriterion) c).port()) |
| 422 | .findAny() |
| 423 | .orElse(null); |
| 424 | } |
| 425 | |
| 426 | private static PortNumber outputPortFromFlow(final Object flow) { |
| 427 | return ((flow instanceof FlowEntry) ? |
| 428 | ((FlowEntry) flow).treatment() : ((FlowRule) flow).treatment()).immediate().stream() |
| 429 | .filter(c -> c instanceof Instructions.OutputInstruction) |
| 430 | .map(c -> ((Instructions.OutputInstruction) c).port()) |
| 431 | .findAny() |
| 432 | .orElse(null); |
| 433 | } |
| 434 | |
| 435 | private static OchSignal ochSignalFromFlow(final Object flow) { |
| 436 | final var fromCriteria = ((flow instanceof FlowEntry) ? |
| 437 | ((FlowEntry) flow).selector() : ((FlowRule) flow).selector()).criteria().stream() |
| 438 | .filter(c -> c instanceof OchSignalCriterion) |
| 439 | .map(c -> ((OchSignalCriterion) c).lambda()) |
| 440 | .findAny() |
| 441 | .orElse(null); |
| 442 | if (fromCriteria != null) { |
| 443 | return fromCriteria; |
| 444 | } |
| 445 | return ((flow instanceof FlowEntry) ? |
| 446 | ((FlowEntry) flow).treatment() : ((FlowRule) flow).treatment()).immediate().stream() |
| 447 | .filter(c -> c instanceof L0ModificationInstruction.ModOchSignalInstruction) |
| 448 | .map(c -> ((L0ModificationInstruction.ModOchSignalInstruction) c).lambda()) |
| 449 | .findAny() |
| 450 | .orElse(null); |
| 451 | } |
| 452 | |
| 453 | private CzechLightDiscovery.DeviceType deviceType() { |
| 454 | var annotations = this.handler().get(DeviceService.class).getDevice(handler().data().deviceId()).annotations(); |
| 455 | return CzechLightDiscovery.DeviceType.valueOf(annotations.value(CzechLightDiscovery.DEVICE_TYPE_ANNOTATION)); |
| 456 | } |
| 457 | |
| 458 | private DeviceConnectionCache getConnectionCache() { |
| 459 | return DeviceConnectionCache.init(); |
| 460 | } |
| 461 | |
| 462 | private HierarchicalConfiguration doGetSubtree(final String subtreeXml) throws NetconfException { |
| 463 | NetconfSession session = getNetconfSession(); |
| 464 | if (session == null) { |
| 465 | log.error("Cannot request NETCONF session for {}", data().deviceId()); |
| 466 | return null; |
| 467 | } |
| 468 | return CzechLightDiscovery.doGetSubtree(session, subtreeXml); |
| 469 | } |
| 470 | |
| 471 | private HierarchicalConfiguration doGetXPath(final String prefix, final String namespace, final String xpathFilter) |
| 472 | throws NetconfException { |
| 473 | NetconfSession session = getNetconfSession(); |
| 474 | if (session == null) { |
| 475 | log.error("Cannot request NETCONF session for {}", data().deviceId()); |
| 476 | return null; |
| 477 | } |
| 478 | return CzechLightDiscovery.doGetXPath(session, prefix, namespace, xpathFilter); |
| 479 | } |
| 480 | |
| 481 | public boolean doEditConfig(String mode, String cfg) { |
| 482 | NetconfSession session = getNetconfSession(); |
| 483 | if (session == null) { |
| 484 | log.error("Cannot request NETCONF session for {}", data().deviceId()); |
| 485 | return false; |
| 486 | } |
| 487 | |
| 488 | try { |
| 489 | return session.editConfig(DatastoreId.RUNNING, mode, cfg); |
| 490 | } catch (NetconfException e) { |
| 491 | throw new IllegalStateException(new NetconfException("Failed to edit configuration.", e)); |
| 492 | } |
| 493 | } |
| 494 | |
| 495 | private NetconfSession getNetconfSession() { |
| 496 | NetconfController controller = |
| 497 | checkNotNull(handler().get(NetconfController.class)); |
| 498 | return controller.getNetconfDevice(data().deviceId()).getSession(); |
| 499 | } |
| 500 | } |