Add driver for oplink netconf device.
Change-Id: I3c8a2e5ba3f79df48a2212b25664821891b6d2ae
diff --git a/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java
new file mode 100755
index 0000000..92847f9
--- /dev/null
+++ b/drivers/oplink/src/main/java/org/onosproject/drivers/oplink/OplinkOpticalPowerConfig.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.onosproject.drivers.oplink;
+
+import com.google.common.collect.Range;
+import org.apache.commons.configuration.HierarchicalConfiguration;
+import org.onosproject.driver.extensions.OplinkAttenuation;
+import org.onosproject.net.Direction;
+import org.onosproject.net.OchSignal;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.behaviour.PowerConfig;
+import org.onosproject.net.driver.AbstractHandlerBehaviour;
+import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.FlowEntry;
+import org.onosproject.net.flow.FlowRuleService;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.slf4j.Logger;
+
+import java.util.Optional;
+
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.MAX_ATTENUATION;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.MIN_ATTENUATION;
+import static org.onosproject.drivers.oplink.OplinkOpticalUtility.POWER_MULTIPLIER;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.onosproject.drivers.oplink.OplinkNetconfUtility.*;
+
+/**
+ * Get current or target port/channel power from an Oplink optical netconf device.
+ * Set target port power or channel attenuation to an optical netconf device.
+ */
+public class OplinkOpticalPowerConfig<T> extends AbstractHandlerBehaviour
+ implements PowerConfig<T> {
+
+ // key
+ public static final String KEY_CHNUM = "wavelength-number";
+ public static final String KEY_CHPWR = "wavelength-power";
+ public static final String KEY_CHSTATS = "wavelength-stats";
+ public static final String KEY_OCMSTATS = "ocm-stats";
+ public static final String KEY_PORTDIRECT_RX = "rx";
+ public static final String KEY_PORTDIRECT_TX = "tx";
+ public static final String KEY_PORTTARPWR = "port-target-power";
+ public static final String KEY_PORTCURPWR = "port-current-power";
+ public static final String KEY_PORTPROPERTY = "port-property";
+ public static final String KEY_PORTPWRCAPMINRX = "port-power-capability-min-rx";
+ public static final String KEY_PORTPWRCAPMAXRX = "port-power-capability-max-rx";
+ public static final String KEY_PORTPWRCAPMINTX = "port-power-capability-min-tx";
+ public static final String KEY_PORTPWRCAPMAXTX = "port-power-capability-max-tx";
+ public static final String KEY_PORTS_PORT = String.format("%s.%s", KEY_DATA_PORTS, KEY_PORT);
+ public static final String KEY_PORTS_PORT_PROPERTY = String.format("%s.%s", KEY_PORTS_PORT, KEY_PORTPROPERTY);
+ // log
+ private static final Logger log = getLogger(OplinkOpticalPowerConfig.class);
+
+
+ @Override
+ public Optional<Long> getTargetPower(PortNumber port, T component) {
+ return Optional.ofNullable(acquireTargetPower(port, component));
+ }
+
+ @Override
+ public void setTargetPower(PortNumber port, T component, long power) {
+ if (component instanceof OchSignal) {
+ setChannelTargetPower(port, (OchSignal) component, power);
+ } else {
+ setPortTargetPower(port, power);
+ }
+ }
+
+ @Override
+ public Optional<Long> currentPower(PortNumber port, T component) {
+ return Optional.ofNullable(acquireCurrentPower(port, component));
+ }
+
+ @Override
+ public Optional<Range<Long>> getTargetPowerRange(PortNumber port, T component) {
+ return Optional.ofNullable(getTxPowerRange(port, component));
+ }
+
+ @Override
+ public Optional<Range<Long>> getInputPowerRange(PortNumber port, T component) {
+ return Optional.ofNullable(getRxPowerRange(port, component));
+ }
+
+ private String getPortPowerFilter(PortNumber port, String selection) {
+ return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+ .append(xmlOpen(KEY_PORTS))
+ .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+ .append(xmlOpen(KEY_PORT))
+ .append(xmlEmpty(selection))
+ .append(xmlClose(KEY_PORT))
+ .append(xmlClose(KEY_PORTS))
+ .append(xmlClose(KEY_OPENOPTICALDEV))
+ .toString();
+ }
+
+ private String getChannelPowerFilter(PortNumber port, OchSignal channel) {
+ return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+ .append(xmlOpen(KEY_PORTS))
+ .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+ .append(xmlOpen(KEY_PORT))
+ .append(xmlOpen(KEY_OCMSTATS))
+ .append(xml(KEY_CHNUM, Integer.toString(channel.spacingMultiplier())))
+ .append(xmlEmpty(KEY_CHSTATS))
+ .append(xmlClose(KEY_OCMSTATS))
+ .append(xmlClose(KEY_PORT))
+ .append(xmlClose(KEY_PORTS))
+ .append(xmlClose(KEY_OPENOPTICALDEV))
+ .toString();
+ }
+
+ private String getChannelAttenuationFilter(PortNumber port, OchSignal channel) {
+ return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+ .append(xmlOpen(KEY_CONNS))
+ .append(xml(KEY_CONNID, Integer.toString(channel.spacingMultiplier())))
+ .append(xmlEmpty(KEY_CHATT))
+ .append(xmlClose(KEY_CONNS))
+ .append(xmlClose(KEY_OPENOPTICALDEV))
+ .toString();
+ }
+
+ private String getPowerRangeFilter(PortNumber port, String direction) {
+ return new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+ .append(xmlOpen(KEY_PORTS))
+ .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+ .append(xmlOpen(KEY_PORT))
+ .append(xml(KEY_PORTDIRECT, direction))
+ .append(xmlEmpty(KEY_PORTPROPERTY))
+ .append(xmlClose(KEY_PORT))
+ .append(xmlClose(KEY_PORTS))
+ .append(xmlClose(KEY_OPENOPTICALDEV))
+ .toString();
+ }
+
+ private Long acquireTargetPower(PortNumber port, T component) {
+ if (component instanceof OchSignal) {
+ return acquireChannelAttenuation(port, (OchSignal) component);
+ }
+ log.debug("Get port{} target power...", port);
+ return acquirePortPower(port, KEY_PORTTARPWR);
+ }
+
+ private Long acquireCurrentPower(PortNumber port, T component) {
+ if (component instanceof OchSignal) {
+ return acquireChannelPower(port, (OchSignal) component);
+ }
+ log.debug("Get port{} current power...", port);
+ return acquirePortPower(port, KEY_PORTCURPWR);
+ }
+
+ private Long acquirePortPower(PortNumber port, String selection) {
+ String reply = netconfGetConfig(handler(), getPortPowerFilter(port, selection));
+ HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT);
+ if (info == null) {
+ return null;
+ }
+ return (long) (info.getDouble(selection) * POWER_MULTIPLIER);
+ }
+
+ private Long acquireChannelAttenuation(PortNumber port, OchSignal channel) {
+ log.debug("Get port{} channel{} attenuation...", port, channel.channelSpacing());
+ String reply = netconfGetConfig(handler(), getChannelAttenuationFilter(port, channel));
+ HierarchicalConfiguration info = configAt(reply, KEY_CONNS);
+ if (info == null) {
+ return null;
+ }
+ return (long) (info.getDouble(KEY_CHATT) * POWER_MULTIPLIER);
+ }
+
+ private Long acquireChannelPower(PortNumber port, OchSignal channel) {
+ log.debug("Get port{} channel{} power...", port, channel.channelSpacing());
+ String reply = netconfGetConfig(handler(), getChannelPowerFilter(port, channel));
+ HierarchicalConfiguration info = configAt(reply, KEY_DATA_CONNS);
+ if (info == null) {
+ return null;
+ }
+ return (long) (info.getDouble(KEY_CHPWR) * POWER_MULTIPLIER);
+ }
+
+ private boolean setPortTargetPower(PortNumber port, long power) {
+ log.debug("Set port{} target power...", port);
+ String cfg = new StringBuilder(xmlOpen(KEY_OPENOPTICALDEV_XMLNS))
+ .append(xmlOpen(KEY_PORTS))
+ .append(xml(KEY_PORTID, Long.toString(port.toLong())))
+ .append(xmlOpen(KEY_PORT))
+ .append(xml(KEY_PORTTARPWR, Long.toString(power)))
+ .append(xmlClose(KEY_PORT))
+ .append(xmlClose(KEY_PORTS))
+ .append(xmlClose(KEY_OPENOPTICALDEV))
+ .toString();
+ return netconfEditConfig(handler(), CFG_MODE_MERGE, cfg);
+ }
+
+ private boolean setChannelTargetPower(PortNumber port, OchSignal channel, long power) {
+ log.debug("Set port{} channel{} attenuation.", port, channel.channelSpacing());
+ FlowRuleService service = handler().get(FlowRuleService.class);
+ Iterable<FlowEntry> entries = service.getFlowEntries(data().deviceId());
+ for (FlowEntry entry : entries) {
+ OplinkCrossConnect crossConnect = OplinkOpticalUtility.fromFlowRule(this, entry);
+ // The channel port might be input port or output port.
+ if ((port.equals(crossConnect.getInPort()) || port.equals(crossConnect.getOutPort())) &&
+ channel.spacingMultiplier() == crossConnect.getChannel()) {
+ log.debug("Flow is found, modify the flow with attenuation.");
+ // Modify attenuation in treatment
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(crossConnect.getOutPort())
+ .extension(new OplinkAttenuation((int) power), data().deviceId())
+ .build();
+ // Apply the new flow rule
+ service.applyFlowRules(DefaultFlowRule.builder()
+ .forDevice(data().deviceId())
+ .makePermanent()
+ .withSelector(entry.selector())
+ .withTreatment(treatment)
+ .withPriority(entry.priority())
+ .withCookie(entry.id().value())
+ .build());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Range<Long> getPowerRange(PortNumber port, String directionKey, String minKey, String maxKey) {
+ String reply = netconfGetConfig(handler(), getPowerRangeFilter(port, directionKey));
+ HierarchicalConfiguration info = configAt(reply, KEY_PORTS_PORT_PROPERTY);
+ if (info == null) {
+ return null;
+ }
+ long minPower = (long) (info.getDouble(minKey) * POWER_MULTIPLIER);
+ long maxPower = (long) (info.getDouble(maxKey) * POWER_MULTIPLIER);
+ return Range.closed(minPower, maxPower);
+ }
+
+ private Range<Long> getTxPowerRange(PortNumber port, T component) {
+ if (component instanceof Direction) {
+ log.debug("Get target port{} power range...", port);
+ return getPowerRange(port, KEY_PORTDIRECT_TX, KEY_PORTPWRCAPMINTX, KEY_PORTPWRCAPMAXTX);
+ } else {
+ log.debug("Get channel attenuation range...");
+ return Range.closed(MIN_ATTENUATION, MAX_ATTENUATION);
+ }
+ }
+
+ private Range<Long> getRxPowerRange(PortNumber port, T component) {
+ log.debug("Get input port{} power range...", port);
+ return getPowerRange(port, KEY_PORTDIRECT_RX, KEY_PORTPWRCAPMINRX, KEY_PORTPWRCAPMAXRX);
+ }
+}