Adding Initial Flows on switch for performance improvement.
[l2switch.git] / l2switch-main / src / main / java / org / opendaylight / l2switch / flow / FlowWriterServiceImpl.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.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import org.opendaylight.l2switch.util.InstanceIdentifierUtils;
13 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Uri;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
15 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.OutputActionCaseBuilder;
16 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.output.action._case.OutputActionBuilder;
17 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
18 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
19 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
20 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowInputBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.AddFlowOutput;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.FlowTableRef;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.service.rev130819.SalFlowService;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowRef;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Match;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRef;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnectorKey;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetDestinationBuilder;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.ethernet.match.fields.EthernetSourceBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatch;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.EthernetMatchBuilder;
49 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
50 import org.opendaylight.yangtools.yang.common.RpcResult;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 import java.math.BigInteger;
55 import java.util.concurrent.Future;
56 import java.util.concurrent.atomic.AtomicLong;
57
58 /**
59  * Implementation of FlowWriterService{@link org.opendaylight.controller.sample.l2switch.md.flow.FlowWriterService},
60  * that builds required flow and writes to configuration data store using provided DataBrokerService
61  * {@link org.opendaylight.controller.sal.binding.api.data.DataBrokerService}
62  */
63 public class FlowWriterServiceImpl implements FlowWriterService {
64   private static final Logger _logger = LoggerFactory.getLogger(FlowWriterServiceImpl.class);
65   private SalFlowService salFlowService;
66   private AtomicLong flowIdInc = new AtomicLong();
67   private AtomicLong flowCookieInc = new AtomicLong(0x2a00000000000000L);
68
69
70   public FlowWriterServiceImpl(SalFlowService salFlowService) {
71     Preconditions.checkNotNull(salFlowService, "salFlowService should not be null.");
72     this.salFlowService = salFlowService;
73   }
74
75   /**
76    * Writes a flow that forwards packets to destPort if destination mac in packet is destMac and
77    * source Mac in packet is sourceMac. If sourceMac is null then flow would not set any source mac,
78    * resulting in all packets with destMac being forwarded to destPort.
79    *
80    * @param sourceMac
81    * @param destMac
82    * @param destNodeConnectorRef
83    */
84   @Override
85   public void addMacToMacFlow(MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destNodeConnectorRef) {
86
87     Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
88     Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
89
90
91     // do not add flow if both macs are same.
92     if(sourceMac != null && destMac.equals(sourceMac)) {
93       _logger.info("In addMacToMacFlow: No flows added. Source and Destination mac are same.");
94       return;
95     }
96
97     // get flow table key
98     TableKey flowTableKey = new TableKey((short) 0); //TODO: Hard coded Table Id 0, need to get it from Configuration data.
99
100     //build a flow path based on node connector to program flow
101     InstanceIdentifier<Flow> flowPath = buildFlowPath(destNodeConnectorRef, flowTableKey);
102
103     // build a flow that target given mac id
104     Flow flowBody = createMacToMacFlow(flowTableKey.getId(), 10, sourceMac, destMac, destNodeConnectorRef);
105
106     // commit the flow in config data
107     writeFlowToConfigData(flowPath, flowBody);
108   }
109
110   /**
111    * Writes mac-to-mac flow on all ports that are in the path between given source and destination ports.
112    * It uses path provided by NetworkGraphService
113    * {@link org.opendaylight.controller.sample.l2switch.md.topology.NetworkGraphService} to find a links
114    * {@link org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Link}
115    * between given ports. And then writes appropriate flow on each port that is covered in that path.
116    *
117    * @param sourceMac
118    * @param sourceNodeConnectorRef
119    * @param destMac
120    * @param destNodeConnectorRef
121    */
122   @Override
123   public void addBidirectionalMacToMacFlows(MacAddress sourceMac,
124                                             NodeConnectorRef sourceNodeConnectorRef,
125                                             MacAddress destMac,
126                                             NodeConnectorRef destNodeConnectorRef) {
127     Preconditions.checkNotNull(sourceMac, "Source mac address should not be null.");
128     Preconditions.checkNotNull(sourceNodeConnectorRef, "Source port should not be null.");
129     Preconditions.checkNotNull(destMac, "Destination mac address should not be null.");
130     Preconditions.checkNotNull(destNodeConnectorRef, "Destination port should not be null.");
131
132     if(sourceNodeConnectorRef.equals(destNodeConnectorRef)) {
133       _logger.info("In addMacToMacFlowsUsingShortestPath: No flows added. Source and Destination ports are same.");
134       return;
135
136     }
137
138     // add destMac-To-sourceMac flow on source port
139     addMacToMacFlow(destMac, sourceMac, sourceNodeConnectorRef);
140
141     // add sourceMac-To-destMac flow on destination port
142     addMacToMacFlow(sourceMac, destMac, destNodeConnectorRef);
143   }
144
145   /**
146    * @param nodeConnectorRef
147    * @return
148    */
149   private InstanceIdentifier<Flow> buildFlowPath(NodeConnectorRef nodeConnectorRef, TableKey flowTableKey) {
150
151     // generate unique flow key
152     FlowId flowId = new FlowId(String.valueOf(flowIdInc.getAndIncrement()));
153     FlowKey flowKey = new FlowKey(flowId);
154
155     return InstanceIdentifierUtils.generateFlowInstanceIdentifier(nodeConnectorRef, flowTableKey, flowKey);
156   }
157
158   /**
159    * @param tableId
160    * @param priority
161    * @param sourceMac
162    * @param destMac
163    * @param destPort
164    * @return {@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder}
165    *         builds flow that forwards all packets with destMac to given port
166    */
167   private Flow createMacToMacFlow(Short tableId, int priority,
168                                   MacAddress sourceMac, MacAddress destMac, NodeConnectorRef destPort) {
169
170     // start building flow
171     FlowBuilder macToMacFlow = new FlowBuilder() //
172         .setTableId(tableId) //
173         .setFlowName("mac2mac");
174
175     // use its own hash code for id.
176     macToMacFlow.setId(new FlowId(Long.toString(macToMacFlow.hashCode())));
177
178     // create a match that has mac to mac ethernet match
179     EthernetMatchBuilder ethernetMatchBuilder = new EthernetMatchBuilder() //
180         .setEthernetDestination(new EthernetDestinationBuilder() //
181             .setAddress(destMac) //
182             .build());
183     // set source in the match only if present
184     if(sourceMac != null) {
185       ethernetMatchBuilder.setEthernetSource(new EthernetSourceBuilder()
186           .setAddress(sourceMac)
187           .build());
188     }
189     EthernetMatch ethernetMatch = ethernetMatchBuilder.build();
190     Match match = new MatchBuilder()
191         .setEthernetMatch(ethernetMatch)
192         .build();
193
194
195     Uri destPortUri = destPort.getValue().firstKeyOf(NodeConnector.class, NodeConnectorKey.class).getId();
196
197     Action outputToControllerAction = new ActionBuilder() //
198         .setOrder(0)
199         .setAction(new OutputActionCaseBuilder() //
200             .setOutputAction(new OutputActionBuilder() //
201                 .setMaxLength(new Integer(0xffff)) //
202                 .setOutputNodeConnector(destPortUri) //
203                 .build()) //
204             .build()) //
205         .build();
206
207     // Create an Apply Action
208     ApplyActions applyActions = new ApplyActionsBuilder().setAction(ImmutableList.of(outputToControllerAction))
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     macToMacFlow
221         .setMatch(match) //
222         .setInstructions(new InstructionsBuilder() //
223             .setInstruction(ImmutableList.of(applyActionsInstruction)) //
224             .build()) //
225         .setPriority(priority) //
226         .setBufferId(0L) //
227         .setHardTimeout(0) //
228         .setIdleTimeout(0) //
229         .setCookie(new FlowCookie(BigInteger.valueOf(flowCookieInc.getAndIncrement())))
230         .setFlags(new FlowModFlags(false, false, false, false, false));
231
232     return macToMacFlow.build();
233   }
234
235   /**
236    * Starts and commits data change transaction which
237    * modifies provided flow path with supplied body.
238    *
239    * @param flowPath
240    * @param flow
241    * @return transaction commit
242    */
243   private Future<RpcResult<AddFlowOutput>> writeFlowToConfigData(InstanceIdentifier<Flow> flowPath,
244                                                                      Flow flow) {
245     final InstanceIdentifier<Table> tableInstanceId = flowPath.<Table> firstIdentifierOf(Table.class);
246     final InstanceIdentifier<Node> nodeInstanceId = flowPath.<Node> firstIdentifierOf(Node.class);
247     final AddFlowInputBuilder builder = new AddFlowInputBuilder(flow);
248     builder.setNode(new NodeRef(nodeInstanceId));
249     builder.setFlowRef(new FlowRef(flowPath));
250     builder.setFlowTable(new FlowTableRef(tableInstanceId));
251     builder.setTransactionUri(new Uri(flow.getId().getValue()));
252     return salFlowService.addFlow(builder.build());
253   }
254 }