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