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