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