diff --git a/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkSwitchHandshaker.java b/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkSwitchHandshaker.java new file mode 100644 index 0000000000..194bea9341 --- /dev/null +++ b/drivers/optical/src/main/java/org/onosproject/driver/optical/handshaker/OplinkSwitchHandshaker.java @@ -0,0 +1,267 @@ +/* + * 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.handshaker; + +import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsEntry; +import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFCalientPortDescStatsRequest; +import org.projectfloodlight.openflow.protocol.OFCalientPortStatsEntry; +import org.projectfloodlight.openflow.protocol.OFCalientPortStatsRequest; +import org.projectfloodlight.openflow.protocol.OFCalientPortStatsReply; +import org.projectfloodlight.openflow.protocol.OFCalientStatsReply; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.OFPortDesc; +import org.projectfloodlight.openflow.protocol.OFStatsRequest; +import org.projectfloodlight.openflow.protocol.OFStatsReply; +import org.projectfloodlight.openflow.protocol.OFStatsType; +import org.projectfloodlight.openflow.protocol.OFType; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; + +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.Device; +import org.onosproject.net.device.DefaultPortDescription; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.device.PortDescription; +import org.onosproject.net.Port; +import org.onosproject.openflow.controller.OpenFlowOpticalSwitch; +import org.onosproject.openflow.controller.PortDescPropertyType; +import org.onosproject.openflow.controller.driver.AbstractOpenFlowSwitch; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeAlreadyStarted; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeCompleted; +import org.onosproject.openflow.controller.driver.SwitchDriverSubHandshakeNotStarted; +import org.projectfloodlight.openflow.protocol.OFObject; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; + + +/** + * Oplink Open Flow Protection Optical Switch handshaker - for Open Flow 1.3. + * In order to reduce the code changes in the short term, we reuse Calient message structure. + */ +public class OplinkSwitchHandshaker extends AbstractOpenFlowSwitch implements OpenFlowOpticalSwitch { + + private final AtomicBoolean driverHandshakeComplete = new AtomicBoolean(false); + private List opticalPorts = new ArrayList<>(); + private static final String INPUT_PORT_STATUS = "inputStatus"; + private static final String OUTPUT_PORT_STATUS = "ouputStatus"; + private static final String INPUT_PORT_POWER = "inputPower"; + private static final String OUTPUT_PORT_POWER = "ouputPower"; + private static final String IN_SERVICE_ANNOTATION = "inService"; + private static final String OUT_SERVICE_ANNOTATION = "outOfService"; + + private enum SubType { + PORT_DESC_STATS, // Port description stats openflow message + FLOW_STATS, // Flow stats openflow message + PORT_STATS // Port stats openflow message + } + + @Override + public Boolean supportNxRole() { + return false; + } + + @Override + public void startDriverHandshake() { + log.info("OPLK Switch: Starting driver handshake for sw {}", getStringId()); + if (startDriverHandshakeCalled) { + throw new SwitchDriverSubHandshakeAlreadyStarted(); + } + startDriverHandshakeCalled = true; + + log.debug("OPLK Switch: sendHandshakeOFExperimenterPortDescRequest for sw {}", getStringId()); + + try { + sendHandshakeOFExperimenterPortDescRequest(); + } catch (IOException e) { + log.error("OPLK Switch: exception while sending experimenter port desc:", e); + } + } + + @Override + public void processDriverHandshakeMessage(OFMessage m) { + if (!startDriverHandshakeCalled) { + throw new SwitchDriverSubHandshakeNotStarted(); + } + if (driverHandshakeComplete.get()) { + throw new SwitchDriverSubHandshakeCompleted(m); + } + + log.info("OPLK Switch: processDriverHandshakeMessage for sw {}", getStringId()); + + switch (m.getType()) { + case STATS_REPLY: // multipart message is reported as STAT + processOFMultipartReply((OFStatsReply) m); + driverHandshakeComplete.set(true); + break; + default: + log.warn("OPLK Switch: Received message {} during switch-driver " + + "subhandshake from switch {} ... " + + "Ignoring message", m, getStringId()); + } + } + + @Override + public final void sendMsg(OFMessage m) { + List messages = new ArrayList<>(); + messages.add(m); + if (m.getType() == OFType.STATS_REQUEST) { + OFStatsRequest sr = (OFStatsRequest) m; + log.debug("OPLK Switch: Rebuilding stats request type {}", sr.getStatsType()); + switch (sr.getStatsType()) { + case PORT: + //Send experiment status request for Optical Fiber switch to device + //Note: We just re-use calient message for a short term. + OFCalientPortStatsRequest portRequest = this.factory().buildCalientPortStatsRequest() + .setXid(sr.getXid()) + .setFlags(sr.getFlags()) + .build(); + messages.add(portRequest); + break; + default: + break; + } + } else { + log.debug("OPLK Switch: sends msg:{}, as is", m.getType()); + } + super.sendMsg(messages); + } + + @Override + public boolean isDriverHandshakeComplete() { + return driverHandshakeComplete.get(); + } + + private void sendHandshakeOFExperimenterPortDescRequest() throws + IOException { + /**Note: + * Oplink protection switch and Calient switch are both optical fiber switch, + * so Calient port description matches well for Oplink switch. + * OFCalientPortDescStatsRequest is generated by loxi. + * If change the OF message name, we need to change onos-loxi. + * To reduce code change for a short term, we reuse calient message and message name. + * These will be re-processed in the future. + */ + OFCalientPortDescStatsRequest preq = factory() + .buildCalientPortDescStatsRequest() + .setXid(getNextTransactionId()) + .build(); + + log.info("OPLK Switch: Sending experimented port description message {}", preq); + + this.sendHandshakeMessage(preq); + } + + @Override + public Device.Type deviceType() { + return Device.Type.FIBER_SWITCH; + } + + /* + * OduClt ports are reported as regular ETH ports. + */ + @Override + public List getPorts() { + return ImmutableList.copyOf( + ports.stream().flatMap(p -> p.getEntries().stream()) + .collect(Collectors.toList())); + } + + @Override + public List getPortsOf(PortDescPropertyType type) { + return ImmutableList.copyOf(opticalPorts); + } + + @Override + public Set getPortTypes() { + return ImmutableSet.of(PortDescPropertyType.OPTICAL_TRANSPORT); + } + + @Override + public List processExpPortStats(OFMessage msg) { + OFCalientStatsReply statsReply = (OFCalientStatsReply) msg; + //Sub type from openflowj is start from index 1 + SubType type = SubType.values()[(int) statsReply.getSubtype() - 1]; + switch (type) { + case PORT_STATS: + //Note: We just re-use calient message for a short term. + OFCalientPortStatsReply portStats = (OFCalientPortStatsReply) msg; + return buildPortDescriptions(portStats.getEntries()); + default: + //Ignore other messages + log.warn("OPLK Switch: Received message {} from switch {} ... " + + "Ignoring message", msg, getStringId()); + return null; + } + } + + private List buildPortDescriptions(List entries) { + DeviceService deviceService = this.handler().get(DeviceService.class); + List ports = deviceService.getPorts(this.data().deviceId()); + HashMap statsMap = new HashMap<>(entries.size()); + entries.forEach(entry -> statsMap.put((long) entry.getPortNo().getPortNumber(), entry)); + final List portDescs = new ArrayList<>(); + for (Port port : ports) { + DefaultAnnotations.Builder builder = DefaultAnnotations.builder(); + builder.putAll(port.annotations()); + OFCalientPortStatsEntry entry = statsMap.get(port.number().toLong()); + if (entry == null) { + continue; + } + builder.set(INPUT_PORT_POWER, entry.getInportPower()); + builder.set(OUTPUT_PORT_POWER, entry.getOutportPower()); + //Note: There are some mistakes about bitmask encoding and decoding in openflowj. + //We just use this code for a short term, and will modify in the future. + if (entry.getInOperStatus().isEmpty()) { + builder.set(INPUT_PORT_STATUS, IN_SERVICE_ANNOTATION); + } else { + builder.set(INPUT_PORT_STATUS, OUT_SERVICE_ANNOTATION); + } + if (entry.getOutOperStatus().isEmpty()) { + builder.set(OUTPUT_PORT_STATUS, IN_SERVICE_ANNOTATION); + } else { + builder.set(OUTPUT_PORT_STATUS, OUT_SERVICE_ANNOTATION); + } + portDescs.add(new DefaultPortDescription(port.number(), port.isEnabled(), + port.type(), port.portSpeed(), builder.build())); + } + return portDescs; + } + + private void processOFMultipartReply(OFStatsReply stats) { + log.debug("OPLK Switch: Received message {} during switch-driver " + + "subhandshake from switch {} ... ", stats, getStringId()); + //Process experimenter messages + if (stats.getStatsType() == OFStatsType.EXPERIMENTER) { + try { + //Note: We just re-use calient message for a short term. + OFCalientPortDescStatsReply descReply = (OFCalientPortDescStatsReply) stats; + opticalPorts.addAll(descReply.getPortDesc()); + driverHandshakeComplete.set(true); + } catch (ClassCastException e) { + log.error("OPLK Switch: Unexspected Experimenter Multipart message type {} ", + stats.getClass().getName()); + } + } + } +} diff --git a/drivers/optical/src/main/resources/optical-drivers.xml b/drivers/optical/src/main/resources/optical-drivers.xml index fd9f81ac14..96dad6c491 100644 --- a/drivers/optical/src/main/resources/optical-drivers.xml +++ b/drivers/optical/src/main/resources/optical-drivers.xml @@ -64,5 +64,11 @@ impl="org.onosproject.driver.extensions.OplinkExtensionTreatmentInterpreter"/> + + + +