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