/* * Copyright (c) 2015 - 2016 Hewlett-Packard Development Company, L.P. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.nic.of.renderer.impl; import java.util.ArrayList; import java.util.List; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.nic.of.renderer.utils.FlowUtils; import org.opendaylight.nic.pipeline_manager.PipelineManager; import org.opendaylight.nic.utils.FlowAction; import org.opendaylight.nic.utils.MdsalUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Dscp; import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNode; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.Table; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.TableKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.Flow; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.OutputPortValues; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.Instructions; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ClearActionsCaseBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActions; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.clear.actions._case.ClearActionsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.Instruction; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public abstract class AbstractFlowManager { /** * Logger instance. */ private static final Logger LOG = LoggerFactory.getLogger(AbstractFlowManager.class); protected final DataBroker dataBroker; private final PipelineManager pipelineManager; private static final String L2_Rule_NAME = "L2_Rule_"; AbstractFlowManager(DataBroker dataBroker, PipelineManager pipelineManager) { this.dataBroker = dataBroker; this.pipelineManager = pipelineManager; } protected abstract String createFlowName(); /** * Programming a flow involves: 1. Creating a Flow object that has a match * and a list of instructions, 2. Adding Flow object as an augmentation to * the Node object in the inventory. 3. FlowProgrammer module of * OpenFlowPlugin picks up this data change and eventually program the * switch. * @param nodeId The OpenDaylight Inventory OpenFlow {@link NodeId} * @param flowAction The {@link FlowAction} */ abstract void pushFlow(NodeId nodeId, FlowAction flowAction); /** * Creates a set of Instruction based on the port values * received. * @param portValues Represents ports (example LOCAL, CONTROLLER, etc) {@link OutputPortValues}} * @return OpenFlow Flow Instructions */ protected Instructions createOutputInstructions(OutputPortValues... portValues) { List<Action> actionList = Lists.newArrayList(); int order = 0; for (OutputPortValues outputPort : portValues) { if (outputPort.equals(OutputPortValues.NORMAL)) { actionList.add(FlowUtils.createOutputNormal(order)); order++; } if (outputPort.equals(OutputPortValues.CONTROLLER)) { actionList.add(FlowUtils.createSendToControllerAction(order)); order++; } } ApplyActions applyOutputActions = new ApplyActionsBuilder().setAction(actionList).build(); Instruction outputInstruction = new InstructionBuilder().setOrder(0) .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyOutputActions).build()).build(); Instructions instructions = new InstructionsBuilder().setInstruction(ImmutableList.of(outputInstruction)) .build(); return instructions; } /** * Creates a set of redirect Instruction based on the port values * received. * @param outputPort OVS port to output the packet to * @return OpenFlow Flow Instructions */ protected Instructions createRedirectIntentInstructions(String outputPort) { List<Action> actionList = Lists.newArrayList(); int order = 0; actionList.add(FlowUtils.createOutputToPort(order++, outputPort)); ApplyActions applyRedirectActions = new ApplyActionsBuilder().setAction(actionList).build(); Instruction redirectInstruction = new InstructionBuilder().setOrder(0) .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyRedirectActions).build()).build(); Instructions instructions = new InstructionsBuilder().setInstruction(ImmutableList.of(redirectInstruction)).build(); return instructions; } /** * To create MPLS VPN intents three actions are pushed for a match made * 1. push_mpls or pop_mpls action * 2. set_field to mpls label * 3. output to a switch port * @param labels List of MPLS labels * @param popLabel Boolean for pop action * @param bos Bottom of Stack value * @param outputPort OVS port to output the packet to * @param forward Boolean for forward MPLS packet action * @return A set of OpenFlow {@link Instructions} that have been construction */ protected Instructions createMPLSIntentInstructions(List<Long> labels, boolean popLabel, Short bos, String outputPort, boolean forward) { return createMPLSIntentInstructions(labels, popLabel, bos, outputPort, forward, null); } /** * To create MPLS VPN intents three actions are pushed for a match made * 1. push_mpls or pop_mpls action * 2. set_field to mpls label * 3. output to a switch port * @param labels List of MPLS labels * @param popLabel Boolean for pop action * @param bos Bottom of Stack value * @param outputPort OVS port to output the packet to * @param forward Boolean for forward MPLS packet action * @param macAddress Server destination MAC Address * @return A set of OpenFlow {@link Instructions} that have been construction */ protected Instructions createMPLSIntentInstructions(List<Long> labels, boolean popLabel, Short bos, String outputPort, boolean forward, String macAddress) { int order = 0; List<Action> actionList = new ArrayList<>(); if(!forward) { for (Long labelValue : labels) { actionList.add(FlowUtils.createMPLSAction(order++, popLabel)); if (!popLabel) { actionList.add(FlowUtils.createSetFieldMPLSLabelAction(order++, labelValue, bos)); } else if (macAddress != null){ actionList.add(FlowUtils.createSetFieldDestinationMacAddress(order++, macAddress)); } } } actionList.add(FlowUtils.createOutputToPort(order++, outputPort)); ApplyActions applyMplsActions = new ApplyActionsBuilder().setAction(actionList).build(); Instruction mplsInstruction = new InstructionBuilder().setOrder(0) .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyMplsActions).build()).build(); Instructions instructions = new InstructionsBuilder().setInstruction(ImmutableList.of(mplsInstruction)).build(); return instructions; } /** * Creates a set of clear instructions to remove flows from switch * @return Instructions */ protected Instructions createClearFlowsInstructions() { Instruction clearInstruction = new InstructionBuilder().setOrder(0) .setInstruction(new ClearActionsCaseBuilder().setClearActions(new ClearActionsBuilder().build()).build()).build(); Instructions instructions = new InstructionsBuilder().setInstruction(ImmutableList.of(clearInstruction)) .build(); return instructions; } /** * Writes a Flow with a flow action on the Configuration * data store so that it can be applied to an OF switch. * @param nodeId The {@link NodeId} of the OF Switch * @param flowBuilder The {@link FlowBuilder} that is built and submitted * @param flowAction The {@link FlowAction} the flow action (ADD or REMOVE) * @return A boolean representing the transaction result */ protected boolean writeDataTransaction(NodeId nodeId, FlowBuilder flowBuilder, FlowAction flowAction) { boolean result = false; final NodeBuilder nodeBuilder = new NodeBuilder(); final FlowKey flowKey = new FlowKey(flowBuilder.getKey()); nodeBuilder.setId(nodeId); nodeBuilder.setKey(new NodeKey(nodeBuilder.getId())); MdsalUtils mdsal = new MdsalUtils(dataBroker); if (!pipelineManager.setTableId(nodeId, flowBuilder)) { flowBuilder.setTableId(OFRendererConstants.FALLBACK_TABLE_ID); } final TableKey tableKey = new TableKey(flowBuilder.getTableId()); InstanceIdentifier<Flow> flowIID = InstanceIdentifier.builder(Nodes.class) .child(Node.class, nodeBuilder.getKey()) .augmentation(FlowCapableNode.class) .child(Table.class, tableKey) .child(Flow.class, flowKey) .build(); if (flowAction == FlowAction.ADD_FLOW) { result = mdsal.put(LogicalDatastoreType.CONFIGURATION, flowIID, flowBuilder.build()); } else if(flowAction == FlowAction.REMOVE_FLOW) { result = mdsal.delete(LogicalDatastoreType.CONFIGURATION, flowIID); } return result; } /** * Creates a set of Instruction based on the port values and DSCP * received. * @param portValues Represents ports (example LOCAL, CONTROLLER, etc) {@link OutputPortValues}} * @param dscp Dscp Value. * @return OpenFlow Flow Instructions */ protected Instructions createQoSInstructions(Dscp dscp, OutputPortValues... portValues) { List<Action> actionList = Lists.newArrayList(); int order = 0; for (OutputPortValues outputPort : portValues) { if (outputPort.equals(OutputPortValues.NORMAL)) { actionList.add(FlowUtils.createOutputNormal(order)); order++; actionList.add(FlowUtils.createQosNormal(order, dscp)); order++; } if (outputPort.equals(OutputPortValues.CONTROLLER)) { actionList.add(FlowUtils.createSendToControllerAction(order)); order++; } } ApplyActions applyOutputActions = new ApplyActionsBuilder().setAction(actionList).build(); Instruction outputInstruction = new InstructionBuilder().setOrder(0) .setInstruction(new ApplyActionsCaseBuilder().setApplyActions(applyOutputActions).build()).build(); Instructions instructions = new InstructionsBuilder().setInstruction(ImmutableList.of(outputInstruction)) .build(); return instructions; } /** * Read the flow id from the data store. * @param nodeId The {@link NodeId} of the OF Switch * @param flowBuilder The {@link FlowBuilder} that is built and submitted * @return flowId Flow Id. */ protected String readDataTransaction(NodeId nodeId, FlowBuilder flowBuilder) { MdsalUtils mdsal = new MdsalUtils(dataBroker); InstanceIdentifier<Table> tableIID = InstanceIdentifier .builder(Nodes.class) .child(Node.class, new NodeKey(nodeId)).augmentation(FlowCapableNode.class) .child(Table.class, new TableKey(OFRendererConstants.FALLBACK_TABLE_ID)).build(); Table table = mdsal.read(LogicalDatastoreType.CONFIGURATION, tableIID); String destination = flowBuilder.getMatch().getEthernetMatch().getEthernetDestination().getAddress().getValue(); String source = flowBuilder.getMatch().getEthernetMatch().getEthernetSource().getAddress().getValue(); String logFlowId = L2_Rule_NAME + source + destination; if (table != null) { List<Flow> flows = table.getFlow(); for (Flow flow : flows) { String flowId = null; flowId = flow.getId().getValue().toString(); if (flowId.contains(logFlowId)) { return flowId; } } } LOG.info("Flow ID doesn't match with source{} and destination{}", source, destination); return null; } }