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