net.es.netshell.controller.client.SdnControllerClient.java Source code

Java tutorial

Introduction

Here is the source code for net.es.netshell.controller.client.SdnControllerClient.java

Source

/*
 * ESnet Network Operating System (ENOS) Copyright (c) 2016, The Regents
 * of the University of California, through Lawrence Berkeley National
 * Laboratory (subject to receipt of any required approvals from the
 * U.S. Dept. of Energy).  All rights reserved.
 *
 * If you have questions about your rights to use or distribute this
 * software, please contact Berkeley Lab's Innovation & Partnerships
 * Office at IPO@lbl.gov.
 *
 * NOTICE.  This Software was developed under funding from the
 * U.S. Department of Energy and the U.S. Government consequently retains
 * certain rights. As such, the U.S. Government has been granted for
 * itself and others acting on its behalf a paid-up, nonexclusive,
 * irrevocable, worldwide license in the Software to reproduce,
 * distribute copies to the public, prepare derivative works, and perform
 * publicly and display publicly, and to permit other to do so.
 *
 */

package net.es.netshell.controller.client;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;
import net.es.netshell.configuration.NetShellConfiguration;
import net.es.netshell.controller.intf.*;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigInteger;
import java.util.UUID;

/**
 * Implementation of the client-side interface to the SDN controller
 */
public class SdnControllerClient implements Runnable, AutoCloseable {

    // Logging
    static final private Logger logger = LoggerFactory.getLogger(SdnControllerClient.class);

    private ConnectionFactory factory;
    private Connection connection;
    private Channel replyChannel;
    private String replyQueueName;
    private QueueingConsumer replyConsumer;

    private Channel packetInChannel;
    private QueueingConsumer packetInConsumer;

    private ObjectMapper mapper;
    private Thread packetInThread;

    private SdnControllerClientCallback callback;

    public void setCallback(SdnControllerClientCallback c) {
        this.callback = c;
    }

    public void clearCallback() {
        this.callback = null;
    }

    class SdnControllerClientFlowHandleImpl implements SdnControllerClientFlowHandle {
        public boolean valid;

        public byte[] dpid;
        public short tableId;
        public String flowId;

        SdnControllerClientFlowHandleImpl() {
            this.valid = true;
        }

        public boolean isValid() {
            return this.valid;
        }

        public void invalidate() {
            this.valid = false;
        }
    }

