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