Fix FindBugs violations and enable enforcement
[l2switch.git] / arphandler / src / main / java / org / opendaylight / l2switch / arphandler / flow / InitialFlowWriter.java
1 /*
2  * Copyright (c) 2014, 2015 Cisco Systems, Inc., Brocade Communications Systems, Inc. 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.l2switch.arphandler.flow;
10
11 import com.google.common.collect.ImmutableList;
12 import java.math.BigInteger;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Set;
18 import java.util.concurrent.ExecutorService;
19 import java.util.concurrent.Executors;
20 import java.util.concurrent.Future;
21 import java.util.concurrent.atomic.AtomicLong;
22 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
23 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
24 import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.openflowplugin.api.OFConstants;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Uri;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
60 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
61 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
62 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.EtherType;
63 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetTypeBuilder;
64 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
65 import org.opendaylight.yang.gen.v1.urn.opendaylight.packet.ethernet.rev140528.KnownEtherType;
66 import org.opendaylight.yangtools.concepts.ListenerRegistration;
67 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
68 import org.opendaylight.yangtools.yang.common.RpcResult;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 /**
73  * Adds a flow, which sends all ARP packets to the controller, on all switches.
74  * Registers as ODL Inventory listener so that it can add flows once a new node i.e. switch is added
75  */
76 public class InitialFlowWriter implements DataTreeChangeListener<Node> {
77     private static final Logger LOG = LoggerFactory.getLogger(InitialFlowWriter.class);
78
79     private static final String FLOW_ID_PREFIX = "L2switch-";
80
81     private final ExecutorService initialFlowExecutor = Executors.newCachedThreadPool();
82     private final SalFlowService salFlowService;
83     private short flowTableId;
84     private int flowPriority;
85     private int flowIdleTimeout;
86     private int flowHardTimeout;
87     private boolean isHybridMode;
88
89     private final AtomicLong flowIdInc = new AtomicLong();
90     private final AtomicLong flowCookieInc = new AtomicLong(0x2b00000000000000L);
91
92
93     public InitialFlowWriter(SalFlowService salFlowService) {
94         this.salFlowService = salFlowService;
95     }
96
97     public void setFlowTableId(short flowTableId) {
98         this.flowTableId = flowTableId;
99     }
100
101     public void setFlowPriority(int flowPriority) {
102         this.flowPriority = flowPriority;
103     }
104
105     public void setFlowIdleTimeout(int flowIdleTimeout) {
106         this.flowIdleTimeout = flowIdleTimeout;
107     }
108
109     public void setFlowHardTimeout(int flowHardTimeout) {
110         this.flowHardTimeout = flowHardTimeout;
111     }
112
113     public void setIsHybridMode(boolean isHybridMode) {
114         this.isHybridMode = isHybridMode;
115     }
116
117     public ListenerRegistration<InitialFlowWriter> registerAsDataChangeListener(DataBroker dataBroker) {
118         InstanceIdentifier<Node> nodeInstanceIdentifier = InstanceIdentifier.builder(Nodes.class)
119                 .child(Node.class).build();
120         return dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
121                 nodeInstanceIdentifier), this);
122     }
123
124     @Override
125     public void onDataTreeChanged(Collection<DataTreeModification<Node>> changes) {
126         Set<InstanceIdentifier<?>> nodeIds = new HashSet<>();
127         for (DataTreeModification<Node> change: changes) {
128             DataObjectModification<Node> rootNode = change.getRootNode();
129             final InstanceIdentifier<Node> identifier = change.getRootPath().getRootIdentifier();
130             switch (rootNode.getModificationType()) {
131                 case WRITE:
132                     if (rootNode.getDataBefore() == null) {
133                         nodeIds.add(identifier);
134                     }
135                     break;
136                 default:
137                     break;
138             }
139         }
140
141         if (!nodeIds.isEmpty()) {
142             initialFlowExecutor.execute(new InitialFlowWriterProcessor(nodeIds));
143         }
144     }
145
146     /**
147      * A private class to process the node updated event in separate thread. Allows to release the
148      * thread that invoked the data node updated event. Avoids any thread lock it may cause.
149      */
150     private class InitialFlowWriterProcessor implements Runnable {
151         private final Set<InstanceIdentifier<?>> nodeIds;
152
153         InitialFlowWriterProcessor(Set<InstanceIdentifier<?>> nodeIds) {
154             this.nodeIds = nodeIds;
155         }
156
157         @Override
158         public void run() {
159             if (nodeIds == null) {
160                 return;
161             }
162
163             for (InstanceIdentifier<?> nodeId : nodeIds) {
164                 if (Node.class.isAssignableFrom(nodeId.getTargetType())) {
165                     InstanceIdentifier<Node> invNodeId = (InstanceIdentifier<Node>)nodeId;
166                     if (invNodeId.firstKeyOf(Node.class,NodeKey.class).getId().getValue().contains("openflow:")) {
167                         addInitialFlows(invNodeId);
168                     }
169                 }
170             }
171
172         }
173
174         /**
175          * Adds a flow, which sends all ARP packets to the controller, to the specified node.
176          * @param nodeId The node to write the flow on.
177          */
178         public void addInitialFlows(InstanceIdentifier<Node> nodeId) {
179             LOG.debug("adding initial flows for node {} ", nodeId);
180
181             InstanceIdentifier<Table> tableId = getTableInstanceId(nodeId);
182             InstanceIdentifier<Flow> flowId = getFlowInstanceId(tableId);
183
184             //add arpToController flow
185             writeFlowToController(nodeId, tableId, flowId, createArpToControllerFlow(flowTableId, flowPriority));
186             LOG.debug("Added initial flows for node {} ", nodeId);
187         }
188
189         private InstanceIdentifier<Table> getTableInstanceId(InstanceIdentifier<Node> nodeId) {
190             // get flow table key
191             TableKey flowTableKey = new TableKey(flowTableId);
192
193             return nodeId.builder()
194                     .augmentation(FlowCapableNode.class)
195                     .child(Table.class, flowTableKey)
196                     .build();
197         }
198
199         private InstanceIdentifier<Flow> getFlowInstanceId(InstanceIdentifier<Table> tableId) {
200             // generate unique flow key
201             FlowId flowId = new FlowId(FLOW_ID_PREFIX + String.valueOf(flowIdInc.getAndIncrement()));
202             FlowKey flowKey = new FlowKey(flowId);
203             return tableId.child(Flow.class, flowKey);
204         }
205
206         private Flow createArpToControllerFlow(Short tableId, int priority) {
207
208             // start building flow
209             FlowBuilder arpFlow = new FlowBuilder() //
210                     .setTableId(tableId) //
211                     .setFlowName("arptocntrl");
212
213             // use its own hash code for id.
214             arpFlow.setId(new FlowId(Long.toString(arpFlow.hashCode())));
215             EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder()
216                     .setEthernetType(new EthernetTypeBuilder()
217                             .setType(new EtherType(Long.valueOf(KnownEtherType.Arp.getIntValue()))).build());
218
219             Match match = new MatchBuilder()
220                     .setEthernetMatch(ethernetMatchBuilder.build())
221                     .build();
222
223             List<Action> actions = new ArrayList<>();
224             actions.add(getSendToControllerAction());
225             if (isHybridMode) {
226                 actions.add(getNormalAction());
227             }
228
229             // Create an Apply Action
230             ApplyActions applyActions = new ApplyActionsBuilder() //
231                     .setAction(ImmutableList.copyOf(actions)) //
232                     .build();
233
234             // Wrap our Apply Action in an Instruction
235             Instruction applyActionsInstruction = new InstructionBuilder() //
236                     .setOrder(0)
237                     .setInstruction(new ApplyActionsCaseBuilder()//
238                             .setApplyActions(applyActions) //
239                             .build()) //
240                     .build();
241
242             // Put our Instruction in a list of Instructions
243             arpFlow
244                     .setMatch(match) //
245                     .setInstructions(new InstructionsBuilder() //
246                             .setInstruction(ImmutableList.of(applyActionsInstruction)) //
247                             .build()) //
248                     .setPriority(priority) //
249                     .setBufferId(OFConstants.OFP_NO_BUFFER) //
250                     .setHardTimeout(flowHardTimeout) //
251                     .setIdleTimeout(flowIdleTimeout) //
252                     .setCookie(new FlowCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement())))
253                     .setFlags(new FlowModFlags(false, false, false, false, false));
254
255             return arpFlow.build();
256         }
257
258         private Action getSendToControllerAction() {
259             Action sendToController = new ActionBuilder()
260                     .setOrder(0)
261                     .setKey(new ActionKey(0))
262                     .setAction(new OutputActionCaseBuilder()
263                             .setOutputAction(new OutputActionBuilder()
264                                     .setMaxLength(0xffff)
265                                     .setOutputNodeConnector(new Uri(OutputPortValues.CONTROLLER.toString()))
266                                     .build())
267                             .build())
268                     .build();
269             return sendToController;
270         }
271
272         private Action getNormalAction() {
273             Action normal = new ActionBuilder()
274                     .setOrder(0)
275                     .setKey(new ActionKey(0))
276                     .setAction(new OutputActionCaseBuilder()
277                             .setOutputAction(new OutputActionBuilder()
278                                     .setMaxLength(0xffff)
279                                     .setOutputNodeConnector(new Uri(OutputPortValues.NORMAL.toString()))
280                                     .build())
281                             .build())
282                     .build();
283             return normal;
284         }
285
286         private Future<RpcResult<AddFlowOutput>> writeFlowToController(InstanceIdentifier<Node> nodeInstanceId,
287                                                                        InstanceIdentifier<Table> tableInstanceId,
288                                                                        InstanceIdentifier<Flow> flowPath,
289                                                                        Flow flow) {
290             LOG.trace("Adding flow to node {}",nodeInstanceId.firstKeyOf(Node.class, NodeKey.class).getId().getValue());
291             final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow);
292             builder.setNode(new NodeRef(nodeInstanceId));
293             builder.setFlowRef(new FlowRef(flowPath));
294             builder.setFlowTable(new FlowTableRef(tableInstanceId));
295             builder.setTransactionUri(new Uri(flow.getId().getValue()));
296             return salFlowService.addFlow(builder.build());
297         }
298     }
299 }