Fix comparison between port numbers in match
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / util / MatchComparatorFactory.java
1 /*
2  * Copyright (c) 2013, 2015 IBM Corporation and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.openflowplugin.impl.util;
10
11 import org.opendaylight.openflowplugin.api.openflow.md.util.OpenflowVersion;
12 import org.opendaylight.openflowplugin.openflow.md.util.InventoryDataServiceUtil;
13 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
14 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorId;
15 import java.util.ArrayList;
16 import java.util.Collection;
17
18 /**
19  * Provides comparator for comparing according to various {@link Match} attributes
20  *
21  */
22 public final class MatchComparatorFactory {
23
24     private MatchComparatorFactory() {
25         // NOOP
26     }
27
28     private static final Collection<SimpleComparator<Match>> MATCH_COMPARATORS = new ArrayList<>();
29     static {
30         MATCH_COMPARATORS.add(MatchComparatorFactory.createEthernet());
31         MATCH_COMPARATORS.add(MatchComparatorFactory.createIcmpv4());
32         MATCH_COMPARATORS.add(MatchComparatorFactory.createInPhyPort());
33         MATCH_COMPARATORS.add(MatchComparatorFactory.createInPort());
34         MATCH_COMPARATORS.add(MatchComparatorFactory.createIp());
35         MATCH_COMPARATORS.add(MatchComparatorFactory.createL3());
36         MATCH_COMPARATORS.add(MatchComparatorFactory.createL4());
37         MATCH_COMPARATORS.add(MatchComparatorFactory.createProtocolMatchFields());
38         MATCH_COMPARATORS.add(MatchComparatorFactory.createMetadata());
39         MATCH_COMPARATORS.add(MatchComparatorFactory.createNull());
40         MATCH_COMPARATORS.add(MatchComparatorFactory.createTunnel());
41         MATCH_COMPARATORS.add(MatchComparatorFactory.createVlan());
42     }
43
44     public static SimpleComparator<Match> createNull() {
45         return new SimpleComparator<Match>() {
46             /**
47              * Comparation by whole object
48              */
49             @Override
50             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
51                 return (statsMatch == null) == (storedMatch == null);
52             }
53         };
54     }
55
56     public static SimpleComparator<Match> createVlan() {
57         return new SimpleComparator<Match>() {
58             /**
59              * Comparation by VLAN
60              */
61             @Override
62             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
63                 if (storedMatch == null) {
64                     return false;
65                 }
66                 if (storedMatch.getVlanMatch() == null) {
67                     if (statsMatch.getVlanMatch() != null) {
68                         return false;
69                     }
70                 } else if (!storedMatch.getVlanMatch().equals(statsMatch.getVlanMatch())) {
71                     return false;
72                 }
73                 return true;
74             }
75         };
76     }
77
78     public static SimpleComparator<Match> createTunnel() {
79         return new SimpleComparator<Match>() {
80             /**
81              * Comparation by tunnel
82              */
83             @Override
84             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
85                 if (storedMatch == null) {
86                     return false;
87                 }
88                 if (storedMatch.getTunnel() == null) {
89                     if (statsMatch.getTunnel() != null) {
90                         return false;
91                     }
92                 } else if (!storedMatch.getTunnel().equals(statsMatch.getTunnel())) {
93                     return false;
94                 }
95                 return true;
96             }
97         };
98     }
99
100     public static SimpleComparator<Match> createProtocolMatchFields() {
101         return new SimpleComparator<Match>() {
102             /**
103              * Comparation by protocol fields
104              */
105             @Override
106             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
107                 if (storedMatch == null) {
108                     return false;
109                 }
110                 if (storedMatch.getProtocolMatchFields() == null) {
111                     if (statsMatch.getProtocolMatchFields() != null) {
112                         return false;
113                     }
114                 } else if (!storedMatch.getProtocolMatchFields().equals(statsMatch.getProtocolMatchFields())) {
115                     return false;
116                 }
117                 return true;
118             }
119         };
120     }
121
122     public static SimpleComparator<Match> createMetadata() {
123         return new SimpleComparator<Match>() {
124             /**
125              * Comparation by metadata
126              */
127             @Override
128             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
129                 if (storedMatch == null) {
130                     return false;
131                 }
132                 if (storedMatch.getMetadata() == null) {
133                     if (statsMatch.getMetadata() != null) {
134                         return false;
135                     }
136                 } else if (!storedMatch.getMetadata().equals(statsMatch.getMetadata())) {
137                     return false;
138                 }
139                 return true;
140             }
141         };
142     }
143
144     public static SimpleComparator<Match> createL4() {
145         return new SimpleComparator<Match>() {
146             /**
147              * Comparation by layer4
148              */
149             @Override
150             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
151                 if (storedMatch == null) {
152                     return false;
153                 }
154                 if (storedMatch.getLayer4Match() == null) {
155                     if (statsMatch.getLayer4Match() != null) {
156                         return false;
157                     }
158                 } else if (!storedMatch.getLayer4Match().equals(statsMatch.getLayer4Match())) {
159                     return false;
160                 }
161                 return true;
162             }
163         };
164     }
165
166     public static SimpleComparator<Match> createL3() {
167         return new SimpleComparator<Match>() {
168             /**
169              * Comparation by layer3
170              */
171             @Override
172             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
173                 if (storedMatch == null) {
174                     return false;
175                 }
176                 if (storedMatch.getLayer3Match() == null) {
177                     if (statsMatch.getLayer3Match() != null) {
178                         return false;
179                     }
180                 } else if (!MatchComparatorHelper.layer3MatchEquals(statsMatch.getLayer3Match(), storedMatch.getLayer3Match())) {
181                     return false;
182                 }
183                 return true;
184             }
185         };
186     }
187
188     public static SimpleComparator<Match> createIp() {
189         return new SimpleComparator<Match>() {
190             /**
191              * Comparation by Ip
192              */
193             @Override
194             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
195                 if (storedMatch == null) {
196                     return false;
197                 }
198                 if (storedMatch.getIpMatch() == null) {
199                     if (statsMatch.getIpMatch() != null) {
200                         return false;
201                     }
202                 } else if (!storedMatch.getIpMatch().equals(statsMatch.getIpMatch())) {
203                     return false;
204                 }
205                 return true;
206             }
207         };
208     }
209
210
211     /**
212      * Converts both ports in node connector id format to number format and compare them
213      *
214      * @param version openflow version
215      * @param left first object to compare
216      * @param right second object to compare
217      * @return true if equal
218      */
219     private static boolean arePortNumbersEqual(short version, NodeConnectorId left, NodeConnectorId right) {
220         final OpenflowVersion ofVersion = OpenflowVersion.get(version);
221
222         final Long leftPort = InventoryDataServiceUtil.portNumberfromNodeConnectorId(ofVersion, left);
223         final Long rightPort = InventoryDataServiceUtil.portNumberfromNodeConnectorId(ofVersion, right);
224
225         if (leftPort == null) {
226             if (rightPort != null) {
227                 return false;
228             }
229         } else if (!leftPort.equals(rightPort)) {
230             return false;
231         }
232
233         return true;
234     }
235
236     public static SimpleComparator<Match> createInPort() {
237         return new SimpleComparator<Match>() {
238             /**
239              * Comparation by InPort
240              */
241             @Override
242             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
243                 if (storedMatch == null) {
244                     return false;
245                 }
246                 if (storedMatch.getInPort() == null) {
247                     if (statsMatch.getInPort() != null) {
248                         return false;
249                     }
250                 } else if (!arePortNumbersEqual(version, storedMatch.getInPort(), statsMatch.getInPort())) {
251                     return false;
252                 }
253                 return true;
254             }
255         };
256     }
257
258     public static SimpleComparator<Match> createInPhyPort() {
259         return new SimpleComparator<Match>() {
260             /**
261              * Comparation by InPhyPort
262              */
263             @Override
264             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
265                 if (storedMatch == null) {
266                     return false;
267                 }
268                 if (storedMatch.getInPhyPort() == null) {
269                     if (statsMatch.getInPhyPort() != null) {
270                         return false;
271                     }
272                 } else if (!arePortNumbersEqual(version, storedMatch.getInPhyPort(), statsMatch.getInPhyPort())) {
273                     return false;
274                 }
275                 return true;
276             }
277         };
278     }
279
280     public static SimpleComparator<Match> createEthernet() {
281         return new SimpleComparator<Match>() {
282             /**
283              * Comparation by Ethernet
284              */
285             @Override
286             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
287                 if (storedMatch == null) {
288                     return false;
289                 }
290                 if (storedMatch.getEthernetMatch() == null) {
291                     if (statsMatch.getEthernetMatch() != null) {
292                         return false;
293                     }
294                 } else if (!MatchComparatorHelper.ethernetMatchEquals(statsMatch.getEthernetMatch(), storedMatch.getEthernetMatch())) {
295                     return false;
296                 }
297                 return true;
298             }
299         };
300     }
301
302     public static SimpleComparator<Match> createIcmpv4() {
303         return new SimpleComparator<Match>() {
304             /**
305              * Comparation by Icmpv4
306              */
307             @Override
308             public boolean areObjectsEqual(short version, Match statsMatch, Match storedMatch) {
309                 if (storedMatch == null) {
310                     return false;
311                 }
312                 if (storedMatch.getIcmpv4Match() == null) {
313                     if (statsMatch.getIcmpv4Match() != null) {
314                         return false;
315                     }
316                 } else if (!storedMatch.getIcmpv4Match().equals(statsMatch.getIcmpv4Match())) {
317                     return false;
318                 }
319                 return true;
320             }
321         };
322     }
323
324     public static SimpleComparator<Match> createMatch() {
325         return new SimpleComparator<Match>() {
326             /**
327              * Compares flows by whole match
328              */
329             @Override
330             public boolean areObjectsEqual(short version, final Match statsFlow, final Match storedFlow) {
331                 if (statsFlow == null) {
332                     if (storedFlow != null) {
333                         return false;
334                     }
335                 } else if (!compareMatches(version, statsFlow, storedFlow)) {
336                     return false;
337                 }
338                 return true;
339             }
340         };
341     }
342
343
344     /**
345      * Explicit equals method to compare the 'match' for flows stored in the data-stores and flow fetched from the switch.
346      * Flow installation process has three steps
347      * 1) Store flow in config data store
348      * 2) and send it to plugin for installation
349      * 3) Flow gets installed in switch
350      *
351      * The flow user wants to install and what finally gets installed in switch can be slightly different.
352      * E.g, If user installs flow with src/dst ip=10.0.0.1/24, when it get installed in the switch
353      * src/dst ip will be changes to 10.0.0.0/24 because of netmask of 24. When statistics manager fetch
354      * stats it gets 10.0.0.0/24 rather then 10.0.0.1/24. Custom match takes care of by using masked ip
355      * while comparing two ip addresses.
356      *
357      * Sometimes when user don't provide few values that is required by flow installation request, like
358      * priority,hard timeout, idle timeout, cookies etc, plugin usages default values before sending
359      * request to the switch. So when statistics manager gets flow statistics, it gets the default value.
360      * But the flow stored in config data store don't have those defaults value. I included those checks
361      * in the customer flow/match equal function.
362      *
363      *
364      * @param statsMatch
365      * @param storedMatch
366      * @return
367      */
368     private static boolean compareMatches(final short version, final Match statsMatch, final Match storedMatch) {
369         if (statsMatch == storedMatch) {
370             return true;
371         }
372
373         for (SimpleComparator<Match> matchComp : MATCH_COMPARATORS) {
374             if (!matchComp.areObjectsEqual(version, statsMatch, storedMatch)) {
375                 return false;
376             }
377         }
378         return true;
379     }
380 }