Squashed commit of the following:
[ovsdb.git] / openstack / net-virt-providers / src / main / java / org / opendaylight / ovsdb / openstack / netvirt / providers / openflow13 / AbstractServiceInstance.java
1 /*
2  * Copyright (C) 2014 Red Hat, Inc.
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  * Authors : Madhu Venugopal
9  */
10 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13;
11
12 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
13 import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
14 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
15 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
16 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
17 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
18 import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
19 import org.opendaylight.ovsdb.openstack.netvirt.MdsalUtils;
20 import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils;
21 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
22 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
23 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbBridgeAugmentation;
38 import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
39 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
40
41 import com.google.common.base.Optional;
42 import com.google.common.base.Preconditions;
43 import com.google.common.collect.Lists;
44 import com.google.common.util.concurrent.CheckedFuture;
45 import java.util.List;
46 import java.util.concurrent.ExecutionException;
47 import org.slf4j.Logger;
48 import org.slf4j.LoggerFactory;
49
50 /**
51  * Any ServiceInstance class that extends AbstractServiceInstance to be a part of the pipeline
52  * have 2 basic requirements : <br>
53  * 1. Program a default pipeline flow to take any unmatched traffic to the next table in the pipeline. <br>
54  * 2. Get Pipeline Instructions from AbstractServiceInstance (using getMutablePipelineInstructionBuilder) and
55  *    use it in any matching flows that needs to be further processed by next service in the pipeline.
56  *
57  */
58 public abstract class AbstractServiceInstance {
59     public static final String SERVICE_PROPERTY ="serviceProperty";
60     private static final Logger logger = LoggerFactory.getLogger(AbstractServiceInstance.class);
61     public static final String OPENFLOW = "openflow:";
62     // OSGi Services that we are dependent on.
63     private volatile MdsalConsumer mdsalConsumer;
64     private volatile PipelineOrchestrator orchestrator;
65
66     // Concrete Service that this AbstractServiceInstance represents
67     private Service service;
68
69     public AbstractServiceInstance (Service service) {
70         this.service = service;
71     }
72
73     void init() {
74         logger.info(">>>>> init service: {}", this.getClass());
75     }
76
77     public boolean isBridgeInPipeline (Node node){
78         String bridgeName = MdsalUtils.getBridgeName(node);
79         //logger.trace("isBridgeInPipeline: node {} bridgeName {}", node, bridgeName);
80         if (bridgeName != null && Constants.INTEGRATION_BRIDGE.equals(bridgeName)) {
81             return true;
82         }
83         return false;
84     }
85
86     public short getTable() {
87         return service.getTable();
88     }
89
90     public Service getService() {
91         return service;
92     }
93
94     public void setService(Service service) {
95         this.service = service;
96     }
97
98     public NodeBuilder createNodeBuilder(String nodeId) {
99         NodeBuilder builder = new NodeBuilder();
100         builder.setId(new NodeId(nodeId));
101         builder.setKey(new NodeKey(builder.getId()));
102         return builder;
103     }
104
105     private static final InstanceIdentifier<Flow> createFlowPath(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
106         return InstanceIdentifier.builder(Nodes.class)
107                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
108                         nodeBuilder.getKey())
109                 .augmentation(FlowCapableNode.class)
110                 .child(Table.class, new TableKey(flowBuilder.getTableId()))
111                 .child(Flow.class, flowBuilder.getKey()).build();
112     }
113
114     private static final
115     InstanceIdentifier<org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node>
116     createNodePath(NodeBuilder nodeBuilder) {
117         return InstanceIdentifier.builder(Nodes.class)
118                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node.class,
119                         nodeBuilder.getKey()).build();
120     }
121
122     /**
123      * This method returns the required Pipeline Instructions to by used by any matching flows that need
124      * to be further processed by next service in the pipeline.
125      *
126      * Important to note that this is a convenience method which returns a mutable instructionBuilder which
127      * needs to be further adjusted by the concrete ServiceInstance class such as setting the Instruction Order, etc.
128      * @return Newly created InstructionBuilder to be used along with other instructions on the main flow
129      */
130     protected final InstructionBuilder getMutablePipelineInstructionBuilder() {
131         Service nextService = orchestrator.getNextServiceInPipeline(service);
132         if (nextService != null) {
133             return InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), nextService.getTable());
134         } else {
135             return InstructionUtils.createDropInstructions(new InstructionBuilder());
136         }
137     }
138
139     protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
140         Preconditions.checkNotNull(mdsalConsumer);
141         logger.debug("writeFlow: flowBuilder: {}, nodeBuilder: {}",
142                 flowBuilder.build(), nodeBuilder.build());
143         if (mdsalConsumer == null) {
144             logger.error("ERROR finding MDSAL Service. Its possible that writeFlow is called too soon ?");
145             return;
146         }
147
148         DataBroker dataBroker = mdsalConsumer.getDataBroker();
149         if (dataBroker == null) {
150             logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
151             return;
152         }
153
154         ReadWriteTransaction modification = dataBroker.newReadWriteTransaction();
155         modification.put(LogicalDatastoreType.CONFIGURATION, createNodePath(nodeBuilder),
156                 nodeBuilder.build(), true /*createMissingParents*/);
157         modification.put(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder),
158                 flowBuilder.build(), true /*createMissingParents*/);
159
160         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
161         try {
162             commitFuture.get();  // TODO: Make it async (See bug 1362)
163             logger.debug("Transaction success for write of Flow "+flowBuilder.getFlowName());
164             Thread.sleep(500);
165         } catch (Exception e) {
166             logger.error(e.getMessage(), e);
167             modification.cancel();
168         }
169     }
170
171     protected void removeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
172         Preconditions.checkNotNull(mdsalConsumer);
173         if (mdsalConsumer == null) {
174             logger.error("ERROR finding MDSAL Service.");
175             return;
176         }
177
178         DataBroker dataBroker = mdsalConsumer.getDataBroker();
179         if (dataBroker == null) {
180             logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
181             return;
182         }
183
184         WriteTransaction modification = dataBroker.newWriteOnlyTransaction();
185         modification.delete(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder));
186
187         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
188         try {
189             commitFuture.get();  // TODO: Make it async (See bug 1362)
190             logger.debug("Transaction success for deletion of Flow " + flowBuilder.getFlowName());
191         } catch (Exception e) {
192             logger.error(e.getMessage(), e);
193             modification.cancel();
194         }
195     }
196
197     public Flow getFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
198         Preconditions.checkNotNull(mdsalConsumer);
199         if (mdsalConsumer == null) {
200             logger.error("ERROR finding MDSAL Service. Its possible that writeFlow is called too soon ?");
201             return null;
202         }
203
204         DataBroker dataBroker = mdsalConsumer.getDataBroker();
205         if (dataBroker == null) {
206             logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
207             return null;
208         }
209
210         ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
211         try {
212             Optional<Flow> data =
213                     readTx.read(LogicalDatastoreType.CONFIGURATION, createFlowPath(flowBuilder, nodeBuilder)).get();
214             if (data.isPresent()) {
215                 return data.get();
216             }
217         } catch (InterruptedException|ExecutionException e) {
218             logger.error(e.getMessage(), e);
219         }
220
221         logger.debug("Cannot find data for Flow " + flowBuilder.getFlowName());
222         return null;
223     }
224
225     private Long getDpid(Node node) {
226         Long dpid = 0L;
227         dpid = MdsalUtils.getDataPathId(node);
228         if (dpid == 0) {
229             logger.warn("getDpid: dpid not found: {}", node);
230         }
231         return dpid;
232     }
233
234     /**
235      * Program Default Pipeline Flow.
236      *
237      * @param nodeId Node on which the default pipeline flow is programmed.
238      */
239     protected void programDefaultPipelineRule(Node node) {
240         if (!isBridgeInPipeline(node)) {
241             //logger.trace("Bridge is not in pipeline {} ", node);
242             return;
243         }
244         MatchBuilder matchBuilder = new MatchBuilder();
245         FlowBuilder flowBuilder = new FlowBuilder();
246         Long dpid = getDpid(node);
247         if (dpid == 0L) {
248             logger.info("could not find dpid: {}", node.getNodeId());
249             return;
250         }
251         String nodeName = OPENFLOW + getDpid(node);
252         NodeBuilder nodeBuilder = createNodeBuilder(nodeName);
253
254         // Create the OF Actions and Instructions
255         InstructionsBuilder isb = new InstructionsBuilder();
256
257         // Instructions List Stores Individual Instructions
258         List<Instruction> instructions = Lists.newArrayList();
259
260         // Call the InstructionBuilder Methods Containing Actions
261         InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
262         ib.setOrder(0);
263         ib.setKey(new InstructionKey(0));
264         instructions.add(ib.build());
265
266         // Add InstructionBuilder to the Instruction(s)Builder List
267         isb.setInstruction(instructions);
268
269         // Add InstructionsBuilder to FlowBuilder
270         flowBuilder.setInstructions(isb.build());
271
272         String flowId = "DEFAULT_PIPELINE_FLOW_"+service.getTable();
273         flowBuilder.setId(new FlowId(flowId));
274         FlowKey key = new FlowKey(new FlowId(flowId));
275         flowBuilder.setMatch(matchBuilder.build());
276         flowBuilder.setPriority(0);
277         flowBuilder.setBarrier(true);
278         flowBuilder.setTableId(service.getTable());
279         flowBuilder.setKey(key);
280         flowBuilder.setFlowName(flowId);
281         flowBuilder.setHardTimeout(0);
282         flowBuilder.setIdleTimeout(0);
283         writeFlow(flowBuilder, nodeBuilder);
284     }
285 }