    public SdnControllerClient() {
        try {
            // Get a connection to the AMPQ broker (RabbitMQ server)
            factory = new ConnectionFactory();
            String rabbitmqHost = "localhost";
            if (NetShellConfiguration.getInstance() != null
                    && NetShellConfiguration.getInstance().getGlobal() != null) {
                rabbitmqHost = NetShellConfiguration.getInstance().getGlobal().getMessagingHost();
            }
            factory.setHost(rabbitmqHost);

            connection = factory.newConnection();
            replyChannel = connection.createChannel();

            // Set up to receive replies from the SDN controller when we get them
            replyQueueName = replyChannel.queueDeclare().getQueue();
            replyConsumer = new QueueingConsumer(replyChannel);
            replyChannel.basicConsume(replyQueueName, true, replyConsumer);

            // JSON parser setup
            mapper = new ObjectMapper();

            logger.info(SdnControllerClient.class.getName() + " ready with connection to " + rabbitmqHost);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void close() throws Exception {
        clearCallback();
        if (connection != null) {
            connection.close();
            connection = null;
        }
    }

    /**
     * Common code for doing a request-reply sequence with SDN Controller.
     * Use this for various command types.
     * @param req Request object
     * @param clazz Return object class
     * @param <T> Return object class
     * @return Return object
     * @throws Exception
     */
    public <T> T SdnReqRep(SdnRequest req, Class<T> clazz) throws Exception {
        String corrId = UUID.randomUUID().toString();

        BasicProperties props = new BasicProperties.Builder().correlationId(corrId).replyTo(replyQueueName).build();

        // Maybe we can factor out the next few lines of code because they
        // should be common to all request types.  Convert message object to JSON.
        String message = mapper.writeValueAsString(req);
        String response = null;

        // Send it.
        replyChannel.basicPublish("", Common.controllerRequestQueueName, props, message.getBytes("UTF-8"));

        // Wait for reply.
        while (true) {
            QueueingConsumer.Delivery delivery = replyConsumer.nextDelivery(); // timeout?
            // No explicit acknowledgement, this is an auto-acknowledge channel

            if (delivery.getProperties().getCorrelationId().equals(corrId)) {
                response = new String(delivery.getBody(), "UTF-8");
                break;
            }
        }

        // Got response, now convert that to an object.
        return mapper.readValue(response, clazz);
    }

    /**
     * Ping the SDN controller to make sure it's alive
     * @param payload ping payload
     * @return boolean to indicate success
     */
    public boolean SdnPing(String payload) {
        SdnPingRequest req = new SdnPingRequest();
        req.setPayload(payload);

        try {
            SdnPingReply rep = SdnReqRep(req, SdnPingReply.class);

            // Make sure the inbound payload equals the outbound payload
            if (rep.isError()) {
                logger.error("Error: " + rep.getErrorMessage());
                return false;
            }
            if (rep.getPayload().equals(payload)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * Install a meter
     *
     * XXX There should be a better way to specify a switch than its DPID.
     */
    public boolean SdnInstallMeter(byte[] dpid, long meter, long cr, long cbs, long er, long ebs) {
        SdnInstallMeterRequest req = new SdnInstallMeterRequest();
        req.setDpid(dpid);
        req.setMeter(meter);
        req.setCr(cr);
        req.setCbs(cbs);
        req.setEr(er);
        req.setEbs(ebs);

        try {
            SdnInstallMeterReply rep = SdnReqRep(req, SdnInstallMeterReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Delete a meter
     */
    public boolean SdnDeleteMeter(byte[] dpid, long meter) {
        SdnDeleteMeterRequest req = new SdnDeleteMeterRequest();
        req.setDpid(dpid);
        req.setMeter(meter);

        try {
            SdnDeleteMeterReply rep = SdnReqRep(req, SdnDeleteMeterReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public SdnControllerClientFlowHandle SdnInstallForward(byte[] dpid, int priority, BigInteger cookie,
            String inPort, int vlan1, String srcMac, String dstMac, SdnControllerClientL2Forward[] outputs, int pcp,
            int queue, int meter) {
        SdnForwardRequest req = new SdnForwardRequest();
        req.setDpid(dpid);
        req.setPriority(priority);
        req.setC(cookie);
        req.setInPort(inPort);
        req.setVlan1(vlan1);
        req.setSrcMac1(srcMac);
        req.setDstMac1(dstMac);

        req.outputs = new SdnForwardRequest.L2TranslationOutput[outputs.length];
        for (int i = 0; i < outputs.length; i++) {
            req.outputs[i] = new SdnForwardRequest.L2TranslationOutput();
            req.outputs[i].outPort = outputs[i].outPort;
            req.outputs[i].vlan = outputs[i].vlan;
            req.outputs[i].dstMac = outputs[i].dstMac;
        }

        req.setPcp(pcp);
        req.setQueue(queue);
        req.setMeter(meter);

        try {
            SdnForwardReply rep = SdnReqRep(req, SdnForwardReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return null;
            } else {
                // Set dpid, table, flowid
                SdnControllerClientFlowHandleImpl fhi = new SdnControllerClientFlowHandleImpl();
                fhi.dpid = rep.getDpid();
                fhi.tableId = rep.getTableId();
                fhi.flowId = rep.getFlowId();
                return fhi;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public SdnControllerClientFlowHandle SdnInstallForward1(byte[] dpid, int priority, BigInteger cookie,
            String inPort, int vlan1, String srcMac1, String dstMac1, String outPort, int vlan2, String dstMac2,
            int pcp, int queue, int meter) {

        // Special case for Layer 2 forwarding to one output port only.
        SdnControllerClientL2Forward[] output1 = new SdnControllerClientL2Forward[1];

        output1[0] = new SdnControllerClientL2Forward();
        output1[0].outPort = outPort;
        output1[0].vlan = vlan2;
        output1[0].dstMac = dstMac2;

        return SdnInstallForward(dpid, priority, cookie, inPort, vlan1, srcMac1, dstMac1, output1, pcp, queue,
                meter);
    }

    public SdnControllerClientFlowHandle SdnInstallForwardToController(byte[] dpid, int priority, BigInteger cookie,
            String inPort, int vlan1, String srcMac, String dstMac) {
        SdnForwardToControllerRequest req = new SdnForwardToControllerRequest();
        req.setDpid(dpid);
        req.setPriority(priority);
        req.setC(cookie);
        req.setInPort(inPort);
        req.setVlan1(vlan1);
        req.setSrcMac1(srcMac);
        req.setDstMac1(dstMac);

        try {
            SdnForwardReply rep = SdnReqRep(req, SdnForwardReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return null;
            } else {
                // Set dpid, table, flowid
                SdnControllerClientFlowHandleImpl fhi = new SdnControllerClientFlowHandleImpl();
                fhi.dpid = rep.getDpid();
                fhi.tableId = rep.getTableId();
                fhi.flowId = rep.getFlowId();
                return fhi;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public boolean SdnDeleteForward(SdnControllerClientFlowHandle fh) {
        SdnControllerClientFlowHandleImpl fhi = (SdnControllerClientFlowHandleImpl) fh;

        SdnDeleteForwardRequest req = new SdnDeleteForwardRequest();
        req.setDpid(fhi.dpid);
        req.setTableId(fhi.tableId);
        req.setFlowId(fhi.flowId);

        try {
            SdnDeleteForwardReply rep = SdnReqRep(req, SdnDeleteForwardReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;

    }

    /**
     * Send a PACKET_OUT
     */
    public boolean SdnTransmitPacket(byte[] dpid, String outPort, byte[] payload) {
        SdnTransmitPacketRequest req = new SdnTransmitPacketRequest();
        req.setDpid(dpid);
        req.setOutPort(outPort);
        req.setPayload(payload);

        try {
            SdnTransmitPacketReply rep = SdnReqRep(req, SdnTransmitPacketReply.class);

            if (rep.isError()) {
                logger.error(rep.getErrorMessage());
                return false;
            } else {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;

    }

    /**
     * Listen for SdnReceivePacket messages, corresponding to PACKET_IN messages
     * received at the controller.
     */
    public void run() {
        try {
            packetInChannel = connection.createChannel();

            // Create an emphemeral queue and bind it to the exchange so we can get
            // notifications.
            String queueName = packetInChannel.queueDeclare().getQueue();
            packetInChannel.queueBind(queueName, Common.notificationExchangeName, "");
            packetInChannel.basicQos(1);
            // Set up consumer to read from this queue on this channel
            packetInConsumer = new QueueingConsumer(packetInChannel);
            packetInChannel.basicConsume(queueName, false, packetInConsumer);
        } catch (Exception e) {
            e.printStackTrace();
        }

        while (true) {
            try {
                // Grab the next PACKET_IN
                QueueingConsumer.Delivery delivery = packetInConsumer.nextDelivery();

                // Get the properties for the request message, set up the properties
                // for the reply message.
                BasicProperties props = delivery.getProperties();
                BasicProperties replyProps = new BasicProperties.Builder().correlationId(props.getCorrelationId())
                        .build();

                try {
                    // Parse the body.  Get the string containing the JSON data.
                    String message = new String(delivery.getBody(), "UTF-8");

                    // Figure out the message type as a string so we know how to parse it.
                    SdnReply rep = mapper.readValue(message, SdnReply.class);

                    if (rep.getReplyType().equals(SdnReceivePacketReply.TYPE)) {
                        // Do PACKET_IN processing here.  Log, and invoke callback if it's
                        // been configured.
                        logger.debug("Got PACKET_IN");
                        SdnReceivePacketReply packetIn = mapper.readValue(message, SdnReceivePacketReply.class);
                        if (callback != null) {
                            callback.packetInCallback(packetIn.getDpid(), packetIn.getInPort(),
                                    packetIn.getPayload());
                        }
                    } else {
                        // Unknown message.
                        logger.error("Unknown message when PACKET_IN expected");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    // ACK the old message
                    packetInChannel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
                }

            } catch (Exception e) {
                e.printStackTrace();
                // We can get here if there was a problem reading a message from the AMPQ service.
                // Sleep for a second to avoid us busy-waiting in this loop.
                try {
                    Thread.sleep(1000);
                } catch (Exception e2) {
                }
            }
        }
    }

}