2 * Copyright (C) 2014 Red Hat, Inc.
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
8 * Authors : Madhu Venugopal
10 package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13;
12 import java.util.List;
13 import java.util.concurrent.BlockingQueue;
14 import java.util.concurrent.ExecutionException;
15 import java.util.concurrent.LinkedBlockingDeque;
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.ReadOnlyTransaction;
20 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
21 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
22 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
25 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
27 import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
28 import org.opendaylight.ovsdb.utils.mdsal.openflow.InstructionUtils;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowId;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionKey;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorRemoved;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeConnectorUpdated;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRef;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeRemoved;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeUpdated;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.OpendaylightInventoryListener;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
52 import org.opendaylight.yangtools.yang.binding.DataObject;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.InstanceIdentifierBuilder;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
58 import com.google.common.base.Optional;
59 import com.google.common.base.Preconditions;
60 import com.google.common.collect.Lists;
61 import com.google.common.util.concurrent.CheckedFuture;
62 import com.google.common.util.concurrent.FutureCallback;
63 import com.google.common.util.concurrent.Futures;
66 * Any ServiceInstance class that extends AbstractServiceInstance to be a part of the pipeline
67 * have 2 basic requirements : <br>
68 * 1. Program a default pipeline flow to take any unmatched traffic to the next table in the pipeline. <br>
69 * 2. Get Pipeline Instructions from AbstractServiceInstance (using getMutablePipelineInstructionBuilder) and
70 * use it in any matching flows that needs to be further processed by next service in the pipeline.
73 public abstract class AbstractServiceInstance implements OpendaylightInventoryListener, Runnable, TransactionChainListener {
74 public static final String SERVICE_PROPERTY ="serviceProperty";
75 private static final Logger logger = LoggerFactory.getLogger(AbstractServiceInstance.class);
76 public static final String OPENFLOW = "openflow:";
77 // OSGi Services that we are dependent on.
78 private volatile MdsalConsumer mdsalConsumer;
79 private volatile PipelineOrchestrator orchestrator;
81 // Concrete Service that this AbstractServiceInstance represent
82 private Service service;
84 // Process Notification in its own thread
86 private final BlockingQueue<String> queue = new LinkedBlockingDeque<>();
88 public AbstractServiceInstance (Service service) {
89 this.service = service;
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);
95 public short getTable() {
96 return service.getTable();
99 public Service getService() {
103 public void setService(Service service) {
104 this.service = service;
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);
114 // Never block a Notification thread. Process the notification in its own Thread.
115 thread = new Thread(this);
116 thread.setDaemon(true);
117 thread.setName("AbstractServiceInstance-"+service.toString());
121 public NodeBuilder createNodeBuilder(String nodeId) {
122 NodeBuilder builder = new NodeBuilder();
123 builder.setId(new NodeId(nodeId));
124 builder.setKey(new NodeKey(builder.getId()));
129 * This method returns the required Pipeline Instructions to by used by any matching flows that needs
130 * to be further processed by next service in the pipeline.
132 * Important to note that this is a convenience method which returns a mutable instructionBuilder which
133 * needs to be further adjusted by the concrete ServiceInstance class such as setting the Instruction Order, etc.
134 * @return Newly created InstructionBuilder to be used along with other instructions on the main flow
136 protected final InstructionBuilder getMutablePipelineInstructionBuilder() {
137 Service nextService = orchestrator.getNextServiceInPipeline(service);
138 if (nextService != null) {
139 return InstructionUtils.createGotoTableInstructions(new InstructionBuilder(), nextService.getTable());
141 return InstructionUtils.createDropInstructions(new InstructionBuilder());
145 protected void writeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
146 Preconditions.checkNotNull(mdsalConsumer);
147 if (mdsalConsumer == null) {
148 logger.error("ERROR finding MDSAL Service. Its possible that writeFlow is called too soon ?");
152 DataBroker dataBroker = mdsalConsumer.getDataBroker();
153 if (dataBroker == null) {
154 logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
158 ReadWriteTransaction modification = dataBroker.newReadWriteTransaction();
159 InstanceIdentifier<Flow> path1 = InstanceIdentifier.builder(Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory
160 .rev130819.nodes.Node.class, nodeBuilder.getKey()).augmentation(FlowCapableNode.class).child(Table.class,
161 new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()).build();
163 //modification.put(LogicalDatastoreType.OPERATIONAL, path1, flowBuilder.build());
164 modification.put(LogicalDatastoreType.CONFIGURATION, path1, flowBuilder.build(), true /*createMissingParents*/);
167 CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
169 commitFuture.get(); // TODO: Make it async (See bug 1362)
170 logger.debug("Transaction success for write of Flow "+flowBuilder.getFlowName());
171 } catch (InterruptedException|ExecutionException e) {
172 logger.error(e.getMessage(), e);
177 protected void removeFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
178 Preconditions.checkNotNull(mdsalConsumer);
179 if (mdsalConsumer == null) {
180 logger.error("ERROR finding MDSAL Service.");
184 DataBroker dataBroker = mdsalConsumer.getDataBroker();
185 if (dataBroker == null) {
186 logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
190 WriteTransaction modification = dataBroker.newWriteOnlyTransaction();
191 InstanceIdentifier<Flow> path1 = InstanceIdentifier.builder(Nodes.class)
192 .child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory
193 .rev130819.nodes.Node.class, nodeBuilder.getKey())
194 .augmentation(FlowCapableNode.class).child(Table.class,
195 new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()).build();
196 //modification.delete(LogicalDatastoreType.OPERATIONAL, nodeBuilderToInstanceId(nodeBuilder));
197 //modification.delete(LogicalDatastoreType.OPERATIONAL, path1);
198 //modification.delete(LogicalDatastoreType.CONFIGURATION, nodeBuilderToInstanceId(nodeBuilder));
199 modification.delete(LogicalDatastoreType.CONFIGURATION, path1);
201 CheckedFuture<Void, TransactionCommitFailedException> commitFuture = modification.submit();
203 commitFuture.get(); // TODO: Make it async (See bug 1362)
204 logger.debug("Transaction success for deletion of Flow "+flowBuilder.getFlowName());
205 } catch (InterruptedException|ExecutionException e) {
206 logger.error(e.getMessage(), e);
210 public Flow getFlow(FlowBuilder flowBuilder, NodeBuilder nodeBuilder) {
211 Preconditions.checkNotNull(mdsalConsumer);
212 if (mdsalConsumer == null) {
213 logger.error("ERROR finding MDSAL Service. Its possible that writeFlow is called too soon ?");
217 DataBroker dataBroker = mdsalConsumer.getDataBroker();
218 if (dataBroker == null) {
219 logger.error("ERROR finding reference for DataBroker. Please check MD-SAL support on the Controller.");
223 InstanceIdentifier<Flow> path1 = InstanceIdentifier.builder(Nodes.class).child(org.opendaylight.yang.gen.v1.urn.opendaylight.inventory
224 .rev130819.nodes.Node.class, nodeBuilder.getKey()).augmentation(FlowCapableNode.class).child(Table.class,
225 new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()).build();
227 ReadOnlyTransaction readTx = dataBroker.newReadOnlyTransaction();
229 Optional<Flow> data = readTx.read(LogicalDatastoreType.CONFIGURATION, path1).get();
230 if (data.isPresent()) {
233 } catch (InterruptedException|ExecutionException e) {
234 logger.error(e.getMessage(), e);
237 logger.debug("Cannot find data for Flow " + flowBuilder.getFlowName());
242 * Program Default Pipeline Flow.
244 * @param nodeId Node on which the default pipeline flow is programmed.
246 protected void programDefaultPipelineRule(String nodeId) {
247 MatchBuilder matchBuilder = new MatchBuilder();
248 FlowBuilder flowBuilder = new FlowBuilder();
249 NodeBuilder nodeBuilder = createNodeBuilder(nodeId);
251 // Create the OF Actions and Instructions
252 InstructionsBuilder isb = new InstructionsBuilder();
254 // Instructions List Stores Individual Instructions
255 List<Instruction> instructions = Lists.newArrayList();
257 // Call the InstructionBuilder Methods Containing Actions
258 InstructionBuilder ib = this.getMutablePipelineInstructionBuilder();
260 ib.setKey(new InstructionKey(0));
261 instructions.add(ib.build());
263 // Add InstructionBuilder to the Instruction(s)Builder List
264 isb.setInstruction(instructions);
266 // Add InstructionsBuilder to FlowBuilder
267 flowBuilder.setInstructions(isb.build());
269 String flowId = "DEFAULT_PIPELINE_FLOW";
270 flowBuilder.setId(new FlowId(flowId));
271 FlowKey key = new FlowKey(new FlowId(flowId));
272 flowBuilder.setMatch(matchBuilder.build());
273 flowBuilder.setPriority(0);
274 flowBuilder.setBarrier(true);
275 flowBuilder.setTableId(service.getTable());
276 flowBuilder.setKey(key);
277 flowBuilder.setFlowName(flowId);
278 flowBuilder.setHardTimeout(0);
279 flowBuilder.setIdleTimeout(0);
280 writeFlow(flowBuilder, nodeBuilder);
284 public void onNodeConnectorRemoved(NodeConnectorRemoved nodeConector) {
288 public void onNodeConnectorUpdated(NodeConnectorUpdated nodeConnector) {
292 public void onNodeRemoved(NodeRemoved node) {
300 String nodeId = queue.take();
301 this.programDefaultPipelineRule(nodeId);
303 } catch (InterruptedException e) {
304 logger.warn("Processing interrupted, terminating", e);
307 while (!queue.isEmpty()) {
313 void enqueue(final String nodeId) {
316 } catch (InterruptedException e) {
317 logger.warn("Failed to enqueue operation {}", nodeId, e);
322 * Process the Node update notification. Check for Openflow node and make sure if the bridge is part of the Pipeline before
323 * programming the Pipeline specific flows.
326 public void onNodeUpdated(NodeUpdated nodeUpdated) {
327 NodeRef ref = nodeUpdated.getNodeRef();
328 InstanceIdentifier<Node> identifier = (InstanceIdentifier<Node>) ref.getValue();
329 logger.info("GOT NOTIFICATION FOR "+identifier.toString());
330 final NodeKey key = identifier.firstKeyOf(Node.class, NodeKey.class);
331 final String nodeId = key.getId().getValue();
332 if (!this.isBridgeInPipeline(nodeId)) {
333 logger.debug("Bridge {} is not in pipeline", nodeId);
336 if (key != null && key.getId().getValue().contains("openflow")) {
337 InstanceIdentifierBuilder<Node> builder = ((InstanceIdentifier<Node>) ref.getValue()).builder();
338 InstanceIdentifierBuilder<FlowCapableNode> augmentation = builder.augmentation(FlowCapableNode.class);
339 final InstanceIdentifier<FlowCapableNode> path = augmentation.build();
340 BindingTransactionChain txChain = mdsalConsumer.getDataBroker().createTransactionChain(this);
341 CheckedFuture readFuture = txChain.newReadWriteTransaction().read(LogicalDatastoreType.OPERATIONAL, path);
342 Futures.addCallback(readFuture, new FutureCallback<Optional<? extends DataObject>>() {
344 public void onSuccess(Optional<? extends DataObject> optional) {
345 if (!optional.isPresent()) {
351 public void onFailure(Throwable throwable) {
352 logger.debug(String.format("Can't retrieve node data for node %s. Writing node data with table0.", nodeId));
360 public void onTransactionChainFailed(final TransactionChain<?, ?> chain, final AsyncTransaction<?, ?> transaction,
361 final Throwable cause) {
362 logger.error("Failed to export Flow Capable Inventory, Transaction {} failed.",transaction.getIdentifier(),cause);
366 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {