2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.l2switch.arphandler.core;
10 import com.google.common.collect.ImmutableList;
11 import com.google.common.util.concurrent.FluentFuture;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
16 import java.util.Optional;
17 import java.util.concurrent.ExecutionException;
18 import java.util.concurrent.Executors;
19 import java.util.concurrent.Future;
20 import java.util.concurrent.ScheduledExecutorService;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.atomic.AtomicLong;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.mdsal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.mdsal.binding.api.DataTreeModification;
27 import org.opendaylight.mdsal.binding.api.ReadTransaction;
28 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
29 import org.opendaylight.openflowplugin.api.OFConstants;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
66 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
67 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
68 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
69 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
70 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
71 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatus;
72 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2switch.loopremover.rev140714.StpStatusAwareNodeConnector;
73 import org.opendaylight.yangtools.concepts.ListenerRegistration;
74 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
75 import org.opendaylight.yangtools.yang.common.RpcResult;
76 import org.opendaylight.yangtools.yang.common.Uint16;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
81 * ProactiveFloodFlowWriter is used for the proactive mode of L2Switch. In this
82 * mode, flood flows are automatically written to each switch and less traffic
83 * is sent to the controller.
85 public class ProactiveFloodFlowWriter implements DataTreeChangeListener<StpStatusAwareNodeConnector>,
86 OpendaylightInventoryListener {
88 private static final Logger LOG = LoggerFactory.getLogger(ProactiveFloodFlowWriter.class);
90 private static final String FLOW_ID_PREFIX = "L2switch-";
92 private final DataBroker dataBroker;
93 private final SalFlowService salFlowService;
94 private final ScheduledExecutorService stpStatusDataChangeEventProcessor = Executors.newScheduledThreadPool(1);
95 private volatile boolean flowRefreshScheduled = false;
96 private volatile boolean threadReschedule = false;
97 private long flowInstallationDelay;
98 private short flowTableId;
99 private int flowPriority;
100 private int flowIdleTimeout;
101 private int flowHardTimeout;
102 private final AtomicLong flowIdInc = new AtomicLong();
103 private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L);
105 public ProactiveFloodFlowWriter(DataBroker dataBroker, SalFlowService salFlowService) {
106 this.dataBroker = dataBroker;
107 this.salFlowService = salFlowService;
110 public void setFlowInstallationDelay(long flowInstallationDelay) {
111 this.flowInstallationDelay = flowInstallationDelay;
114 public void setFlowTableId(short flowTableId) {
115 this.flowTableId = flowTableId;
118 public void setFlowPriority(int flowPriority) {
119 this.flowPriority = flowPriority;
122 public void setFlowIdleTimeout(int flowIdleTimeout) {
123 this.flowIdleTimeout = flowIdleTimeout;
126 public void setFlowHardTimeout(int flowHardTimeout) {
127 this.flowHardTimeout = flowHardTimeout;
131 public void onNodeConnectorRemoved(NodeConnectorRemoved notification) {
136 public void onNodeConnectorUpdated(NodeConnectorUpdated notification) {
141 public void onNodeRemoved(NodeRemoved notification) {
146 * Install flood flows when a node comes up/down.
148 * @param notification
149 * Notification for when a node comes up.
152 public void onNodeUpdated(NodeUpdated notification) {
153 if (!flowRefreshScheduled) {
154 synchronized (this) {
155 if (!flowRefreshScheduled) {
156 stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(),
157 flowInstallationDelay, TimeUnit.MILLISECONDS);
158 flowRefreshScheduled = true;
159 LOG.debug("Scheduled Flows for refresh.");
163 LOG.debug("Already scheduled for flow refresh.");
164 threadReschedule = true;
169 * Registers as a data listener for Nodes.
171 public ListenerRegistration<ProactiveFloodFlowWriter> registerAsDataChangeListener() {
172 return dataBroker.registerDataTreeChangeListener(
173 DataTreeIdentifier.create(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(Nodes.class)
175 .child(NodeConnector.class)
176 .augmentation(StpStatusAwareNodeConnector.class)
181 * Install flows when a link comes up/down.
184 public void onDataTreeChanged(Collection<DataTreeModification<StpStatusAwareNodeConnector>> changes) {
185 if (!flowRefreshScheduled) {
186 synchronized (this) {
187 if (!flowRefreshScheduled) {
188 stpStatusDataChangeEventProcessor.schedule(new StpStatusDataChangeEventProcessor(),
189 flowInstallationDelay, TimeUnit.MILLISECONDS);
190 flowRefreshScheduled = true;
191 LOG.debug("Scheduled Flows for refresh.");
195 LOG.debug("Already scheduled for flow refresh.");
196 threadReschedule = true;
200 private class StpStatusDataChangeEventProcessor implements Runnable {
203 LOG.debug("In flow refresh thread.");
204 if (threadReschedule) {
205 LOG.debug("Rescheduling thread");
206 stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS);
207 threadReschedule = false;
211 flowRefreshScheduled = false;
216 * Installs a FloodFlow on each node.
218 private void installFloodFlows() {
219 final FluentFuture<Optional<Nodes>> readFuture;
220 try (ReadTransaction readOnlyTransaction = dataBroker.newReadOnlyTransaction()) {
221 readFuture = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL,
222 InstanceIdentifier.create(Nodes.class));
227 nodes = readFuture.get().orElse(null);
228 } catch (InterruptedException e) {
229 LOG.error("Failed to read nodes from Operation data store.");
230 throw new RuntimeException("Failed to read nodes from Operation data store.", e);
231 } catch (ExecutionException e) {
232 LOG.error("Failed to read nodes from Operation data store.");
233 throw new RuntimeException("Failed to read nodes from Operation data store.", e);
237 // Reschedule thread when the data store read had errors
238 LOG.debug("Rescheduling flow refresh thread because datastore read failed.");
239 if (!flowRefreshScheduled) {
240 flowRefreshScheduled = true;
241 stpStatusDataChangeEventProcessor.schedule(this, flowInstallationDelay, TimeUnit.MILLISECONDS);
244 for (Node node : nodes.nonnullNode().values()) {
245 // Install a FloodFlow on each node
246 final Map<NodeConnectorKey, NodeConnector> nodeConnectors = node.getNodeConnector();
247 if (nodeConnectors != null) {
248 for (NodeConnector outerNodeConnector : nodeConnectors.values()) {
249 StpStatusAwareNodeConnector outerSaNodeConnector = outerNodeConnector
250 .augmentation(StpStatusAwareNodeConnector.class);
251 if (outerSaNodeConnector != null
252 && StpStatus.Discarding.equals(outerSaNodeConnector.getStatus())) {
255 if (!outerNodeConnector.getId().toString().contains("LOCAL")) {
257 ArrayList<Action> outputActions = new ArrayList<>();
258 for (NodeConnector nodeConnector : nodeConnectors.values()) {
259 if (!nodeConnector.getId().toString().contains("LOCAL")
260 && !outerNodeConnector.equals(nodeConnector)) {
261 // NodeConnectors without STP status
262 // (external ports) and NodeConnectors
263 // that are "forwarding" will be flooded
265 StpStatusAwareNodeConnector saNodeConnector = nodeConnector
266 .augmentation(StpStatusAwareNodeConnector.class);
267 if (saNodeConnector == null
268 || StpStatus.Forwarding.equals(saNodeConnector.getStatus())) {
269 outputActions.add(new ActionBuilder()
272 new OutputActionCaseBuilder()
273 .setOutputAction(new OutputActionBuilder()
274 .setMaxLength(Uint16.MAX_VALUE)
275 .setOutputNodeConnector(nodeConnector.getId())
283 // Add controller port to outputActions for
284 // external ports only
285 if (outerSaNodeConnector == null) {
286 outputActions.add(new ActionBuilder().withKey(new ActionKey(order++))
288 new OutputActionCaseBuilder()
289 .setOutputAction(new OutputActionBuilder()
290 .setMaxLength(Uint16.MAX_VALUE)
291 .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString()))
297 // Create an Apply Action
298 ApplyActions applyActions = new ApplyActionsBuilder() //
299 .setAction(ImmutableList.copyOf(outputActions)).build();
301 // Wrap our Apply Action in an Instruction
302 Instruction applyActionsInstruction = new InstructionBuilder() //
304 .setInstruction(new ApplyActionsCaseBuilder()//
305 .setApplyActions(applyActions) //
309 FlowBuilder floodFlowBuilder = createBaseFlowForPortMatch(outerNodeConnector);
310 floodFlowBuilder.setInstructions(new InstructionsBuilder() //
311 .setInstruction(ImmutableList.of(applyActionsInstruction)) //
314 writeFlowToSwitch(node.getId(), floodFlowBuilder.build());
322 private FlowBuilder createBaseFlowForPortMatch(NodeConnector nc) {
323 FlowBuilder floodFlow = new FlowBuilder().setTableId(flowTableId).setFlowName("flood");
324 floodFlow.setId(new FlowId(Long.toString(floodFlow.hashCode())));
326 Match match = new MatchBuilder().setInPort(nc.getId()).build();
328 floodFlow.setMatch(match) //
329 .setPriority(flowPriority) //
330 .setBufferId(OFConstants.OFP_NO_BUFFER) //
331 .setHardTimeout(flowHardTimeout) //
332 .setIdleTimeout(flowIdleTimeout) //
333 .setCookie(new FlowCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement())))
334 .setFlags(new FlowModFlags(false, false, false, false, false));
339 * Starts and commits data change transaction which modifies provided
340 * flow path with supplied body.
342 private Future<RpcResult<AddFlowOutput>> writeFlowToSwitch(NodeId nodeId, Flow flow) {
343 InstanceIdentifier<Node> nodeInstanceId = InstanceIdentifier.<Nodes>builder(Nodes.class)
344 .<Node, NodeKey>child(Node.class, new NodeKey(nodeId)).build();
345 InstanceIdentifier<Table> tableInstanceId = nodeInstanceId
346 .<FlowCapableNode>augmentation(FlowCapableNode.class)
347 .<Table, TableKey>child(Table.class, new TableKey(flowTableId));
348 InstanceIdentifier<Flow> flowPath = tableInstanceId.<Flow, FlowKey>child(Flow.class,
349 new FlowKey(new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement()))));
351 final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow).setNode(new NodeRef(nodeInstanceId))
352 .setFlowTable(new FlowTableRef(tableInstanceId)).setFlowRef(new FlowRef(flowPath))
353 .setTransactionUri(new Uri(flow.getId().getValue()));
354 return salFlowService.addFlow(builder.build());