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