88b426c2ff542ee2903559d7852c110d0bf4dbfd
[vtn.git] /
1 /*
2  * Copyright (c) 2014 NEC Corporation
3  * 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
7  * distribution, and is available at http://www.eclipse.org/legal/epl-v10.html
8  */
9
10 package org.opendaylight.vtn.manager.internal.cluster;
11
12 import java.io.Serializable;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.TreeMap;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import org.opendaylight.vtn.manager.VTNException;
23 import org.opendaylight.vtn.manager.VTenantPath;
24 import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
25
26 import org.opendaylight.vtn.manager.internal.PacketContext;
27 import org.opendaylight.vtn.manager.internal.VTNFlowDatabase;
28 import org.opendaylight.vtn.manager.internal.VTNManagerImpl;
29 import org.opendaylight.vtn.manager.internal.VTNThreadData;
30
31 import org.opendaylight.controller.sal.core.UpdateType;
32 import org.opendaylight.controller.sal.utils.Status;
33 import org.opendaylight.controller.sal.utils.StatusCode;
34
35 /**
36  * A map that keeps flow filters.
37  *
38  * <p>
39  *   Note that this class is not synchronized. Synchronization should be done
40  *   by the virtual node that contains the flow filter.
41  * </p>
42  * <p>
43  *   Although this class is public to other packages, this class does not
44  *   provide any API. Applications other than VTN Manager must not use this
45  *   class.
46  * </p>
47  */
48 public abstract class FlowFilterMap implements Serializable, Cloneable {
49     /**
50      * Version number for serialization.
51      */
52     private static final long serialVersionUID = -8039322793324993017L;
53
54     /**
55      * A pseudo VLAN ID which represents the VLAN ID is not specified.
56      */
57     public static final short  VLAN_UNSPEC = -1;
58
59     /**
60      * Logger instance.
61      */
62     private static final Logger  LOG =
63         LoggerFactory.getLogger(FlowFilterMap.class);
64
65     /**
66      * Pseudo flow filter index that indicates all flow filters in this map.
67      */
68     private static final int  INDEX_ALL = 0;
69
70     /**
71      * A string that indicates this flow filter is applied to outgoing flow.
72      */
73     private static final String  DIRECTION_IN = "IN";
74
75     /**
76      * A string that indicates this flow filter is applied to outgoing flow.
77      */
78     private static final String  DIRECTION_OUT = "OUT";
79
80     /**
81      * Pairs of filter index and flow filter implementation.
82      */
83     private Map<Integer, FlowFilterImpl>  flowFilters =
84         new TreeMap<Integer, FlowFilterImpl>();
85
86     /**
87      * The virtual node that contains this flow filter map.
88      *
89      * <p>
90      *   This field never affects object identify.
91      * </p>
92      */
93     private transient FlowFilterNode  parent;
94
95     /**
96      * Create a new flow filter map for incoming packets.
97      *
98      * @param fnode  Virtual node that contains this flow filter.
99      * @return  A {@link FlowFilterMap} instance.
100      */
101     public static FlowFilterMap createIncoming(FlowFilterNode fnode) {
102         return new Incoming(fnode);
103     }
104
105     /**
106      * Create a new flow filter map for outgoing packets.
107      *
108      * @param fnode  Virtual node that contains this flow filter.
109      * @return  A {@link FlowFilterMap} instance.
110      */
111     public static FlowFilterMap createOutgoing(FlowFilterNode fnode) {
112         return new Outgoing(fnode);
113     }
114
115     /**
116      * Invoked when a flow filter event is received from another controller.
117      *
118      * @param mgr  VTN Manager service.
119      * @param ev   A {@link FlowFilterEvent} instance.
120      */
121     public static void eventReceived(VTNManagerImpl mgr, FlowFilterEvent ev) {
122         // Save VTN configuration.
123         VTenantPath path = ev.getPath();
124         mgr.saveTenantConfig(path.getTenantName());
125
126         String container = mgr.getContainerName();
127         boolean out = ev.isOutput();
128         String direction = getFlowDirectionName(out);
129         int index = ev.getIndex();
130         if (index == INDEX_ALL) {
131             assert ev.getUpdateType() == UpdateType.REMOVED;
132             logCleared(container, path, direction);
133         } else {
134             UpdateType type = ev.getUpdateType();
135             logUpdated(container, path, direction, index, type, null);
136         }
137     }
138
139     /**
140      * Return a string which describes the flow direction to be evaluated.
141      *
142      * @param out  A boolean value which specifies the flow direction.
143      * @return  A string which represents the flow direction.
144      */
145     public static String getFlowDirectionName(boolean out) {
146         return (out) ? DIRECTION_OUT : DIRECTION_IN;
147     }
148
149     /**
150      * Record an informational log that indicates a flow filter was updated.
151      *
152      * @param container  The name of the container.
153      * @param path       Pat to the virtual node.
154      * @param direction  A string which represents the flow direction.
155      * @param index      Index of the flow filter.
156      * @param type       Update type.
157      * @param fi         A {@link FlowFilterImpl} for trace logging.
158      */
159     private static void logUpdated(String container, VTenantPath path,
160                                    String direction, int index,
161                                    UpdateType type, FlowFilterImpl fi) {
162         if (fi != null && LOG.isTraceEnabled()) {
163             LOG.trace("{}:{}:{}.{}: Flow filter was {}: {}",
164                       container, path, direction, index, type.getName(),
165                       fi.getFlowFilter());
166         } else {
167             LOG.info("{}:{}:{}.{}: Flow filter was {}.",
168                      container, path, direction, index, type.getName());
169         }
170     }
171
172     /**
173      * Record an informational log that indicates all flow filters was updated.
174      *
175      * @param container  The name of the container.
176      * @param path       Pat to the virtual node.
177      * @param direction  A string which represents the flow direction.
178      */
179     private static void logCleared(String container, VTenantPath path,
180                                    String direction) {
181         LOG.info("{}:{}:{}: All flow filters were removed.",
182                  container, path, direction);
183     }
184
185     /**
186      * Construct a new instance.
187      *
188      * @param fnode  Virtual node that contains this flow filter.
189      */
190     private FlowFilterMap(FlowFilterNode fnode) {
191         setParent(fnode);
192     }
193
194     /**
195      * Return a list of flow filters configured in this instance.
196      *
197      * @return  A list of {@link FlowFilter} instances.
198      */
199     public final List<FlowFilter> getAll() {
200         List<FlowFilter> list = new ArrayList<FlowFilter>(flowFilters.size());
201         for (FlowFilterImpl fi: flowFilters.values()) {
202             list.add(fi.getFlowFilter());
203         }
204
205         return list;
206     }
207
208     /**
209      * Return the flow filter associated with the specified index number
210      * in this instance.
211      *
212      * @param index  The index number of the flow filter.
213      * @return  A {@link FlowFilter} instance if found.
214      *          {@code null} if not found.
215      */
216     public final FlowFilter get(int index) {
217         FlowFilterImpl fi = flowFilters.get(index);
218         return (fi == null) ? null : fi.getFlowFilter();
219     }
220
221     /**
222      * Create or modify the flow filter specified by the index number.
223      *
224      * @param mgr     VTN Manager service.
225      * @param index   The index number of the flow filter.
226      * @param filter  A {@link FlowFilter} instance which specifies the
227      *                configuration of the flow filter.
228      * @return  A {@link UpdateType} object which represents the result of the
229      *          operation is returned. {@code null} is returned if no change
230      *          was made.
231      * @throws VTNException  An error occurred.
232      */
233     public final UpdateType set(VTNManagerImpl mgr, int index,
234                                 FlowFilter filter) throws VTNException {
235         FlowFilterImpl fi = FlowFilterImpl.create(parent, index, filter);
236         Integer key = Integer.valueOf(index);
237
238         UpdateType result;
239         FlowFilterImpl old = flowFilters.put(key, fi);
240         if (old == null) {
241             result = UpdateType.ADDED;
242         } else if (old.equals(fi)) {
243             // No change was made to the flow filter.
244             return null;
245         } else {
246             result = UpdateType.CHANGED;
247         }
248
249         // REVISIT: Select flow entries affected by the change.
250         VTenantPath path = parent.getPath();
251         String tname = path.getTenantName();
252         VTNFlowDatabase fdb = mgr.getTenantFlowDB(tname);
253         VTNThreadData.removeFlows(mgr, fdb);
254
255         logUpdated(parent.getContainerName(), path, getFlowDirectionName(),
256                    index, result, fi);
257         FlowFilterEvent.raise(mgr, path, isOutput(), index, result);
258         return result;
259     }
260
261     /**
262      * Remove the flow filter specified by the index number.
263      *
264      * @param mgr    VTN Manager service.
265      * @param index  The index number of the flow filter.
266      * @return  A {@link Status} instance which indicates the result of the
267      *          operation. {@code null} is returned if the specified
268      *          flow filter does not exist.
269      */
270     public final Status remove(VTNManagerImpl mgr, int index) {
271         Integer key = Integer.valueOf(index);
272         FlowFilterImpl fi = flowFilters.remove(key);
273         if (fi == null) {
274             return null;
275         }
276
277         // REVISIT: Select flow entries affected by the change.
278         VTenantPath path = parent.getPath();
279         String tname = path.getTenantName();
280         VTNFlowDatabase fdb = mgr.getTenantFlowDB(tname);
281         VTNThreadData.removeFlows(mgr, fdb);
282
283         UpdateType type = UpdateType.REMOVED;
284         logUpdated(parent.getContainerName(), path, getFlowDirectionName(),
285                    index, type, fi);
286         FlowFilterEvent.raise(mgr, path, isOutput(), index, type);
287         return new Status(StatusCode.SUCCESS, null);
288     }
289
290     /**
291      * Remove all flow filters in this instance.
292      *
293      * @param mgr    VTN Manager service.
294      * @return  A {@link Status} instance which indicates the result of the
295      *          operation. {@code null} is returned if this instance contains
296      *          no flow filter.
297      */
298     public final Status clear(VTNManagerImpl mgr) {
299         if (flowFilters.isEmpty()) {
300             return null;
301         }
302
303         HashMap<Integer, FlowFilterImpl> removed = (LOG.isTraceEnabled())
304             ? new HashMap<Integer, FlowFilterImpl>(flowFilters)
305             : null;
306         flowFilters.clear();
307
308         // REVISIT: Select flow entries affected by the change.
309         VTenantPath path = parent.getPath();
310         String tname = path.getTenantName();
311         VTNFlowDatabase fdb = mgr.getTenantFlowDB(tname);
312         VTNThreadData.removeFlows(mgr, fdb);
313
314         String container = parent.getContainerName();
315         UpdateType type = UpdateType.REMOVED;
316         String direction = getFlowDirectionName();
317         if (removed == null) {
318             logCleared(container, path, direction);
319         } else {
320             for (FlowFilterImpl fi: removed.values()) {
321                 logUpdated(container, path, direction, fi.getIndex(), type,
322                            fi);
323             }
324         }
325
326         FlowFilterEvent.raise(mgr, path, isOutput(), INDEX_ALL, type);
327         return new Status(StatusCode.SUCCESS, null);
328     }
329
330     /**
331      * Determine whether this flow filter map is empty or not.
332      *
333      * @return  {@code true} only if this flow filter map is empty.
334      */
335     public final boolean isEmpty() {
336         return flowFilters.isEmpty();
337     }
338
339     /**
340      * Evaluate flow filters configured in this instance.
341      *
342      * <p>
343      *   This method must be called with holding the lock for the parent node.
344      * </p>
345      *
346      * @param mgr   VTN Manager service.
347      * @param pctx  A packet context which contains the packet.
348      * @param vid   A VLAN ID to be used for packet matching.
349      *              A VLAN ID configured in the given packet is used if a
350      *              negative value is specified.
351      * @return  A {@link PacketContext} to be used for succeeding packet
352      *          processing.
353      * @throws DropFlowException
354      *    The given packet was discarded by a flow filter configured in
355      *    this instance.
356      * @throws RedirectFlowException
357      *    The given packet was redirected by a flow filter configured in
358      *    this instance.
359      */
360     public final PacketContext evaluate(VTNManagerImpl mgr, PacketContext pctx,
361                                         short vid)
362         throws DropFlowException, RedirectFlowException {
363         if (pctx.isFilterDisabled()) {
364             logDisabled(mgr, pctx);
365             return pctx;
366         }
367
368         PacketContext pc;
369         if (flowFilters.isEmpty()) {
370             pc = pctx;
371         } else {
372             pc = getPacketContext(pctx);
373             evaluateImpl(mgr, pc, vid);
374         }
375
376         return pc;
377     }
378
379     /**
380      * Create a prefix string for a log record.
381      *
382      * @param index  Index of the flow filter.
383      * @return  A prefix for a log record.
384      */
385     final String getLogPrefix(int index) {
386         StringBuilder builder = new StringBuilder(parent.getContainerName());
387         return builder.append(':').append(parent.getPath()).
388             append(':').append(getFlowDirectionName()).
389             append('.').append(index).toString();
390     }
391
392     /**
393      * Return the virtual node that contains this flow filter map.
394      *
395      * @return  A {@link FlowFilterNode} instance that contains this
396      *          flow filter map.
397      */
398     final FlowFilterNode getParent() {
399         return parent;
400     }
401
402     /**
403      * Set the virtual node that contains this flow filter.
404      *
405      * @param fnode  Virtual node that contains this flow filter.
406      */
407     final void setParent(FlowFilterNode fnode) {
408         parent = fnode;
409     }
410
411     /**
412      * Determine the flow direction to be evaluated.
413      *
414      * @return  {@code true} is returned if the flow filter is applied to
415      *          outgoing flow. Otherwise {@code false} is returned.
416      */
417     protected abstract boolean isOutput();
418
419     /**
420      * Return a string which describes the flow direction to be evaluated.
421      *
422      * @return  A string which represents the flow direction.
423      */
424     protected abstract String getFlowDirectionName();
425
426     /**
427      * Return a {@link PacketContext} instance to be used for filtering.
428      *
429      * @param pctx  A packet context which contains the packet.
430      * @return  A {@link PacketContext} instance.
431      */
432     protected abstract PacketContext getPacketContext(PacketContext pctx);
433
434     /**
435      * Evaluate flow filters configured in this instance.
436      *
437      * @param mgr   VTN Manager service.
438      * @param pctx  A packet context which contains the packet.
439      * @param vid   A VLAN ID to be used for packet matching.
440      *              A VLAN ID configured in the given packet is used if a
441      *              negative value is specified.
442      * @throws DropFlowException
443      *    The given packet was discarded by a flow filter configured in
444      *    this instance.
445      * @throws RedirectFlowException
446      *    The given packet was redirected by a flow filter configured in
447      *    this instance.
448      */
449     private void evaluateImpl(VTNManagerImpl mgr, PacketContext pctx,
450                               short vid)
451         throws DropFlowException, RedirectFlowException {
452         if (vid >= 0) {
453             // Use the given VLAN ID for packet matching.
454             pctx.setVlan(vid);
455         }
456
457         if (LOG.isDebugEnabled()) {
458             LOG.debug("{}:{}:{}: Evaluating flow filter map: {}",
459                       parent.getContainerName(), parent.getPath(),
460                       getFlowDirectionName(), pctx.getDescription());
461         }
462
463         for (FlowFilterImpl fi: flowFilters.values()) {
464             if (LOG.isTraceEnabled()) {
465                 LOG.trace("{}: Evaluating flow filter: {}",
466                           getLogPrefix(fi.getIndex()), fi);
467             }
468             if (fi.evaluate(mgr, pctx, this)) {
469                 return;
470             }
471         }
472
473         if (LOG.isDebugEnabled()) {
474             LOG.debug("{}:{}:{}: No flow filter was matched",
475                       parent.getContainerName(), parent.getPath(),
476                       getFlowDirectionName());
477         }
478     }
479
480     /**
481      * Record a log message that indicates the given packet disables the
482      * flow filter.
483      *
484      * @param mgr    VTN Manager service.
485      * @param pctx   A packet context which contains the packet.
486      */
487     private void logDisabled(VTNManagerImpl mgr, PacketContext pctx) {
488         if (LOG.isTraceEnabled()) {
489             LOG.trace("{}:{}:{}: Flow filter is disabled: {}",
490                       parent.getContainerName(), parent.getPath(),
491                       getFlowDirectionName(), pctx.getDescription());
492         }
493     }
494
495     /**
496      * Determine whether the given object is identical to this object.
497      *
498      * @param o  An object to be compared.
499      * @return   {@code true} if identical. Otherwise {@code false}.
500      */
501     @Override
502     public final boolean equals(Object o) {
503         if (o == this) {
504             return true;
505         }
506         if (o == null || !getClass().equals(o.getClass())) {
507             return false;
508         }
509
510         FlowFilterMap fmap = (FlowFilterMap)o;
511         return flowFilters.equals(fmap.flowFilters);
512     }
513
514     /**
515      * Return the hash code of this object.
516      *
517      * @return  The hash code.
518      */
519     @Override
520     public final int hashCode() {
521         return getClass().getName().hashCode() +
522             (flowFilters.hashCode() * 37);
523     }
524
525     // Cloneable
526
527     /**
528      * Return a shallow copy of this instance.
529      *
530      * @return  A copy of this instance.
531      */
532     @Override
533     public final FlowFilterMap clone() {
534         try {
535             FlowFilterMap fmap = (FlowFilterMap)super.clone();
536             fmap.flowFilters = (Map<Integer, FlowFilterImpl>)
537                 ((TreeMap<Integer, FlowFilterImpl>)flowFilters).clone();
538
539             return fmap;
540         } catch (CloneNotSupportedException e) {
541             // This should never happen.
542             throw new IllegalStateException("clone() failed", e);
543         }
544     }
545
546     /**
547      * A map that keeps flow filters for incoming packets.
548      *
549      * <p>
550      *   Although this class is public to other packages, this class does not
551      *   provide any API. Applications other than VTN Manager must not use this
552      *   class.
553      * </p>
554      */
555     public static final class Incoming extends FlowFilterMap {
556         /**
557          * Version number for serialization.
558          */
559         private static final long serialVersionUID = -7266877348382534521L;
560
561         /**
562          * Construct a new instance.
563          *
564          * @param fnode  Virtual node that contains this flow filter.
565          */
566         private Incoming(FlowFilterNode fnode) {
567             super(fnode);
568         }
569
570         /**
571          * Determine the flow direction to be evaluated.
572          *
573          * @return  {@code false}.
574          */
575         @Override
576         protected boolean isOutput() {
577             return false;
578         }
579
580         /**
581          * Return a string which describes the flow direction to be evaluated.
582          *
583          * @return  {@code "IN"}.
584          */
585         @Override
586         protected String getFlowDirectionName() {
587             return DIRECTION_IN;
588         }
589
590         /**
591          * Return a {@link PacketContext} instance to be used for filtering.
592          *
593          * @param pctx  A packet context which contains the packet.
594          * @return  The given {@link PacketContext} instance is always
595          *          returned.
596          */
597         @Override
598         protected PacketContext getPacketContext(PacketContext pctx) {
599             return pctx;
600         }
601     }
602
603     /**
604      * A map that keeps flow filters for outgoing packets.
605      *
606      * <p>
607      *   Although this class is public to other packages, this class does not
608      *   provide any API. Applications other than VTN Manager must not use this
609      *   class.
610      * </p>
611      */
612     public static final class Outgoing extends FlowFilterMap {
613         /**
614          * Version number for serialization.
615          */
616         private static final long serialVersionUID = 7451633827904306067L;
617
618         /**
619          * Construct a new instance.
620          *
621          * @param fnode  Virtual node that contains this flow filter.
622          */
623         private Outgoing(FlowFilterNode fnode) {
624             super(fnode);
625         }
626
627         /**
628          * Determine the flow direction to be evaluated.
629          *
630          * @return  {@code true}.
631          */
632         @Override
633         protected boolean isOutput() {
634             return true;
635         }
636
637         /**
638          * Return a string which describes the flow direction to be evaluated.
639          *
640          * @return  {@code "OUT"}.
641          */
642         @Override
643         protected String getFlowDirectionName() {
644             return DIRECTION_OUT;
645         }
646
647         /**
648          * Return a {@link PacketContext} instance to be used for filtering.
649          *
650          * @param pctx  A packet context which contains the packet.
651          * @return  A {@link PacketContext} to be used for transmitting packet.
652          */
653         @Override
654         protected PacketContext getPacketContext(PacketContext pctx) {
655             // If the given packet is going to be broadcasted, we have to
656             // preserve the original packet for succeeding transmission.
657             return (pctx.isFlooding()) ? pctx.clone() : pctx;
658         }
659     }
660 }