Java tutorial
/* * Copyright 2016-present 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.driver.optical.power; import java.util.List; import java.util.Optional; import com.google.common.collect.Range; import org.onosproject.driver.extensions.OplinkAttenuation; import org.onosproject.net.OchSignal; import org.onosproject.net.driver.AbstractHandlerBehaviour; import org.onosproject.net.Direction; import org.onosproject.net.Port; import org.onosproject.net.PortNumber; import org.onosproject.net.behaviour.PowerConfig; import org.onosproject.net.device.DeviceService; import org.onosproject.net.flow.DefaultFlowRule; import org.onosproject.net.flow.DefaultTrafficTreatment; import org.onosproject.net.flow.FlowEntry; import org.onosproject.net.flow.FlowRule; import org.onosproject.net.flow.FlowRuleService; import org.onosproject.net.flow.TrafficSelector; import org.onosproject.net.flow.TrafficTreatment; import org.onosproject.net.flow.criteria.Criterion; import org.onosproject.net.flow.criteria.OchSignalCriterion; import org.onosproject.net.flow.criteria.PortCriterion; import org.onosproject.net.flow.instructions.ExtensionTreatment; import org.onosproject.net.flow.instructions.ExtensionTreatmentType; import org.onosproject.net.flow.instructions.Instruction; import org.onosproject.net.flow.instructions.Instructions; import org.onosproject.net.optical.OpticalAnnotations; import org.onosproject.openflow.controller.Dpid; import org.onosproject.openflow.controller.OpenFlowController; import org.onosproject.openflow.controller.OpenFlowSwitch; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Port Power (Gain and attenuation) implementation for Oplink 1-SLOT-8D ROADM. * * An Oplink ROADM port exposes OchSignal resources. * Optical Power can be set at port level or channel/wavelength level (attenuation). * */ public class OplinkRoadmPowerConfig extends AbstractHandlerBehaviour implements PowerConfig<Object> { private static final int LINE_IN = 1; private static final int LINE_OUT = 2; private static final int AUX_OUT_1 = 3; private static final int AUX_OUT_2 = 4; private static final int EXPRESS_OUT_1 = 5; private static final int EXPRESS_OUT_2 = 6; private static final int EXPRESS_OUT_3 = 7; private static final int EXPRESS_OUT_4 = 8; private static final int EXPRESS_OUT_5 = 9; private static final int EXPRESS_OUT_6 = 10; private static final int EXPRESS_OUT_7 = 11; private static final int AUX_IN_1 = 12; private static final int AUX_IN_2 = 13; private static final int EXPRESS_IN_1 = 14; private static final int EXPRESS_IN_2 = 15; private static final int EXPRESS_IN_3 = 16; private static final int EXPRESS_IN_4 = 17; private static final int EXPRESS_IN_5 = 18; private static final int EXPRESS_IN_6 = 19; private static final int EXPRESS_IN_7 = 20; protected final Logger log = LoggerFactory.getLogger(getClass()); // Component type private enum Type { NONE, PORT, CHANNEL } // Get the type if component is valid private Type getType(Object component) { if (component == null || component instanceof Direction) { return Type.PORT; } else if (component instanceof OchSignal) { return Type.CHANNEL; } else { return Type.NONE; } } private OpenFlowSwitch getOpenFlowDevice() { final OpenFlowController controller = this.handler().get(OpenFlowController.class); final Dpid dpid = Dpid.dpid(this.data().deviceId().uri()); OpenFlowSwitch sw = controller.getSwitch(dpid); if (sw == null || !sw.isConnected()) { return null; } else { return sw; } } // Find matching flow on device private FlowEntry findFlow(PortNumber portNum, OchSignal och) { FlowRuleService service = this.handler().get(FlowRuleService.class); Iterable<FlowEntry> flowEntries = service.getFlowEntries(this.data().deviceId()); // Return first matching flow for (FlowEntry entry : flowEntries) { TrafficSelector selector = entry.selector(); OchSignalCriterion entrySigid = (OchSignalCriterion) selector.getCriterion(Criterion.Type.OCH_SIGID); if (entrySigid != null && och.equals(entrySigid.lambda())) { PortCriterion entryPort = (PortCriterion) selector.getCriterion(Criterion.Type.IN_PORT); if (entryPort != null && portNum.equals(entryPort.port())) { return entry; } } } log.warn("No matching flow found"); return null; } @Override public Optional<Long> getTargetPower(PortNumber portNum, Object component) { Long returnVal = null; // Check if switch is connected, otherwise do not return value in store, // which is obsolete. if (getOpenFlowDevice() != null) { switch (getType(component)) { case PORT: // Will be implemented in the future. break; case CHANNEL: returnVal = getChannelAttenuation(portNum, (OchSignal) component); break; default: break; } } return Optional.ofNullable(returnVal); } @Override public Optional<Long> currentPower(PortNumber portNum, Object component) { Long returnVal = null; // Check if switch is connected, otherwise do not return value in store, // which is obsolete. if (getOpenFlowDevice() != null) { switch (getType(component)) { case PORT: returnVal = getCurrentPortPower(portNum); break; case CHANNEL: returnVal = getCurrentChannelPower(portNum, (OchSignal) component); break; default: break; } } return Optional.ofNullable(returnVal); } @Override public void setTargetPower(PortNumber portNum, Object component, long power) { if (getOpenFlowDevice() != null) { switch (getType(component)) { case PORT: setTargetPortPower(portNum, power); break; case CHANNEL: setChannelAttenuation(portNum, (OchSignal) component, power); break; default: break; } } else { log.warn("OpenFlow handshaker driver not found or device is not connected"); } } @Override public Optional<Range<Long>> getTargetPowerRange(PortNumber port, Object component) { Range<Long> range = null; switch (getType(component)) { case PORT: range = getTargetPortPowerRange(port); break; case CHANNEL: range = getChannelAttenuationRange(port); break; default: break; } return Optional.ofNullable(range); } @Override public Optional<Range<Long>> getInputPowerRange(PortNumber port, Object component) { Range<Long> range = null; switch (getType(component)) { case PORT: range = getInputPortPowerRange(port); break; default: break; } return Optional.ofNullable(range); } private Long getChannelAttenuation(PortNumber portNum, OchSignal och) { FlowEntry flowEntry = findFlow(portNum, och); if (flowEntry != null) { List<Instruction> instructions = flowEntry.treatment().allInstructions(); for (Instruction ins : instructions) { if (ins.type() == Instruction.Type.EXTENSION) { ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins) .extensionInstruction(); if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) { return (long) ((OplinkAttenuation) ext).getAttenuation(); } } } } return null; } private Long getCurrentPortPower(PortNumber portNum) { DeviceService deviceService = this.handler().get(DeviceService.class); Port port = deviceService.getPort(this.data().deviceId(), portNum); if (port != null) { String currentPower = port.annotations().value(OpticalAnnotations.CURRENT_POWER); if (currentPower != null) { return Long.valueOf(currentPower); } } return null; } private Long getCurrentChannelPower(PortNumber portNum, OchSignal och) { FlowEntry flowEntry = findFlow(portNum, och); if (flowEntry != null) { // TODO put somewhere else if possible // We put channel power in packets return flowEntry.packets(); } return null; } private void setTargetPortPower(PortNumber portNum, long power) { OpenFlowSwitch device = getOpenFlowDevice(); device.sendMsg(device.factory().buildOplinkPortPowerSet().setXid(0).setPort((int) portNum.toLong()) .setPowerValue((int) power).build()); } private void setChannelAttenuation(PortNumber portNum, OchSignal och, long power) { FlowEntry flowEntry = findFlow(portNum, och); if (flowEntry != null) { List<Instruction> instructions = flowEntry.treatment().allInstructions(); for (Instruction ins : instructions) { if (ins.type() == Instruction.Type.EXTENSION) { ExtensionTreatment ext = ((Instructions.ExtensionInstructionWrapper) ins) .extensionInstruction(); if (ext.type() == ExtensionTreatmentType.ExtensionTreatmentTypes.OPLINK_ATTENUATION.type()) { ((OplinkAttenuation) ext).setAttenuation((int) power); FlowRuleService service = this.handler().get(FlowRuleService.class); service.applyFlowRules(flowEntry); return; } } } addAttenuation(flowEntry, power); } else { log.warn("Target channel power not set"); } } // Replace flow with new flow containing Oplink attenuation extension instruction. Also resets // metrics. private void addAttenuation(FlowEntry flowEntry, long power) { FlowRule.Builder flowBuilder = new DefaultFlowRule.Builder(); flowBuilder.withCookie(flowEntry.id().value()); flowBuilder.withPriority(flowEntry.priority()); flowBuilder.forDevice(flowEntry.deviceId()); flowBuilder.forTable(flowEntry.tableId()); if (flowEntry.isPermanent()) { flowBuilder.makePermanent(); } else { flowBuilder.makeTemporary(flowEntry.timeout()); } flowBuilder.withSelector(flowEntry.selector()); // Copy original instructions and add attenuation instruction TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder(); flowEntry.treatment().allInstructions().forEach(ins -> treatmentBuilder.add(ins)); treatmentBuilder.add(Instructions.extension(new OplinkAttenuation((int) power), this.data().deviceId())); flowBuilder.withTreatment(treatmentBuilder.build()); FlowRuleService service = this.handler().get(FlowRuleService.class); service.applyFlowRules(flowBuilder.build()); } // Returns the acceptable target range for an output Port, null otherwise private Range<Long> getTargetPortPowerRange(PortNumber port) { Range<Long> range = null; long num = port.toLong(); if (num == LINE_OUT) { range = Range.closed(100L, 2040L); } else if (num >= AUX_OUT_1 && num <= EXPRESS_OUT_7) { range = Range.closed(-680L, 1530L); } return range; } // Returns the acceptable attenuation range for a connection (represented as // a flow with attenuation instruction). Port can be either the input or // output port of the connection. Returns null if the connection does not // support attenuation. private Range<Long> getChannelAttenuationRange(PortNumber port) { Range<Long> range = null; long num = port.toLong(); // Only connections from AuxIn to LineOut or ExpressIn to LineOut support // attenuation. if (num == LINE_OUT || num >= AUX_IN_1 && num <= EXPRESS_IN_7) { range = Range.closed(0L, 2550L); } return range; } // Returns the working input power range for an input port, null if the port // is not an input port. private Range<Long> getInputPortPowerRange(PortNumber port) { Range<Long> range = null; long portNum = port.toLong(); if (portNum == LINE_IN) { // TODO implement support for IR and ER range // only supports LR right now range = Range.closed(-2600L, 540L); } else if (portNum == AUX_IN_1 || portNum == AUX_IN_2) { range = Range.closed(-1250L, 1590L); } else if (portNum >= EXPRESS_IN_1 && portNum <= EXPRESS_IN_7) { range = Range.closed(-1420L, 1420L); } return range; } }