Add test for generated code checking list of dependencies.
[controller.git] / opendaylight / containermanager / api / src / main / java / org / opendaylight / controller / containermanager / ContainerFlowConfig.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 package org.opendaylight.controller.containermanager;
11
12 import java.io.Serializable;
13 import java.net.Inet4Address;
14 import java.net.Inet6Address;
15 import java.net.InetAddress;
16 import java.net.UnknownHostException;
17 import java.util.ArrayList;
18 import java.util.List;
19
20 import javax.xml.bind.annotation.XmlAccessType;
21 import javax.xml.bind.annotation.XmlAccessorType;
22 import javax.xml.bind.annotation.XmlElement;
23 import javax.xml.bind.annotation.XmlRootElement;
24
25 import org.opendaylight.controller.sal.match.Match;
26 import org.opendaylight.controller.sal.match.MatchType;
27 import org.opendaylight.controller.sal.packet.BitBufferHelper;
28 import org.opendaylight.controller.sal.utils.IPProtocols;
29 import org.opendaylight.controller.sal.utils.NetUtils;
30 import org.opendaylight.controller.sal.utils.Status;
31 import org.opendaylight.controller.sal.utils.StatusCode;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * Flow Specification Java Object for Container Manager
37  * Represents a container flow configuration information for Container Manager.
38  *
39  * Objects of this class are serialized to and de-serialized from binary files through
40  * java serialization API when saving to/reading from Container manager startup
41  * configuration file.
42  */
43 @XmlRootElement (name = "flow-spec-config")
44 @XmlAccessorType(XmlAccessType.NONE)
45 public class ContainerFlowConfig implements Serializable {
46     private static Logger log = LoggerFactory.getLogger(ContainerFlowConfig.class);
47
48     /** The Constant serialVersionUID. */
49     private static final long serialVersionUID = 1L;
50
51     /** The Constant regexName. */
52     private static final String regexName = "^[\\w-+.@]+$";
53
54     /** Flow Spec name. */
55     @XmlElement
56     private String name;
57
58     /** The vlan. */
59     @XmlElement
60     private String dlVlan;
61
62     /** The network Source. */
63     @XmlElement
64     private String nwSrc;
65
66     /** The network Destination */
67     @XmlElement
68     private String nwDst;
69
70     /** The protocol. */
71     @XmlElement
72     private String protocol;
73
74     /** The transport source */
75     @XmlElement
76     private String tpSrc;
77
78     /** The transport destination */
79     @XmlElement
80     private String tpDst;
81
82     /* unidirectional flag
83     do not include this flag in equality check
84     @XmlElement */
85     private static boolean unidirectional = false;
86
87
88     /**
89      * Instantiates a new container flow config.
90      */
91     public ContainerFlowConfig() {
92     }
93
94     /**
95      * Instantiates a new container flow config.
96      *
97      * @param name Flow Spec configuration name
98      * @param container Container Name
99      * @param srcIP Source IP Address
100      * @param dstIP Destination IP Address
101      * @param proto Protocol
102      * @param srcPort Source Layer4 Port
103      * @param dstPort Destination Layer4 Port
104      */
105     public ContainerFlowConfig(String name, String srcIP, String dstIP, String proto, String srcPort,
106             String dstPort) {
107         this.name = name;
108         this.dlVlan = null;
109         this.nwSrc = srcIP;
110         this.nwDst = dstIP;
111         this.protocol = proto;
112         this.tpSrc = srcPort;
113         this.tpDst = dstPort;
114         //this.unidirectional = false;
115     }
116
117     public ContainerFlowConfig(String name, String dlVlan, String srcIP, String dstIP, String proto, String srcPort,
118             String dstPort) {
119         this.name = name;
120         this.dlVlan = dlVlan;
121         this.nwSrc = srcIP;
122         this.nwDst = dstIP;
123         this.protocol = proto;
124         this.tpSrc = srcPort;
125         this.tpDst = dstPort;
126     }
127
128
129     public ContainerFlowConfig(ContainerFlowConfig containerFlowConfig) {
130         this.name = containerFlowConfig.name;
131         this.dlVlan = containerFlowConfig.dlVlan;
132         this.nwSrc = containerFlowConfig.nwSrc;
133         this.nwDst = containerFlowConfig.nwDst;
134         this.protocol = containerFlowConfig.protocol;
135         this.tpSrc = containerFlowConfig.tpSrc;
136         this.tpDst = containerFlowConfig.tpDst;
137         //this.unidirectional = containerFlowConfig.unidirectional;
138     }
139
140     /**
141      * Returns the name of this Flow Specification
142      *
143      * @return the name of the Flow Specification
144      */
145     public String getName() {
146         // mandatory field
147         return name;
148     }
149
150     /**
151      * Returns the vlan id.
152      *
153      * @return the Vlan Id
154      */
155     public String getVlan() {
156         return (dlVlan == null || dlVlan.isEmpty()) ? null : dlVlan;
157     }
158
159     /**
160      * Returns the Source IP Address.
161      *
162      * @return the Source IP Address
163      */
164     public String getSrcIP() {
165         return (nwSrc == null || nwSrc.isEmpty()) ? null : nwSrc;
166     }
167
168     /**
169      * Returns the Destination IP Address.
170      *
171      * @return the Destination IP Address
172      */
173     public String getDstIP() {
174         return (nwDst == null || nwDst.isEmpty()) ? null : nwDst;
175     }
176
177     /**
178      * Returns the protocol.
179      *
180      * @return the protocol
181      */
182     public String getProtocol() {
183         return protocol;
184     }
185
186     /**
187      * Returns Source Layer4 Port.
188      *
189      * @return Source Layer4 Port
190      */
191     public String getSrcPort() {
192         return (tpSrc == null || tpSrc.isEmpty()) ? null : tpSrc;
193     }
194
195     /**
196      * Returns Destination Layer4 Port.
197      *
198      * @return Destination Layer4 Port
199      */
200     public String getDstPort() {
201         return (tpDst == null || tpDst.isEmpty()) ? null : tpDst;
202     }
203
204     /*
205      * @return the unidirectional flag
206      */
207     public boolean isUnidirectional() {
208         return unidirectional;
209     }
210
211     /* (non-Javadoc)
212      * @see java.lang.Object#hashCode()
213      */
214     @Override
215     public int hashCode() {
216         final int prime = 31;
217         int result = 1;
218         result = prime * result
219                 + ((protocol == null) ? 0 : protocol.hashCode());
220         result = prime * result + ((name == null) ? 0 : name.hashCode());
221         result = prime * result + ((dlVlan == null) ? 0 : dlVlan.hashCode());
222         result = prime * result + ((nwDst == null) ? 0 : nwDst.hashCode());
223         result = prime * result + ((tpDst == null) ? 0 : tpDst.hashCode());
224         result = prime * result + ((nwSrc == null) ? 0 : nwSrc.hashCode());
225         result = prime * result + ((tpSrc == null) ? 0 : tpSrc.hashCode());
226         return result;
227     }
228
229     /*
230      * For comparison, consider that container flow can have empty fields
231      */
232     /* (non-Javadoc)
233      * @see java.lang.Object#equals(java.lang.Object)
234      */
235     @Override
236     public boolean equals(Object obj) {
237         /*
238          * Configuration will be stored in collection only if it is valid
239          * Hence we don't check here for uninitialized fields
240          */
241         if (this == obj) {
242             return true;
243         }
244         if (obj == null) {
245             return false;
246         }
247         if (getClass() != obj.getClass()) {
248             return false;
249         }
250         ContainerFlowConfig other = (ContainerFlowConfig) obj;
251         if (matchName(other) && matchDlVlan(other) && matchSrcIP(other)
252                 && matchDstIP(other) && matchProtocol(other)
253                 && matchSrcPort(other) && matchDstPort(other)) {
254             return true;
255         }
256         return false;
257     }
258
259     /**
260      * Equals by Flow Spec name.
261      *
262      * @param name flow spec name for comparison
263      * @return true, if successful
264      */
265     public boolean equalsByName(String name) {
266         return this.name.equals(name);
267     }
268
269     /**
270      * equalsByMatch
271      *
272      * @param that ContainerFlowConfig for comparison
273      * @return true, if any match is equal
274      */
275     public boolean equalsByMatch(ContainerFlowConfig that) {
276         // get both matches
277         // the match is equal if any of the match is equal
278         List<Match> thisMatch = this.getMatches();
279         List<Match> otherMatch = that.getMatches();
280         // both the lists cannot be null
281         for(Match m1 : thisMatch) {
282             for(Match m2 : otherMatch) {
283                   if(m1.equals(m2)) {
284                     return true;
285                 }
286             }
287         }
288         // if you have reached here without a match
289         // being found
290         // return false
291         return false;
292     }
293
294     /**
295      * Matches the name of this flow spec with that of ContainerFlowConfig parameter's flow spec.
296      *
297      * @param o the ContainerFlowConfig parameter
298      * @return true, if successful
299      */
300     private boolean matchName(ContainerFlowConfig flowSpec) {
301         if (name == flowSpec.name) {
302             return true;
303         }
304         if (name == null || flowSpec.name == null) {
305             return false;
306         }
307         return name.equals(flowSpec.name);
308     }
309
310     /**
311      * Match Source IP Address.
312      *
313      * @param flowSpec Flow Specification
314      * @return true, if successful
315      */
316     private boolean matchDlVlan(ContainerFlowConfig flowSpec) {
317         if (dlVlan == flowSpec.dlVlan) {
318             return true;
319         }
320         if (dlVlan == null || flowSpec.dlVlan == null) {
321             return false;
322         }
323         return dlVlan.equals(flowSpec.dlVlan);
324     }
325
326     /**
327      * Match Source IP Address.
328      *
329      * @param flowSpec Flow Specification
330      * @return true, if successful
331      */
332     private boolean matchSrcIP(ContainerFlowConfig flowSpec) {
333         if (nwSrc == flowSpec.nwSrc) {
334             return true;
335         }
336         if (nwSrc == null || flowSpec.nwSrc == null) {
337             return false;
338         }
339         return nwSrc.equals(flowSpec.nwSrc);
340     }
341
342     /**
343      * Match Destination IP Address.
344      *
345      * @param flowSpec Flow Specification
346      * @return true, if successful
347      */
348     private boolean matchDstIP(ContainerFlowConfig flowSpec) {
349         if (nwDst == flowSpec.nwDst) {
350             return true;
351         }
352         if (nwDst == null || flowSpec.nwDst == null) {
353             return false;
354         }
355         return this.nwDst.equals(flowSpec.nwDst);
356     }
357
358     /**
359      * Match protocol.
360      *
361      * @param flowSpec Flow Specification
362      * @return true, if successful
363      */
364     private boolean matchProtocol(ContainerFlowConfig flowSpec) {
365         if (protocol == flowSpec.protocol) {
366             return true;
367         }
368         if (protocol == null || flowSpec.protocol == null) {
369             return false;
370         }
371         return this.protocol.equals(flowSpec.protocol);
372     }
373
374     /**
375      * Match Source Layer4 Port.
376      *
377      * @param flowSpec Flow Specification
378      * @return true, if successful
379      */
380     private boolean matchSrcPort(ContainerFlowConfig flowSpec) {
381         if (tpSrc == flowSpec.tpSrc) {
382             return true;
383         }
384         if (tpSrc == null || flowSpec.tpSrc == null) {
385             return false;
386         }
387         return tpSrc.equals(flowSpec.tpSrc);
388     }
389
390     /**
391      * Match Destination Layer4 Port.
392      *
393      * @param flowSpec Flow Specification
394      * @return true, if successful
395      */
396     private boolean matchDstPort(ContainerFlowConfig flowSpec) {
397         if (tpDst == flowSpec.tpDst) {
398             return true;
399         }
400         if (tpDst == null || flowSpec.tpDst == null) {
401             return false;
402         }
403         return this.tpDst.equals(flowSpec.tpDst);
404     }
405
406     /**
407      * Returns the vlan id number
408      *
409      * @return the vlan id number
410      */
411     public Short getVlanId() {
412         Short vlan = 0;
413         try {
414             vlan = Short.parseShort(dlVlan);
415         } catch (NumberFormatException e) {
416
417         }
418         return vlan;
419     }
420
421     /**
422      * Returns the Source IP Address mask length.
423      *
424      * @return the Source IP Address mask length
425      */
426     public Short getSrcIPMaskLen() {
427         Short maskLen = 0;
428
429         if (nwSrc != null && !nwSrc.isEmpty()) {
430             String[] s = nwSrc.split("/");
431             if (s.length == 2) {
432                 try {
433                     maskLen = Short.valueOf(s[1]);
434                 } catch (Exception e) {
435                     // no mask or bad mask
436                 }
437             } else {
438                 InetAddress ip = this.getSrcIPNum();
439                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
440             }
441         }
442         return maskLen;
443     }
444
445     /**
446      * Returns the Destination IP Address mask length.
447      *
448      * @return the Destination IP Address mask length
449      */
450     public Short getDstIPMaskLen() {
451         Short maskLen = 0;
452         if (nwDst != null && !nwDst.isEmpty()) {
453             String[] s = nwDst.split("/");
454             if (s.length == 2) {
455                 try {
456                     maskLen = Short.valueOf(s[1]);
457                 } catch (Exception e) {
458                     // no mask or bad mask
459                 }
460             } else {
461                 InetAddress ip = this.getDstIPNum();
462                 maskLen = (short) ((ip instanceof Inet4Address) ? 32 : 128);
463             }
464         }
465         return maskLen;
466     }
467
468     /**
469      * Returns the Source IP Address.
470      *
471      * @return the Source IP Address
472      */
473     public InetAddress getSrcIPNum() {
474         InetAddress ip = null;
475         if (nwSrc == null || nwSrc.isEmpty()) {
476             try {
477                 ip = InetAddress.getByAddress(new byte[16]);
478                 return ip;
479             } catch (UnknownHostException e) {
480                 log.error("", e);
481                 return null;
482             }
483         }
484         try {
485             ip = InetAddress.getByName(nwSrc.split("/")[0]);
486         } catch (UnknownHostException e1) {
487             log.error("", e1);
488             return null;
489         }
490         return ip;
491     }
492
493     /**
494      * Returns the Destination IP Address.
495      *
496      * @return the Destination IP Address
497      */
498     public InetAddress getDstIPNum() {
499         InetAddress ip = null;
500         if (nwDst == null || nwDst.isEmpty()) {
501             try {
502                 ip = InetAddress.getByAddress(new byte[16]);
503                 return ip;
504             } catch (UnknownHostException e) {
505                 log.error("",e);
506                 return null;
507             }
508         }
509         try {
510             ip = InetAddress.getByName(nwDst.split("/")[0]);
511         } catch (UnknownHostException e1) {
512             log.error("", e1);
513             return null;
514         }
515         return ip;
516     }
517
518     /**
519      * Returns Source Layer4 Port number.
520      *
521      * @return Source Layer4 Port number
522      */
523     public Short getSrcPortNum() {
524         return (tpSrc == null || tpSrc.isEmpty()) ? Short.valueOf((short) 0)
525                 : Short.valueOf(tpSrc);
526     }
527
528     /**
529      * Returns Destination Layer4 Port number.
530      *
531      * @return Destination Layer4 Port number
532      */
533     public Short getDstPortNum() {
534         return (tpDst == null || tpDst.isEmpty()) ? Short.valueOf((short) 0)
535                 : Short.valueOf(tpDst);
536     }
537
538     /**
539      * Get the IP protocol value
540      *
541      * @return the protocol
542      */
543     public Short getProtoNum() {
544         return protocol == null ? null : IPProtocols.getProtocolNumberShort(protocol);
545     }
546
547     /**
548      * Returns whether this container flow overlap with the passed one This is
549      * true when any two of the resulting matches for the two container flow
550      * configurations intersect.
551      *
552      * @param other
553      *            the other container flow config with which checking the
554      *            overlap
555      * @return true if the two configurations overlap, false otherwise
556      */
557     public boolean overlap(ContainerFlowConfig other) {
558         if (other == null) {
559             return false;
560         }
561         List<Match> myMathes = this.getMatches();
562         List<Match> hisMatches = other.getMatches();
563         for (Match mine : myMathes) {
564             for (Match his : hisMatches) {
565                 if (mine.intersetcs(his)) {
566                     return true;
567                 }
568             }
569         }
570         return false;
571     }
572
573     /**
574      * Checks if this flow specification configuration is valid.
575      *
576      * @return true, if is valid
577      */
578     public Status validate() {
579         if (!hasValidName()) {
580             return new Status(StatusCode.BADREQUEST, "Invalid name");
581         }
582         Status status = validateVlan();
583         if (!status.isSuccess()) {
584             return status;
585         }
586         status = validateIPs();
587         if (!status.isSuccess()) {
588             return status;
589         }
590         if(!hasValidProtocol()) {
591             return new Status(StatusCode.BADREQUEST, "Invalid IP protocol");
592         }
593         if (!hasValidPorts()) {
594             return new Status(StatusCode.BADREQUEST, "Invalid Source or Destination Port");
595         }
596         if (this.getMatches().get(0).getMatches() == 0) {
597             return new Status(StatusCode.BADREQUEST, "Flow Spec is empty");
598         }
599         return new Status(StatusCode.SUCCESS);
600     }
601
602     /**
603      * Checks if this flow specification configuration has a valid name.
604      *
605      * @return true, if successful
606      */
607     private boolean hasValidName() {
608         return (name != null && !name.isEmpty() && name.matches(regexName));
609     }
610
611     /**
612      * Validates the vlan number
613      *
614      * @return the result of the check as Status object
615      */
616     private Status validateVlan() {
617         if (dlVlan != null) {
618             short vlanId = 0;
619             try {
620                 vlanId = Short.parseShort(dlVlan);
621             } catch (NumberFormatException e) {
622                 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
623             }
624             if (vlanId < 0 || vlanId > 0xfff) {
625                 return new Status(StatusCode.BADREQUEST, "Invalid vlan id");
626             }
627         }
628         return new Status(StatusCode.SUCCESS);
629     }
630
631     /**
632      * Validates the network addresses, checks syntax and semantic
633      *
634      * @return the result of the check as Status object, if successful
635      */
636     private Status validateIPs() {
637         if (nwSrc != null) {
638             if (!NetUtils.isIPAddressValid(nwSrc)) {
639                 return new Status(StatusCode.BADREQUEST, "Invalid network source address");
640             }
641             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getSrcIPNum(), this.getSrcIPMaskLen()).getAddress();
642             long prefix = BitBufferHelper.getLong(bytePrefix);
643             if (prefix == 0) {
644                 return new Status(StatusCode.BADREQUEST, "Invalid network source address: subnet zero");
645             }
646         }
647         if (nwDst != null) {
648             if (!NetUtils.isIPAddressValid(nwDst)) {
649                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address");
650             }
651             byte[] bytePrefix = NetUtils.getSubnetPrefix(this.getDstIPNum(), this.getDstIPMaskLen()).getAddress();
652             long prefix = BitBufferHelper.getLong(bytePrefix);
653             if (prefix == 0) {
654                 return new Status(StatusCode.BADREQUEST, "Invalid network destination address: subnet zero");
655             }
656         }
657         return new Status(StatusCode.SUCCESS);
658     }
659
660     /**
661      * Validate the protocol field. Either it can be a enum defined in IPProtocols.java
662      * or a valid IP proto value between 0 and 255, see:
663      * http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
664      * for more details.
665      *
666      * @return true if a valid protocol value
667      */
668     private boolean hasValidProtocol() {
669         IPProtocols p = IPProtocols.fromString(protocol);
670         return p != null;
671     }
672
673     /**
674      *
675      * @param tpPort
676      *               String representing the transport protocol port number
677      * @return true if tpPort contains a decimal value between 0 and 65535
678      */
679     private boolean hasValidPort(String tpPort) {
680         try {
681             int port = Integer.decode(tpPort);
682             return ((port >= 0) && (port <= 0xffff));
683         } catch (NumberFormatException e) {
684             return false;
685         }
686     }
687
688     /**
689      * Validate the transport protocol source and destination ports as
690      * entered by users.
691      *
692      * @return true if ports are defined and are in valid range
693      */
694     private boolean hasValidPorts() {
695         if (tpSrc !=null && !tpSrc.isEmpty()) {
696             if (!hasValidPort(tpSrc)) {
697                 return false;
698             }
699         }
700
701         if (tpDst !=null && !tpDst.isEmpty()) {
702             return hasValidPort(tpDst);
703         }
704         return true;
705     }
706
707     /**
708      * Returns the matches.
709      * If unidirectional flag is set, there will be only one match in the list
710      * If unidirectional flag is unset there will be two matches in the list,
711      * only if the specified flow has an intrinsic direction.
712      * For Ex. if the cFlow only has the protocol field configured, no matter
713      * if unidirectional flag is set or not, only one match will be returned
714      * The client just has to iterate over the returned list
715      * @return the matches
716      */
717     public List<Match> getMatches() {
718         List<Match> matches = new ArrayList<Match>();
719         Match match = new Match();
720
721         if (this.dlVlan != null && !this.dlVlan.isEmpty()) {
722             match.setField(MatchType.DL_VLAN, this.getVlanId());
723         }
724         if (this.nwSrc != null && !this.nwSrc.trim().isEmpty()) {
725             String parts[] = this.nwSrc.split("/");
726             InetAddress ip = NetUtils.parseInetAddress(parts[0]);
727             InetAddress mask = null;
728             int maskLen = 0;
729             if (parts.length > 1) {
730                 maskLen = Integer.parseInt(parts[1]);
731             } else {
732                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
733             }
734             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
735             match.setField(MatchType.NW_SRC, ip, mask);
736         }
737         if (this.nwDst != null && !this.nwDst.trim().isEmpty()) {
738             String parts[] = this.nwDst.split("/");
739             InetAddress ip = NetUtils.parseInetAddress(parts[0]);
740             InetAddress mask = null;
741             int maskLen = 0;
742             if (parts.length > 1) {
743                 maskLen = Integer.parseInt(parts[1]);
744             } else {
745                 maskLen = (ip instanceof Inet6Address) ? 128 : 32;
746             }
747             mask = NetUtils.getInetNetworkMask(maskLen, ip instanceof Inet6Address);
748             match.setField(MatchType.NW_DST, ip, mask);
749         }
750         if (IPProtocols.fromString(this.protocol) != IPProtocols.ANY) {
751             match.setField(MatchType.NW_PROTO, IPProtocols.getProtocolNumberByte(this.protocol));
752         }
753         if (this.tpSrc != null && !this.tpSrc.trim().isEmpty()) {
754             match.setField(MatchType.TP_SRC, Integer.valueOf(tpSrc).shortValue());
755         }
756         if (this.tpDst != null && !this.tpDst.trim().isEmpty()) {
757             match.setField(MatchType.TP_DST, Integer.valueOf(tpDst).shortValue());
758         }
759
760         matches.add(match);
761         if(!ContainerFlowConfig.unidirectional) {
762             Match reverse = match.reverse();
763             if (!match.equals(reverse)) {
764                 matches.add(reverse);
765             }
766         }
767         return matches;
768     }
769
770     /*
771      * (non-Javadoc)
772      *
773      * @see java.lang.Object#toString()
774      */
775     @Override
776     public String toString() {
777         return "Container Flow={name:" + name + " dlVlan:" + dlVlan + " nwSrc:" + nwSrc + " nwDst:" + nwDst + " " + "protocol:" + protocol
778                 + " tpSrc:" + tpSrc + " tpDst:" + tpDst + "}";
779     }
780 }