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