Initial opendaylight infrastructure commit!!
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / core / NodeConnector.java
diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/core/NodeConnector.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/core/NodeConnector.java
new file mode 100644 (file)
index 0000000..bbe952d
--- /dev/null
@@ -0,0 +1,589 @@
+
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+/**
+ * @file   NodeConnector.java
+ *
+ * @brief  Describe a generic network element attachment points,
+ * attached to one Node, the NodeConnector is formed by the pair
+ * (NodeConnectorType, NodeConnectorID) because each SDN technlogy can
+ * identify an attachment point on the Node in different way.
+ *
+ */
+package org.opendaylight.controller.sal.core;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import java.util.concurrent.ConcurrentHashMap;
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlAttribute;
+
+/**
+ * Describe a generic network element attachment points,
+ * attached to one Node, the NodeConnector is formed by the pair
+ * (NodeConnectorType, NodeConnectorID) because each SDN technology can
+ * identify an attachment point on the Node in different way.
+ *
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.NONE)
+public class NodeConnector implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final Short SPECIALNODECONNECTORID = (short) 0;
+
+    /**
+     * Enumerate the different types of NodeConnectors supported by the class
+     *
+     */
+    public static class NodeConnectorIDType {
+        private static final
+        ConcurrentHashMap<String, ImmutablePair<Class, String>> compatibleType =
+            new ConcurrentHashMap<String, ImmutablePair<Class, String>>();
+        /**
+         * Represent a special port pointing toward the controller,
+         * this is to send data packets toward the controller from
+         * data plane.
+         */
+        public static String CONTROLLER = "CTRL";
+        /**
+         * Special port describing ALL the ports in the system,
+         * should be used for flooding like mechanism but better
+         * to be carefull with it
+         */
+        public static String ALL = "ALL";
+        /**
+         * Describe the local networking stack of the node
+         * on which the packet is destined. Yet another special port
+         */
+        public static String SWSTACK = "SW";
+        /**
+         * Describe a special destination that invoke the
+         * traditional HW forwarding on platforms that has this
+         * provision.
+         */
+        public static String HWPATH = "HW";
+        public static String OPENFLOW = "OF";
+        public static String PCEP = "PE";
+        public static String ONEPK = "PK";
+        public static String OPENFLOW2PCEP = "O2E";
+        public static String PCEP2OPENFLOW = "E2O";
+        public static String OPENFLOW2ONEPK = "O2K";
+        public static String ONEPK2OPENFLOW = "K2O";
+        public static String PCEP2ONEPK = "E2K";
+        public static String ONEPK2PCEP = "K2E";
+        public static String PRODUCTION = "PR";
+
+        // Initialize the map with some well known, even though all of
+        // them could be siting outside of here, but it's convenient
+        // for Unit Testing coverage
+        static {
+            compatibleType.put(CONTROLLER,
+                               new ImmutablePair(Short.class, null));
+            compatibleType.put(ALL,
+                               new ImmutablePair(Short.class, null));
+            compatibleType.put(SWSTACK,
+                               new ImmutablePair(Short.class, null));
+            compatibleType.put(HWPATH,
+                               new ImmutablePair(Short.class, null));
+            compatibleType.put(OPENFLOW,
+                               new ImmutablePair(Short.class,
+                                                 Node.NodeIDType.OPENFLOW));
+            compatibleType.put(PCEP,
+                               new ImmutablePair(Integer.class,
+                                                 Node.NodeIDType.PCEP));
+            compatibleType.put(ONEPK,
+                               new ImmutablePair(String.class,
+                                                 Node.NodeIDType.ONEPK));
+            compatibleType.put(OPENFLOW2PCEP,
+                               new ImmutablePair(Short.class,
+                                                 Node.NodeIDType.OPENFLOW));
+            compatibleType.put(OPENFLOW2ONEPK,
+                               new ImmutablePair(Short.class,
+                                                 Node.NodeIDType.OPENFLOW));
+            compatibleType.put(PCEP2OPENFLOW,
+                               new ImmutablePair(Integer.class,
+                                                 Node.NodeIDType.PCEP));
+            compatibleType.put(PCEP2ONEPK,
+                               new ImmutablePair(Integer.class,
+                                                 Node.NodeIDType.PCEP));
+            compatibleType.put(ONEPK2OPENFLOW,
+                               new ImmutablePair(String.class,
+                                                 Node.NodeIDType.ONEPK));
+            compatibleType.put(ONEPK2PCEP,
+                               new ImmutablePair(String.class,
+                                                 Node.NodeIDType.ONEPK));
+            compatibleType.put(PRODUCTION,
+                               new ImmutablePair(String.class,
+                                                 Node.NodeIDType.PRODUCTION));
+        }
+
+        /**
+         * Return the type of the class expected for the
+         * NodeConnectorID, it's used for validity check in the constructor
+         *
+         * @param type, the type of the NodeConnector for which we
+         * want to retrieve the compatible class to be used as ID.
+         *
+         * @return The Class which is supposed to instantiate the ID
+         * for the NodeConnectorID
+         */
+        public static Class<?> getClassType(String type) {
+            if (compatibleType.get(type) == null) {
+                return null;
+            }
+            return compatibleType.get(type).getLeft();
+        }
+
+        /**
+         * Return the NodeIDType compatible with this NodeConnector,
+         * in fact you cannot attach for example a PCEP NodeConnector
+         * to an OpenFlow Node.
+         *
+         * @param type, the type of the NodeConnector for which we
+         * want to retrieve the compatible class to be used as ID.
+         *
+         * @return The ID of the compatible Node
+         */
+        public static String getCompatibleNode(String type) {
+            if (compatibleType.get(type) == null) {
+                return null;
+            }
+            return compatibleType.get(type).getRight();
+        }
+
+        /**
+         * Register a new ID for which Node can be created
+         *
+         * @param type, the new type being registered
+         * @param compatibleID, the type of class to be accepted as ID
+         * @param compatibleNode, the type of Node with which this
+         * NodeConnector is compatible
+         *
+         * @return true if registered, false otherwise
+         */
+        public static boolean registerIDType(String type,
+                                             Class compatibleID,
+                                             String compatibleNode) {
+            if (compatibleType.get(type) != null) {
+                return false;
+            }  else {
+                compatibleType.put(type, new ImmutablePair(compatibleID,
+                                                           compatibleNode));
+                return true;
+            }
+        }
+
+        /**
+         * UNRegister a new ID for which Node can be created
+         *
+         * @param type, the type being UN-registered
+         *
+         */
+        public static void unRegisterIDType(String type) {
+            compatibleType.remove(type);
+        }
+    }
+
+    // Elements that constitute the NodeConnector
+    private Object nodeConnectorID;
+    private String nodeConnectorType;
+    @XmlElement(name = "node")
+    private Node nodeConnectorNode;
+
+    // Helper field for JAXB
+    private String nodeConnectorIDString;
+
+    /**
+     * Private constructor used for JAXB mapping
+     */
+    private NodeConnector() {
+        this.nodeConnectorIDString = null;
+        this.nodeConnectorID = null;
+        this.nodeConnectorType = null;
+        this.nodeConnectorNode = null;
+    }
+
+    /**
+     * Create a NodeConnector from the component element. The
+     * constructor make sure the NodeConnector type is congruent with
+     * the Node used and also the NodeConnector ID is of type expected
+     *
+     * @param nodeConnectorType Type of the NodeConnector
+     * @param id ID portion of the NodeConnector
+     * @param node Node to which the NodeConnector is attached too
+     *
+     */
+    public NodeConnector(String nodeConnectorType, Object id,
+            Node node) throws ConstructionException {
+        // In case of compatible type being null then assume that this
+        // port can be attached on any node.
+        String compatibleNode =
+            NodeConnectorIDType.getCompatibleNode(nodeConnectorType);
+        if (NodeConnectorIDType.getClassType(nodeConnectorType) != null &&
+            NodeConnectorIDType.getClassType(nodeConnectorType).isInstance(id) &&
+            (compatibleNode == null ||
+             node.getType().equals(compatibleNode))) {
+            this.nodeConnectorType = nodeConnectorType;
+            this.nodeConnectorID = id;
+            this.nodeConnectorNode = node;
+        } else {
+            throw new ConstructionException("Type of incoming object:"
+                    + id.getClass() + " not compatible with expected type:"
+                    + NodeConnectorIDType.getClassType(nodeConnectorType)
+                    + " or Node type incompatible:" + node.getType());
+        }
+    }
+
+    /**
+     * Copy constructor for NodeConnector
+     *
+     * @param src NodeConnector to copy from
+     *
+     */
+    public NodeConnector(NodeConnector src) throws ConstructionException {
+        if (src != null) {
+            this.nodeConnectorType = src.getType();
+            // Here we can reference the object because that is
+            // supposed to be an immutable identifier as well like a
+            // UUID/Integer and so on, hence no need to create a copy
+            // of it
+            this.nodeConnectorID = src.getID();
+            this.nodeConnectorNode = new Node(src.getNode());
+        } else {
+            throw
+                new ConstructionException("Null incoming object to copy from");
+        }
+    }
+
+    /**
+     * getter method for NodeConnector
+     *
+     *
+     * @return the NodeConnectorType of this object
+     */
+    @XmlAttribute(name = "type")
+    public String getType() {
+        return this.nodeConnectorType;
+    }
+
+    /**
+     * fill the current object from the string parameters passed, will
+     * be only used by JAXB
+     *
+     * @param typeStr string representing the type of the Node
+     * @param IDStr String representation of the ID
+     */
+    private void fillmeFromString(String typeStr, String IDStr) {
+        if (typeStr == null) {
+            return;
+        }
+
+        if (IDStr == null) {
+            return;
+        }
+
+        this.nodeConnectorType = typeStr;
+        if (typeStr.equals(NodeConnectorIDType.OPENFLOW) ||
+            typeStr.equals(NodeConnectorIDType.OPENFLOW2ONEPK) ||
+            typeStr.equals(NodeConnectorIDType.OPENFLOW2PCEP)) {
+            try {
+                Short ID = Short.parseShort(IDStr);
+                this.nodeConnectorID = ID;
+            } catch (Exception ex) {
+                return;
+            }
+        } else if (typeStr.equals(NodeConnectorIDType.ONEPK) ||
+                   typeStr.equals(NodeConnectorIDType.ONEPK2OPENFLOW) ||
+                   typeStr.equals(NodeConnectorIDType.ONEPK2PCEP) ||
+                   typeStr.equals(NodeConnectorIDType.PRODUCTION)) {
+            try {
+                this.nodeConnectorID = IDStr;
+            } catch (Exception ex) {
+                return;
+            }
+        } else if (typeStr.equals(NodeConnectorIDType.PCEP) ||
+                   typeStr.equals(NodeConnectorIDType.PCEP2ONEPK) ||
+                   typeStr.equals(NodeConnectorIDType.PCEP2OPENFLOW)) {
+            try {
+                Integer ID = Integer.parseInt(IDStr);
+                this.nodeConnectorID = ID;
+            } catch (Exception ex) {
+                return;
+            }
+        } else {
+            // Lookup via OSGi service registry
+        }
+    }
+
+    /** 
+     * Private setter for nodeConnectorType to be called by JAXB not by anyone
+     * else, NodeConnector is immutable
+     * 
+     * @param type of node to be set
+     */
+    private void setType(String type) {
+        this.nodeConnectorType = type;
+        if (this.nodeConnectorIDString != null) {
+            this.fillmeFromString(type, this.nodeConnectorIDString);
+        }
+    }
+
+    /**
+     * getter method for NodeConnector
+     *
+     *
+     * @return the NodeConnector ID of this object
+     */
+    public Object getID() {
+        return this.nodeConnectorID;
+    }
+
+    /**
+     * getter method for NodeConnector ID in string format.
+     *
+     *
+     * @return the NodeConnector ID of this object in String format
+     */
+    @XmlAttribute(name = "id")
+    public String getNodeConnectorIDString() {
+        return this.nodeConnectorID.toString();
+    }
+
+    /** 
+     * private setter to be used by JAXB
+     * 
+     * @param nodeConnectorIDString String representation for NodeConnectorID
+     */
+    private void setNodeConnectorIDString(String IDStr) {
+        this.nodeConnectorIDString = IDStr;
+        if (this.nodeConnectorType != null) {
+            this.fillmeFromString(this.nodeConnectorType, IDStr);
+        }
+    }
+
+    /**
+     * getter method for NodeConnector
+     *
+     *
+     * @return the Node of this object
+     */
+    public Node getNode() {
+        return this.nodeConnectorNode;
+    }
+
+    @Override
+    public int hashCode() {
+        return new HashCodeBuilder(63389, 4951)
+            .append(nodeConnectorType)
+            .append(nodeConnectorID)
+            .append(nodeConnectorNode)
+            .hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) { return false; }
+        if (obj == this) { return true; }
+        if (obj.getClass() != getClass()) {
+            return false;
+        }
+        NodeConnector rhs = (NodeConnector)obj;
+        return new EqualsBuilder()
+            .append(this.getType(), rhs.getType())
+            .append(this.getID(), rhs.getID())
+            .append(this.getNode(), rhs.getNode())
+            .isEquals();
+    }
+
+    @Override
+    public String toString() {
+        return this.getNodeConnectorIdAsString() + "@" + this.nodeConnectorNode;
+    }
+
+    /**
+     * A String representation of the NodeConnector without
+     * the Node context
+     *
+     * @return A String representation of the NodeConnector without
+     * the Node context
+     */
+    public String getNodeConnectorIdAsString() {
+        if (this.nodeConnectorType
+            .equals(NodeConnectorIDType.CONTROLLER) ||
+            this.nodeConnectorType
+            .equals(NodeConnectorIDType.ALL) ||
+            this.nodeConnectorType
+            .equals(NodeConnectorIDType.SWSTACK) ||
+            this.nodeConnectorType
+            .equals(NodeConnectorIDType.HWPATH)) {
+            return this.nodeConnectorType.toString();
+        } else {
+            return this.nodeConnectorType.toString() + "|"
+                    + this.nodeConnectorID.toString();
+        }
+    }
+
+    /**
+     * return a NodeConnector from a string
+     *
+     * @param str String to be parsed in a NodeConnector
+     *
+     * @return the NodeConnector if parse is succesfull, null otherwise
+     */
+    public static NodeConnector fromString(String str) {
+        if (str == null) {
+            return null;
+        }
+        String parts[] = str.split("\\@");
+        if (parts.length != 2) {
+            return null;
+        }
+        // Now get the Node from the Node portion
+        Node n = Node.fromString(parts[1]);
+        if (n == null) {
+            return null;
+        }
+        return fromStringNoNode(parts[0], n);
+    }
+
+    /**
+     * return a NodeConnector from a string not containing explicitely
+     * the Node portion which has to be supplied as parameter
+     *
+     * @param str String to be parsed in a NodeConnector
+     * @param n Node to which the NodeConnector is attached
+     *
+     * @return the NodeConnector if parse is succesfull, null otherwise
+     */
+    public static NodeConnector fromStringNoNode(String str, Node n) {
+        if (str == null) {
+            return null;
+        }
+        String nodeConnectorParts[] = str.split("\\|");
+        if (nodeConnectorParts.length != 2) {
+            // Try to guess from a String formatted as a short because
+            // for long time openflow has been prime citizen so lets
+            // keep this legacy for now
+            String numStr = str.toUpperCase();
+
+            Short ofPortID = null;
+            // Try as an decimal/hex number
+            try {
+                ofPortID = Short.decode(numStr);
+            } catch (Exception ex) {
+                ofPortID = null;
+            }
+
+            // Lets try the special ports we know about
+            if (ofPortID == null) {
+                try {
+                    if (str.equalsIgnoreCase(NodeConnectorIDType.CONTROLLER
+                            .toString())) {
+                        return new NodeConnector(
+                                NodeConnectorIDType.CONTROLLER,
+                                SPECIALNODECONNECTORID, n);
+                    }
+                    if (str.equalsIgnoreCase(NodeConnectorIDType.HWPATH
+                            .toString())) {
+                        return new NodeConnector(NodeConnectorIDType.HWPATH,
+                                SPECIALNODECONNECTORID, n);
+                    }
+                    if (str.equalsIgnoreCase(NodeConnectorIDType.SWSTACK
+                            .toString())) {
+                        return new NodeConnector(NodeConnectorIDType.SWSTACK,
+                                SPECIALNODECONNECTORID, n);
+                    }
+                    if (str
+                            .equalsIgnoreCase(NodeConnectorIDType.ALL
+                                    .toString())) {
+                        return new NodeConnector(NodeConnectorIDType.ALL,
+                                SPECIALNODECONNECTORID, n);
+                    }
+                } catch (ConstructionException ex) {
+                    return null;
+                }
+                return null;
+            }
+
+            // Lets return the cooked up NodeID
+            try {
+                return new NodeConnector(NodeConnectorIDType.OPENFLOW,
+                        ofPortID, n);
+            } catch (ConstructionException ex) {
+                return null;
+            }
+        }
+
+        String typeStr = nodeConnectorParts[0];
+        String IDStr = nodeConnectorParts[1];
+        return fromStringNoNode(typeStr, IDStr, n);
+    }
+
+    /**
+     * return a NodeConnector from a pair (type, ID) in string format
+     * not containing explicitely the Node portion which has to be
+     * supplied as parameter
+     *
+     * @param typeStr type String to be parsed in a NodeConnector
+     * @param IDStr ID String portion to be parsed in a NodeConnector
+     * @param n Node to which the NodeConnector is attached
+     *
+     * @return the NodeConnector if parse is succesfull, null otherwise
+     */
+    public static NodeConnector fromStringNoNode(String typeStr, String IDStr,
+                                                 Node n) {
+        if (typeStr == null) {
+            return null;
+        }
+        if (IDStr == null) {
+            return null;
+        }
+
+        if (typeStr.equals(NodeConnectorIDType.OPENFLOW) ||
+            typeStr.equals(NodeConnectorIDType.OPENFLOW2ONEPK) ||
+            typeStr.equals(NodeConnectorIDType.OPENFLOW2PCEP)) {
+            try {
+                Short ID = Short.parseShort(IDStr);
+                return new NodeConnector(typeStr, ID, n);
+            } catch (Exception ex) {
+                return null;
+            }
+        } else if (typeStr.equals(NodeConnectorIDType.ONEPK) ||
+                   typeStr.equals(NodeConnectorIDType.ONEPK2OPENFLOW) ||
+                   typeStr.equals(NodeConnectorIDType.ONEPK2PCEP) ||
+                   typeStr.equals(NodeConnectorIDType.PRODUCTION)) {
+            try {
+                return new NodeConnector(typeStr, IDStr, n);
+            } catch (Exception ex) {
+                return null;
+            }
+        } else if (typeStr.equals(NodeConnectorIDType.PCEP) ||
+                   typeStr.equals(NodeConnectorIDType.PCEP2ONEPK) ||
+                   typeStr.equals(NodeConnectorIDType.PCEP2OPENFLOW)) {
+            try {
+                Integer ID = Integer.parseInt(IDStr);
+                return new NodeConnector(typeStr, ID, n);
+            } catch (Exception ex) {
+                return null;
+            }
+        } else {
+            // Lookup via OSGi service registry
+        }
+        return null;
+    }
+}