/* * 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); } }