Merge "Making changes to LBaaSHandler and Activator to add logic for LB Neutron calls."
[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.concurrent.BlockingQueue;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.LinkedBlockingDeque;
16
17 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
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.AsyncTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
25 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
26 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
27 import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
51 import org.opendaylight.yangtools.yang.binding.DataObject;
52 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 import com.google.common.base.Optional;
58 import com.google.common.base.Preconditions;
59 import com.google.common.collect.Lists;
60 import com.google.common.util.concurrent.CheckedFuture;
61 import com.google.common.util.concurrent.FutureCallback;
62 import com.google.common.util.concurrent.Futures;
63
64 /**
65  * Any ServiceInstance class that extends AbstractServiceInstance to be a part of the pipeline
66  * have 2 basic requirements : <br>
67  * 1. Program a default pipeline flow to take any unmatched traffic to the next table in the pipeline. <br>
68  * 2. Get Pipeline Instructions from AbstractServiceInstance (using getMutablePipelineInstructionBuilder) and
69  *    use it in any matching flows that needs to be further processed by next service in the pipeline.
70  *
71  */
72 public abstract class AbstractServiceInstance implements OpendaylightInventoryListener, Runnable, TransactionChainListener {
73     public static final String SERVICE_PROPERTY ="serviceProperty";
74     private static final Logger logger = LoggerFactory.getLogger(AbstractServiceInstance.class);
75
76     // OSGi Services that we are dependent on.
77     private volatile MdsalConsumer mdsalConsumer;
78     private volatile PipelineOrchestrator orchestrator;
79
80     // Concrete Service that this AbstractServiceInstance represent
81     private Service service;
82
83     private BindingTransactionChain txChain;
84
85     // Process Notification in its own thread
86     Thread thread = null;
87     private final BlockingQueue<String> queue = new LinkedBlockingDeque<>();
88
89     public AbstractServiceInstance (Service service) {
90         this.service = service;
91     }
92
93     // Let the Concrete service instance class decide if a Bride is part of the pipeline or not.
94     public abstract boolean isBridgeInPipeline (String nodeId);
95
96     public short getTable() {
97         return service.getTable();
98     }
99
100     public Service getService() {
101         return service;
102     }
103
104     public void setService(Service service) {
105         this.service = service;
106     }
107
108     public void start() {
109         // Register for OpenFlow bridge/node Creation notification.
110         NotificationProviderService notificationService = mdsalConsumer.getNotificationService();
111         if (notificationService != null) {
112             notificationService.registerNotificationListener(this);
113         }
114         this.txChain =  mdsalConsumer.getDataBroker().createTransactionChain(this);
115
116         // Never block a Notification thread. Process the notification in its own Thread.
117         thread = new Thread(this);
118         thread.setDaemon(true);
119         thread.setName("AbstractServiceInstance-"+service.toString());
120         thread.start();
121     }
122
123     private NodeBuilder createNodeBuilder(String nodeId) {
124         NodeBuilder builder = new NodeBuilder();
125         builder.setId(new NodeId(nodeId));
126         builder.setKey(new NodeKey(builder.getId()));
127         return builder;
128     }
129
130     /**
131      * This method returns the required Pipeline Instructions to by used by any matching flows that needs
132      * to be further processed by next service in the pipeline.
133      *
134      * Important to note that this is a convenience method which returns a mutable instructionBuilder which
135      * needs to be further adjusted by the concrete ServiceInstance class such as setting the Instruction Order, etc.
136      * @return Newly created InstructionBuilder to be used along with other instructions on the main flow
137      */
138     protected final InstructionBuilder getMutablePipelineInstructionBuilder() {
139         Service nextService = orchestrator.getNextServiceInPipeline(service);
140         if (nextService != null) {
141             return InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), nextService.getTable());
142         } else {
143             return InstructionUtils.createDropInstructions(new InstructionBuilder());
144         }
145     }
146
147     protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
148         Preconditions.checkNotNull(mdsalConsumer);
149         if (mdsalConsumer == null) {
150             logger.error("ERROR finding MDSAL Service. Its possible that writeFlow is called too soon ?");
151             return;
152         }
153
154         DataBroker dataBroker = mdsalConsumer.getDataBroker();
155         if (dataBroker == null) {
156             logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
157             return;
158         }
159
160         ReadWriteTransaction modification = dataBroker.newReadWriteTransaction();
161         InstanceIdentifier<Flow> path1 = InstanceIdentifier.builder(Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory
162                 .rev130819.nodes.Node.class, nodeBuilder.getKey()).augmentation(FlowCapableNode.class).child(Table.class,
163                 new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()).build();
164
165         //modification.put(LogicalDatastoreType.OPERATIONAL, path1, flowBuilder.build());
166         modification.put(LogicalDatastoreType.CONFIGURATION, path1, flowBuilder.build(), true /*createMissingParents*/);
167
168
169         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
170         try {
171             commitFuture.get();  // TODO: Make it async (See bug 1362)
172             logger.debug("Transaction success for write of Flow "+flowBuilder.getFlowName());
173         } catch (InterruptedException|ExecutionException e) {
174             logger.error(e.getMessage(), e);
175
176         }
177     }
178
179     protected void removeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
180         Preconditions.checkNotNull(mdsalConsumer);
181         if (mdsalConsumer == null) {
182             logger.error("ERROR finding MDSAL Service.");
183             return;
184         }
185
186         DataBroker dataBroker = mdsalConsumer.getDataBroker();
187         if (dataBroker == null) {
188             logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
189             return;
190         }
191
192         WriteTransaction modification = dataBroker.newWriteOnlyTransaction();
193         InstanceIdentifier<Flow> path1 = InstanceIdentifier.builder(Nodes.class)
194                 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory
195                                .rev130819.nodes.Node.class, nodeBuilder.getKey())
196                 .augmentation(FlowCapableNode.class).child(Table.class,
197                                                            new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()).build();
198         //modification.delete(LogicalDatastoreType.OPERATIONAL, nodeBuilderToInstanceId(nodeBuilder));
199         //modification.delete(LogicalDatastoreType.OPERATIONAL, path1);
200         //modification.delete(LogicalDatastoreType.CONFIGURATION, nodeBuilderToInstanceId(nodeBuilder));
201         modification.delete(LogicalDatastoreType.CONFIGURATION, path1);
202
203         CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
204         try {
205             commitFuture.get();  // TODO: Make it async (See bug 1362)
206             logger.debug("Transaction success for deletion of Flow "+flowBuilder.getFlowName());
207         } catch (InterruptedException|ExecutionException e) {
208             logger.error(e.getMessage(), e);
209         }
210     }
211
212     /**
213      * Program Default Pipeline Flow.
214      *
215      * @param nodeId Node on which the default pipeline flow is programmed.
216      */
217     protected void programDefaultPipelineRule(String nodeId) {
218         MatchBuilder matchBuilder = new MatchBuilder();
219         FlowBuilder flowBuilder = new FlowBuilder();
220         NodeBuilder nodeBuilder = createNodeBuilder(nodeId);
221
222         // Create the OF Actions and Instructions
223         InstructionsBuilder isb = new InstructionsBuilder();
224
225         // Instructions List Stores Individual Instructions
226         List<Instruction> instructions = Lists.newArrayList();
227
228         // Call the InstructionBuilder Methods Containing Actions
229         InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
230         ib.setOrder(0);
231         ib.setKey(new InstructionKey(0));
232         instructions.add(ib.build());
233
234         // Add InstructionBuilder to the Instruction(s)Builder List
235         isb.setInstruction(instructions);
236
237         // Add InstructionsBuilder to FlowBuilder
238         flowBuilder.setInstructions(isb.build());
239
240         String flowId = "DEFAULT_PIPELINE_FLOW";
241         flowBuilder.setId(new FlowId(flowId));
242         FlowKey key = new FlowKey(new FlowId(flowId));
243         flowBuilder.setMatch(matchBuilder.build());
244         flowBuilder.setPriority(0);
245         flowBuilder.setBarrier(true);
246         flowBuilder.setTableId(service.getTable());
247         flowBuilder.setKey(key);
248         flowBuilder.setFlowName(flowId);
249         flowBuilder.setHardTimeout(0);
250         flowBuilder.setIdleTimeout(0);
251         writeFlow(flowBuilder, nodeBuilder);
252     }
253
254     @Override
255     public void onNodeConnectorRemoved(NodeConnectorRemoved nodeConector) {
256     }
257
258     @Override
259     public void onNodeConnectorUpdated(NodeConnectorUpdated nodeConnector) {
260     }
261
262     @Override
263     public void onNodeRemoved(NodeRemoved node) {
264     }
265
266
267     @Override
268     public void run() {
269         try {
270             for (; ; ) {
271                 String nodeId = queue.take();
272                 this.programDefaultPipelineRule(nodeId);
273             }
274         } catch (InterruptedException e) {
275             logger.warn("Processing interrupted, terminating", e);
276         }
277
278         while (!queue.isEmpty()) {
279             queue.poll();
280         }
281
282     }
283
284     void enqueue(final String nodeId) {
285         try {
286             queue.put(nodeId);
287         } catch (InterruptedException e) {
288             logger.warn("Failed to enqueue operation {}", nodeId, e);
289         }
290     }
291
292     /**
293      * Process the Node update notification. Check for Openflow node and make sure if the bridge is part of the Pipeline before
294      * programming the Pipeline specific flows.
295      */
296     @Override
297     public void onNodeUpdated(NodeUpdated nodeUpdated) {
298         NodeRef ref = nodeUpdated.getNodeRef();
299         InstanceIdentifier<Node> identifier = (InstanceIdentifier<Node>) ref.getValue();
300         logger.info("GOT NOTIFICATION FOR "+identifier.toString());
301         final NodeKey key = identifier.firstKeyOf(Node.class, NodeKey.class);
302         final String nodeId = key.getId().getValue();
303         if (!this.isBridgeInPipeline(nodeId)) {
304             logger.debug("Bridge {} is not in pipeline", nodeId);
305             return;
306         }
307         if (key != null && key.getId().getValue().contains("openflow")) {
308             InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) ref.getValue()).builder();
309             InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder.augmentation(FlowCapableNode.class);
310             final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
311             CheckedFuture readFuture = txChain.newReadWriteTransaction().read(LogicalDatastoreType.OPERATIONAL, path);
312             Futures.addCallback(readFuture, new FutureCallback<Optional<? extends DataObject>>() {
313                 @Override
314                 public void onSuccess(Optional<? extends DataObject> optional) {
315                     if (!optional.isPresent()) {
316                         enqueue(nodeId);
317                     }
318                 }
319
320                 @Override
321                 public void onFailure(Throwable throwable) {
322                     logger.debug(String.format("Can't retrieve node data for node %s. Writing node data with table0.", nodeId));
323                     enqueue(nodeId);
324                 }
325             });
326         }
327     }
328
329     @Override
330     public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
331             final Throwable cause) {
332         logger.error("Failed to export Flow Capable Inventory, Transaction {} failed.",transaction.getIdentifier(),cause);
333     }
334
335     @Override
336     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
337     }
338 }