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