2 * Copyright (c) 2014 NEC Corporation
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
10 package org.opendaylight.vtn.manager.internal.cluster;
12 import java.io.Serializable;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
17 import java.util.TreeMap;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
22 import org.opendaylight.vtn.manager.VTNException;
23 import org.opendaylight.vtn.manager.VTenantPath;
24 import org.opendaylight.vtn.manager.flow.filter.FlowFilter;
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;
31 import org.opendaylight.controller.sal.core.UpdateType;
32 import org.opendaylight.controller.sal.utils.Status;
33 import org.opendaylight.controller.sal.utils.StatusCode;
36 * A map that keeps flow filters.
39 * Note that this class is not synchronized. Synchronization should be done
40 * by the virtual node that contains the flow filter.
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
48 public abstract class FlowFilterMap implements Serializable, Cloneable {
50 * Version number for serialization.
52 private static final long serialVersionUID = -8039322793324993017L;
55 * A pseudo VLAN ID which represents the VLAN ID is not specified.
57 public static final short VLAN_UNSPEC = -1;
62 private static final Logger LOG =
63 LoggerFactory.getLogger(FlowFilterMap.class);
66 * Pseudo flow filter index that indicates all flow filters in this map.
68 private static final int INDEX_ALL = 0;
71 * A string that indicates this flow filter is applied to outgoing flow.
73 private static final String DIRECTION_IN = "IN";
76 * A string that indicates this flow filter is applied to outgoing flow.
78 private static final String DIRECTION_OUT = "OUT";
81 * Pairs of filter index and flow filter implementation.
83 private Map<Integer, FlowFilterImpl> flowFilters =
84 new TreeMap<Integer, FlowFilterImpl>();
87 * The virtual node that contains this flow filter map.
90 * This field never affects object identify.
93 private transient FlowFilterNode parent;
96 * Create a new flow filter map for incoming packets.
98 * @param fnode Virtual node that contains this flow filter.
99 * @return A {@link FlowFilterMap} instance.
101 public static FlowFilterMap createIncoming(FlowFilterNode fnode) {
102 return new Incoming(fnode);
106 * Create a new flow filter map for outgoing packets.
108 * @param fnode Virtual node that contains this flow filter.
109 * @return A {@link FlowFilterMap} instance.
111 public static FlowFilterMap createOutgoing(FlowFilterNode fnode) {
112 return new Outgoing(fnode);
116 * Invoked when a flow filter event is received from another controller.
118 * @param mgr VTN Manager service.
119 * @param ev A {@link FlowFilterEvent} instance.
121 public static void eventReceived(VTNManagerImpl mgr, FlowFilterEvent ev) {
122 // Save VTN configuration.
123 VTenantPath path = ev.getPath();
124 mgr.saveTenantConfig(path.getTenantName());
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);
134 UpdateType type = ev.getUpdateType();
135 logUpdated(container, path, direction, index, type, null);
140 * Return a string which describes the flow direction to be evaluated.
142 * @param out A boolean value which specifies the flow direction.
143 * @return A string which represents the flow direction.
145 public static String getFlowDirectionName(boolean out) {
146 return (out) ? DIRECTION_OUT : DIRECTION_IN;
150 * Record an informational log that indicates a flow filter was updated.
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.
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(),
167 LOG.info("{}:{}:{}.{}: Flow filter was {}.",
168 container, path, direction, index, type.getName());
173 * Record an informational log that indicates all flow filters was updated.
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.
179 private static void logCleared(String container, VTenantPath path,
181 LOG.info("{}:{}:{}: All flow filters were removed.",
182 container, path, direction);
186 * Construct a new instance.
188 * @param fnode Virtual node that contains this flow filter.
190 private FlowFilterMap(FlowFilterNode fnode) {
195 * Return a list of flow filters configured in this instance.
197 * @return A list of {@link FlowFilter} instances.
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());
209 * Return the flow filter associated with the specified index number
212 * @param index The index number of the flow filter.
213 * @return A {@link FlowFilter} instance if found.
214 * {@code null} if not found.
216 public final FlowFilter get(int index) {
217 FlowFilterImpl fi = flowFilters.get(index);
218 return (fi == null) ? null : fi.getFlowFilter();
222 * Create or modify the flow filter specified by the index number.
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
231 * @throws VTNException An error occurred.
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);
239 FlowFilterImpl old = flowFilters.put(key, fi);
241 result = UpdateType.ADDED;
242 } else if (old.equals(fi)) {
243 // No change was made to the flow filter.
246 result = UpdateType.CHANGED;
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);
255 logUpdated(parent.getContainerName(), path, getFlowDirectionName(),
257 FlowFilterEvent.raise(mgr, path, isOutput(), index, result);
262 * Remove the flow filter specified by the index number.
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.
270 public final Status remove(VTNManagerImpl mgr, int index) {
271 Integer key = Integer.valueOf(index);
272 FlowFilterImpl fi = flowFilters.remove(key);
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);
283 UpdateType type = UpdateType.REMOVED;
284 logUpdated(parent.getContainerName(), path, getFlowDirectionName(),
286 FlowFilterEvent.raise(mgr, path, isOutput(), index, type);
287 return new Status(StatusCode.SUCCESS, null);
291 * Remove all flow filters in this instance.
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
298 public final Status clear(VTNManagerImpl mgr) {
299 if (flowFilters.isEmpty()) {
303 HashMap<Integer, FlowFilterImpl> removed = (LOG.isTraceEnabled())
304 ? new HashMap<Integer, FlowFilterImpl>(flowFilters)
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);
314 String container = parent.getContainerName();
315 UpdateType type = UpdateType.REMOVED;
316 String direction = getFlowDirectionName();
317 if (removed == null) {
318 logCleared(container, path, direction);
320 for (FlowFilterImpl fi: removed.values()) {
321 logUpdated(container, path, direction, fi.getIndex(), type,
326 FlowFilterEvent.raise(mgr, path, isOutput(), INDEX_ALL, type);
327 return new Status(StatusCode.SUCCESS, null);
331 * Determine whether this flow filter map is empty or not.
333 * @return {@code true} only if this flow filter map is empty.
335 public final boolean isEmpty() {
336 return flowFilters.isEmpty();
340 * Evaluate flow filters configured in this instance.
343 * This method must be called with holding the lock for the parent node.
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
353 * @throws DropFlowException
354 * The given packet was discarded by a flow filter configured in
356 * @throws RedirectFlowException
357 * The given packet was redirected by a flow filter configured in
360 public final PacketContext evaluate(VTNManagerImpl mgr, PacketContext pctx,
362 throws DropFlowException, RedirectFlowException {
363 if (pctx.isFilterDisabled()) {
364 logDisabled(mgr, pctx);
369 if (flowFilters.isEmpty()) {
372 pc = getPacketContext(pctx);
373 evaluateImpl(mgr, pc, vid);
380 * Create a prefix string for a log record.
382 * @param index Index of the flow filter.
383 * @return A prefix for a log record.
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();
393 * Return the virtual node that contains this flow filter map.
395 * @return A {@link FlowFilterNode} instance that contains this
398 final FlowFilterNode getParent() {
403 * Set the virtual node that contains this flow filter.
405 * @param fnode Virtual node that contains this flow filter.
407 final void setParent(FlowFilterNode fnode) {
412 * Determine the flow direction to be evaluated.
414 * @return {@code true} is returned if the flow filter is applied to
415 * outgoing flow. Otherwise {@code false} is returned.
417 protected abstract boolean isOutput();
420 * Return a string which describes the flow direction to be evaluated.
422 * @return A string which represents the flow direction.
424 protected abstract String getFlowDirectionName();
427 * Return a {@link PacketContext} instance to be used for filtering.
429 * @param pctx A packet context which contains the packet.
430 * @return A {@link PacketContext} instance.
432 protected abstract PacketContext getPacketContext(PacketContext pctx);
435 * Evaluate flow filters configured in this instance.
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
445 * @throws RedirectFlowException
446 * The given packet was redirected by a flow filter configured in
449 private void evaluateImpl(VTNManagerImpl mgr, PacketContext pctx,
451 throws DropFlowException, RedirectFlowException {
453 // Use the given VLAN ID for packet matching.
457 if (LOG.isDebugEnabled()) {
458 LOG.debug("{}:{}:{}: Evaluating flow filter map: {}",
459 parent.getContainerName(), parent.getPath(),
460 getFlowDirectionName(), pctx.getDescription());
463 for (FlowFilterImpl fi: flowFilters.values()) {
464 if (LOG.isTraceEnabled()) {
465 LOG.trace("{}: Evaluating flow filter: {}",
466 getLogPrefix(fi.getIndex()), fi);
468 if (fi.evaluate(mgr, pctx, this)) {
473 if (LOG.isDebugEnabled()) {
474 LOG.debug("{}:{}:{}: No flow filter was matched",
475 parent.getContainerName(), parent.getPath(),
476 getFlowDirectionName());
481 * Record a log message that indicates the given packet disables the
484 * @param mgr VTN Manager service.
485 * @param pctx A packet context which contains the packet.
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());
496 * Determine whether the given object is identical to this object.
498 * @param o An object to be compared.
499 * @return {@code true} if identical. Otherwise {@code false}.
502 public final boolean equals(Object o) {
506 if (o == null || !getClass().equals(o.getClass())) {
510 FlowFilterMap fmap = (FlowFilterMap)o;
511 return flowFilters.equals(fmap.flowFilters);
515 * Return the hash code of this object.
517 * @return The hash code.
520 public final int hashCode() {
521 return getClass().getName().hashCode() +
522 (flowFilters.hashCode() * 37);
528 * Return a shallow copy of this instance.
530 * @return A copy of this instance.
533 public final FlowFilterMap clone() {
535 FlowFilterMap fmap = (FlowFilterMap)super.clone();
536 fmap.flowFilters = (Map<Integer, FlowFilterImpl>)
537 ((TreeMap<Integer, FlowFilterImpl>)flowFilters).clone();
540 } catch (CloneNotSupportedException e) {
541 // This should never happen.
542 throw new IllegalStateException("clone() failed", e);
547 * A map that keeps flow filters for incoming packets.
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
555 public static final class Incoming extends FlowFilterMap {
557 * Version number for serialization.
559 private static final long serialVersionUID = -7266877348382534521L;
562 * Construct a new instance.
564 * @param fnode Virtual node that contains this flow filter.
566 private Incoming(FlowFilterNode fnode) {
571 * Determine the flow direction to be evaluated.
573 * @return {@code false}.
576 protected boolean isOutput() {
581 * Return a string which describes the flow direction to be evaluated.
583 * @return {@code "IN"}.
586 protected String getFlowDirectionName() {
591 * Return a {@link PacketContext} instance to be used for filtering.
593 * @param pctx A packet context which contains the packet.
594 * @return The given {@link PacketContext} instance is always
598 protected PacketContext getPacketContext(PacketContext pctx) {
604 * A map that keeps flow filters for outgoing packets.
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
612 public static final class Outgoing extends FlowFilterMap {
614 * Version number for serialization.
616 private static final long serialVersionUID = 7451633827904306067L;
619 * Construct a new instance.
621 * @param fnode Virtual node that contains this flow filter.
623 private Outgoing(FlowFilterNode fnode) {
628 * Determine the flow direction to be evaluated.
630 * @return {@code true}.
633 protected boolean isOutput() {
638 * Return a string which describes the flow direction to be evaluated.
640 * @return {@code "OUT"}.
643 protected String getFlowDirectionName() {
644 return DIRECTION_OUT;
648 * Return a {@link PacketContext} instance to be used for filtering.
650 * @param pctx A packet context which contains the packet.
651 * @return A {@link PacketContext} to be used for transmitting packet.
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;