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