Initial opendaylight infrastructure commit!!
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / core / NodeConnector.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
5  * This program and the accompanying materials are made available under the
6  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7  * and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 /**
11  * @file   NodeConnector.java
12  *
13  * @brief  Describe a generic network element attachment points,
14  * attached to one Node, the NodeConnector is formed by the pair
15  * (NodeConnectorType, NodeConnectorID) because each SDN technlogy can
16  * identify an attachment point on the Node in different way.
17  *
18  */
19 package org.opendaylight.controller.sal.core;
20
21 import org.apache.commons.lang3.tuple.ImmutablePair;
22 import java.util.concurrent.ConcurrentHashMap;
23 import java.io.Serializable;
24 import java.util.HashMap;
25 import java.util.Map;
26
27 import org.apache.commons.lang3.builder.EqualsBuilder;
28 import org.apache.commons.lang3.builder.HashCodeBuilder;
29
30 import javax.xml.bind.annotation.XmlRootElement;
31 import javax.xml.bind.annotation.XmlAccessType;
32 import javax.xml.bind.annotation.XmlAccessorType;
33 import javax.xml.bind.annotation.XmlElement;
34 import javax.xml.bind.annotation.XmlAttribute;
35
36 /**
37  * Describe a generic network element attachment points,
38  * attached to one Node, the NodeConnector is formed by the pair
39  * (NodeConnectorType, NodeConnectorID) because each SDN technology can
40  * identify an attachment point on the Node in different way.
41  *
42  */
43 @XmlRootElement
44 @XmlAccessorType(XmlAccessType.NONE)
45 public class NodeConnector implements Serializable {
46     private static final long serialVersionUID = 1L;
47     public static final Short SPECIALNODECONNECTORID = (short) 0;
48
49     /**
50      * Enumerate the different types of NodeConnectors supported by the class
51      *
52      */
53     public static class NodeConnectorIDType {
54         private static final
55         ConcurrentHashMap<String, ImmutablePair<Class, String>> compatibleType =
56             new ConcurrentHashMap<String, ImmutablePair<Class, String>>();
57         /**
58          * Represent a special port pointing toward the controller,
59          * this is to send data packets toward the controller from
60          * data plane.
61          */
62         public static String CONTROLLER = "CTRL";
63         /**
64          * Special port describing ALL the ports in the system,
65          * should be used for flooding like mechanism but better
66          * to be carefull with it
67          */
68         public static String ALL = "ALL";
69         /**
70          * Describe the local networking stack of the node
71          * on which the packet is destined. Yet another special port
72          */
73         public static String SWSTACK = "SW";
74         /**
75          * Describe a special destination that invoke the
76          * traditional HW forwarding on platforms that has this
77          * provision.
78          */
79         public static String HWPATH = "HW";
80         public static String OPENFLOW = "OF";
81         public static String PCEP = "PE";
82         public static String ONEPK = "PK";
83         public static String OPENFLOW2PCEP = "O2E";
84         public static String PCEP2OPENFLOW = "E2O";
85         public static String OPENFLOW2ONEPK = "O2K";
86         public static String ONEPK2OPENFLOW = "K2O";
87         public static String PCEP2ONEPK = "E2K";
88         public static String ONEPK2PCEP = "K2E";
89         public static String PRODUCTION = "PR";
90
91         // Initialize the map with some well known, even though all of
92         // them could be siting outside of here, but it's convenient
93         // for Unit Testing coverage
94         static {
95             compatibleType.put(CONTROLLER,
96                                new ImmutablePair(Short.class, null));
97             compatibleType.put(ALL,
98                                new ImmutablePair(Short.class, null));
99             compatibleType.put(SWSTACK,
100                                new ImmutablePair(Short.class, null));
101             compatibleType.put(HWPATH,
102                                new ImmutablePair(Short.class, null));
103             compatibleType.put(OPENFLOW,
104                                new ImmutablePair(Short.class,
105                                                  Node.NodeIDType.OPENFLOW));
106             compatibleType.put(PCEP,
107                                new ImmutablePair(Integer.class,
108                                                  Node.NodeIDType.PCEP));
109             compatibleType.put(ONEPK,
110                                new ImmutablePair(String.class,
111                                                  Node.NodeIDType.ONEPK));
112             compatibleType.put(OPENFLOW2PCEP,
113                                new ImmutablePair(Short.class,
114                                                  Node.NodeIDType.OPENFLOW));
115             compatibleType.put(OPENFLOW2ONEPK,
116                                new ImmutablePair(Short.class,
117                                                  Node.NodeIDType.OPENFLOW));
118             compatibleType.put(PCEP2OPENFLOW,
119                                new ImmutablePair(Integer.class,
120                                                  Node.NodeIDType.PCEP));
121             compatibleType.put(PCEP2ONEPK,
122                                new ImmutablePair(Integer.class,
123                                                  Node.NodeIDType.PCEP));
124             compatibleType.put(ONEPK2OPENFLOW,
125                                new ImmutablePair(String.class,
126                                                  Node.NodeIDType.ONEPK));
127             compatibleType.put(ONEPK2PCEP,
128                                new ImmutablePair(String.class,
129                                                  Node.NodeIDType.ONEPK));
130             compatibleType.put(PRODUCTION,
131                                new ImmutablePair(String.class,
132                                                  Node.NodeIDType.PRODUCTION));
133         }
134
135         /**
136          * Return the type of the class expected for the
137          * NodeConnectorID, it's used for validity check in the constructor
138          *
139          * @param type, the type of the NodeConnector for which we
140          * want to retrieve the compatible class to be used as ID.
141          *
142          * @return The Class which is supposed to instantiate the ID
143          * for the NodeConnectorID
144          */
145         public static Class<?> getClassType(String type) {
146             if (compatibleType.get(type) == null) {
147                 return null;
148             }
149             return compatibleType.get(type).getLeft();
150         }
151
152         /**
153          * Return the NodeIDType compatible with this NodeConnector,
154          * in fact you cannot attach for example a PCEP NodeConnector
155          * to an OpenFlow Node.
156          *
157          * @param type, the type of the NodeConnector for which we
158          * want to retrieve the compatible class to be used as ID.
159          *
160          * @return The ID of the compatible Node
161          */
162         public static String getCompatibleNode(String type) {
163             if (compatibleType.get(type) == null) {
164                 return null;
165             }
166             return compatibleType.get(type).getRight();
167         }
168
169         /**
170          * Register a new ID for which Node can be created
171          *
172          * @param type, the new type being registered
173          * @param compatibleID, the type of class to be accepted as ID
174          * @param compatibleNode, the type of Node with which this
175          * NodeConnector is compatible
176          *
177          * @return true if registered, false otherwise
178          */
179         public static boolean registerIDType(String type,
180                                              Class compatibleID,
181                                              String compatibleNode) {
182             if (compatibleType.get(type) != null) {
183                 return false;
184             }  else {
185                 compatibleType.put(type, new ImmutablePair(compatibleID,
186                                                            compatibleNode));
187                 return true;
188             }
189         }
190
191         /**
192          * UNRegister a new ID for which Node can be created
193          *
194          * @param type, the type being UN-registered
195          *
196          */
197         public static void unRegisterIDType(String type) {
198             compatibleType.remove(type);
199         }
200     }
201
202     // Elements that constitute the NodeConnector
203     private Object nodeConnectorID;
204     private String nodeConnectorType;
205     @XmlElement(name = "node")
206     private Node nodeConnectorNode;
207
208     // Helper field for JAXB
209     private String nodeConnectorIDString;
210
211     /**
212      * Private constructor used for JAXB mapping
213      */
214     private NodeConnector() {
215         this.nodeConnectorIDString = null;
216         this.nodeConnectorID = null;
217         this.nodeConnectorType = null;
218         this.nodeConnectorNode = null;
219     }
220
221     /**
222      * Create a NodeConnector from the component element. The
223      * constructor make sure the NodeConnector type is congruent with
224      * the Node used and also the NodeConnector ID is of type expected
225      *
226      * @param nodeConnectorType Type of the NodeConnector
227      * @param id ID portion of the NodeConnector
228      * @param node Node to which the NodeConnector is attached too
229      *
230      */
231     public NodeConnector(String nodeConnectorType, Object id,
232             Node node) throws ConstructionException {
233         // In case of compatible type being null then assume that this
234         // port can be attached on any node.
235         String compatibleNode =
236             NodeConnectorIDType.getCompatibleNode(nodeConnectorType);
237         if (NodeConnectorIDType.getClassType(nodeConnectorType) != null &&
238             NodeConnectorIDType.getClassType(nodeConnectorType).isInstance(id) &&
239             (compatibleNode == null ||
240              node.getType().equals(compatibleNode))) {
241             this.nodeConnectorType = nodeConnectorType;
242             this.nodeConnectorID = id;
243             this.nodeConnectorNode = node;
244         } else {
245             throw new ConstructionException("Type of incoming object:"
246                     + id.getClass() + " not compatible with expected type:"
247                     + NodeConnectorIDType.getClassType(nodeConnectorType)
248                     + " or Node type incompatible:" + node.getType());
249         }
250     }
251
252     /**
253      * Copy constructor for NodeConnector
254      *
255      * @param src NodeConnector to copy from
256      *
257      */
258     public NodeConnector(NodeConnector src) throws ConstructionException {
259         if (src != null) {
260             this.nodeConnectorType = src.getType();
261             // Here we can reference the object because that is
262             // supposed to be an immutable identifier as well like a
263             // UUID/Integer and so on, hence no need to create a copy
264             // of it
265             this.nodeConnectorID = src.getID();
266             this.nodeConnectorNode = new Node(src.getNode());
267         } else {
268             throw
269                 new ConstructionException("Null incoming object to copy from");
270         }
271     }
272
273     /**
274      * getter method for NodeConnector
275      *
276      *
277      * @return the NodeConnectorType of this object
278      */
279     @XmlAttribute(name = "type")
280     public String getType() {
281         return this.nodeConnectorType;
282     }
283
284     /**
285      * fill the current object from the string parameters passed, will
286      * be only used by JAXB
287      *
288      * @param typeStr string representing the type of the Node
289      * @param IDStr String representation of the ID
290      */
291     private void fillmeFromString(String typeStr, String IDStr) {
292         if (typeStr == null) {
293             return;
294         }
295
296         if (IDStr == null) {
297             return;
298         }
299
300         this.nodeConnectorType = typeStr;
301         if (typeStr.equals(NodeConnectorIDType.OPENFLOW) ||
302             typeStr.equals(NodeConnectorIDType.OPENFLOW2ONEPK) ||
303             typeStr.equals(NodeConnectorIDType.OPENFLOW2PCEP)) {
304             try {
305                 Short ID = Short.parseShort(IDStr);
306                 this.nodeConnectorID = ID;
307             } catch (Exception ex) {
308                 return;
309             }
310         } else if (typeStr.equals(NodeConnectorIDType.ONEPK) ||
311                    typeStr.equals(NodeConnectorIDType.ONEPK2OPENFLOW) ||
312                    typeStr.equals(NodeConnectorIDType.ONEPK2PCEP) ||
313                    typeStr.equals(NodeConnectorIDType.PRODUCTION)) {
314             try {
315                 this.nodeConnectorID = IDStr;
316             } catch (Exception ex) {
317                 return;
318             }
319         } else if (typeStr.equals(NodeConnectorIDType.PCEP) ||
320                    typeStr.equals(NodeConnectorIDType.PCEP2ONEPK) ||
321                    typeStr.equals(NodeConnectorIDType.PCEP2OPENFLOW)) {
322             try {
323                 Integer ID = Integer.parseInt(IDStr);
324                 this.nodeConnectorID = ID;
325             } catch (Exception ex) {
326                 return;
327             }
328         } else {
329             // Lookup via OSGi service registry
330         }
331     }
332
333     /** 
334      * Private setter for nodeConnectorType to be called by JAXB not by anyone
335      * else, NodeConnector is immutable
336      * 
337      * @param type of node to be set
338      */
339     private void setType(String type) {
340         this.nodeConnectorType = type;
341         if (this.nodeConnectorIDString != null) {
342             this.fillmeFromString(type, this.nodeConnectorIDString);
343         }
344     }
345
346     /**
347      * getter method for NodeConnector
348      *
349      *
350      * @return the NodeConnector ID of this object
351      */
352     public Object getID() {
353         return this.nodeConnectorID;
354     }
355
356     /**
357      * getter method for NodeConnector ID in string format.
358      *
359      *
360      * @return the NodeConnector ID of this object in String format
361      */
362     @XmlAttribute(name = "id")
363     public String getNodeConnectorIDString() {
364         return this.nodeConnectorID.toString();
365     }
366
367     /** 
368      * private setter to be used by JAXB
369      * 
370      * @param nodeConnectorIDString String representation for NodeConnectorID
371      */
372     private void setNodeConnectorIDString(String IDStr) {
373         this.nodeConnectorIDString = IDStr;
374         if (this.nodeConnectorType != null) {
375             this.fillmeFromString(this.nodeConnectorType, IDStr);
376         }
377     }
378
379     /**
380      * getter method for NodeConnector
381      *
382      *
383      * @return the Node of this object
384      */
385     public Node getNode() {
386         return this.nodeConnectorNode;
387     }
388
389     @Override
390     public int hashCode() {
391         return new HashCodeBuilder(63389, 4951)
392             .append(nodeConnectorType)
393             .append(nodeConnectorID)
394             .append(nodeConnectorNode)
395             .hashCode();
396     }
397
398     @Override
399     public boolean equals(Object obj) {
400         if (obj == null) { return false; }
401         if (obj == this) { return true; }
402         if (obj.getClass() != getClass()) {
403             return false;
404         }
405         NodeConnector rhs = (NodeConnector)obj;
406         return new EqualsBuilder()
407             .append(this.getType(), rhs.getType())
408             .append(this.getID(), rhs.getID())
409             .append(this.getNode(), rhs.getNode())
410             .isEquals();
411     }
412
413     @Override
414     public String toString() {
415         return this.getNodeConnectorIdAsString() + "@" + this.nodeConnectorNode;
416     }
417
418     /**
419      * A String representation of the NodeConnector without
420      * the Node context
421      *
422      * @return A String representation of the NodeConnector without
423      * the Node context
424      */
425     public String getNodeConnectorIdAsString() {
426         if (this.nodeConnectorType
427             .equals(NodeConnectorIDType.CONTROLLER) ||
428             this.nodeConnectorType
429             .equals(NodeConnectorIDType.ALL) ||
430             this.nodeConnectorType
431             .equals(NodeConnectorIDType.SWSTACK) ||
432             this.nodeConnectorType
433             .equals(NodeConnectorIDType.HWPATH)) {
434             return this.nodeConnectorType.toString();
435         } else {
436             return this.nodeConnectorType.toString() + "|"
437                     + this.nodeConnectorID.toString();
438         }
439     }
440
441     /**
442      * return a NodeConnector from a string
443      *
444      * @param str String to be parsed in a NodeConnector
445      *
446      * @return the NodeConnector if parse is succesfull, null otherwise
447      */
448     public static NodeConnector fromString(String str) {
449         if (str == null) {
450             return null;
451         }
452         String parts[] = str.split("\\@");
453         if (parts.length != 2) {
454             return null;
455         }
456         // Now get the Node from the Node portion
457         Node n = Node.fromString(parts[1]);
458         if (n == null) {
459             return null;
460         }
461         return fromStringNoNode(parts[0], n);
462     }
463
464     /**
465      * return a NodeConnector from a string not containing explicitely
466      * the Node portion which has to be supplied as parameter
467      *
468      * @param str String to be parsed in a NodeConnector
469      * @param n Node to which the NodeConnector is attached
470      *
471      * @return the NodeConnector if parse is succesfull, null otherwise
472      */
473     public static NodeConnector fromStringNoNode(String str, Node n) {
474         if (str == null) {
475             return null;
476         }
477         String nodeConnectorParts[] = str.split("\\|");
478         if (nodeConnectorParts.length != 2) {
479             // Try to guess from a String formatted as a short because
480             // for long time openflow has been prime citizen so lets
481             // keep this legacy for now
482             String numStr = str.toUpperCase();
483
484             Short ofPortID = null;
485             // Try as an decimal/hex number
486             try {
487                 ofPortID = Short.decode(numStr);
488             } catch (Exception ex) {
489                 ofPortID = null;
490             }
491
492             // Lets try the special ports we know about
493             if (ofPortID == null) {
494                 try {
495                     if (str.equalsIgnoreCase(NodeConnectorIDType.CONTROLLER
496                             .toString())) {
497                         return new NodeConnector(
498                                 NodeConnectorIDType.CONTROLLER,
499                                 SPECIALNODECONNECTORID, n);
500                     }
501                     if (str.equalsIgnoreCase(NodeConnectorIDType.HWPATH
502                             .toString())) {
503                         return new NodeConnector(NodeConnectorIDType.HWPATH,
504                                 SPECIALNODECONNECTORID, n);
505                     }
506                     if (str.equalsIgnoreCase(NodeConnectorIDType.SWSTACK
507                             .toString())) {
508                         return new NodeConnector(NodeConnectorIDType.SWSTACK,
509                                 SPECIALNODECONNECTORID, n);
510                     }
511                     if (str
512                             .equalsIgnoreCase(NodeConnectorIDType.ALL
513                                     .toString())) {
514                         return new NodeConnector(NodeConnectorIDType.ALL,
515                                 SPECIALNODECONNECTORID, n);
516                     }
517                 } catch (ConstructionException ex) {
518                     return null;
519                 }
520                 return null;
521             }
522
523             // Lets return the cooked up NodeID
524             try {
525                 return new NodeConnector(NodeConnectorIDType.OPENFLOW,
526                         ofPortID, n);
527             } catch (ConstructionException ex) {
528                 return null;
529             }
530         }
531
532         String typeStr = nodeConnectorParts[0];
533         String IDStr = nodeConnectorParts[1];
534         return fromStringNoNode(typeStr, IDStr, n);
535     }
536
537     /**
538      * return a NodeConnector from a pair (type, ID) in string format
539      * not containing explicitely the Node portion which has to be
540      * supplied as parameter
541      *
542      * @param typeStr type String to be parsed in a NodeConnector
543      * @param IDStr ID String portion to be parsed in a NodeConnector
544      * @param n Node to which the NodeConnector is attached
545      *
546      * @return the NodeConnector if parse is succesfull, null otherwise
547      */
548     public static NodeConnector fromStringNoNode(String typeStr, String IDStr,
549                                                  Node n) {
550         if (typeStr == null) {
551             return null;
552         }
553         if (IDStr == null) {
554             return null;
555         }
556
557         if (typeStr.equals(NodeConnectorIDType.OPENFLOW) ||
558             typeStr.equals(NodeConnectorIDType.OPENFLOW2ONEPK) ||
559             typeStr.equals(NodeConnectorIDType.OPENFLOW2PCEP)) {
560             try {
561                 Short ID = Short.parseShort(IDStr);
562                 return new NodeConnector(typeStr, ID, n);
563             } catch (Exception ex) {
564                 return null;
565             }
566         } else if (typeStr.equals(NodeConnectorIDType.ONEPK) ||
567                    typeStr.equals(NodeConnectorIDType.ONEPK2OPENFLOW) ||
568                    typeStr.equals(NodeConnectorIDType.ONEPK2PCEP) ||
569                    typeStr.equals(NodeConnectorIDType.PRODUCTION)) {
570             try {
571                 return new NodeConnector(typeStr, IDStr, n);
572             } catch (Exception ex) {
573                 return null;
574             }
575         } else if (typeStr.equals(NodeConnectorIDType.PCEP) ||
576                    typeStr.equals(NodeConnectorIDType.PCEP2ONEPK) ||
577                    typeStr.equals(NodeConnectorIDType.PCEP2OPENFLOW)) {
578             try {
579                 Integer ID = Integer.parseInt(IDStr);
580                 return new NodeConnector(typeStr, ID, n);
581             } catch (Exception ex) {
582                 return null;
583             }
584         } else {
585             // Lookup via OSGi service registry
586         }
587         return null;
588     }
589 }