/** * Copyright (c) 2015 Hewlett Packard Enterprise Development LP 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.pipeline_manager; import com.google.common.base.Optional; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.ReadTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker; import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.match.MatchConvertorImpl; 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.FlowId; 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.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.flow.Match; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.GoToTableCaseBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.WriteActionsCase; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.go.to.table._case.GoToTableBuilder; 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.NodeKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.MatchField; import org.opendaylight.yang.gen.v1.urn.opendaylight.openflow.oxm.rev150225.match.entries.grouping.MatchEntry; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.set.field.match.SetFieldMatch; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.NextTableMiss; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.TableFeatures; import org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.features.table.features.table.properties.TableFeatureProperties; import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; public class PipelineManagerProviderImpl implements DataChangeListener, PipelineManager { private static final Logger LOG = LoggerFactory.getLogger(PipelineManagerProviderImpl.class); private DataBroker dataBroker; private ListenerRegistration<DataChangeListener> nodesListener; public PipelineManagerProviderImpl(DataBroker dataBroker) { this.dataBroker = dataBroker; final InstanceIdentifier<Nodes> nodesIdentifier = InstanceIdentifier.create(Nodes.class); nodesListener = dataBroker.registerDataChangeListener( LogicalDatastoreType.OPERATIONAL, nodesIdentifier, this, AsyncDataBroker.DataChangeScope.SUBTREE); // FIXME: Try to listen to the Node changes only instead of subtree LOG.info("new Pipeline Manager created: {}", this); } @Override public void close() throws Exception { nodesListener.close(); LOG.info("Pipeline Manager destroyed: {}", this); } @Override public void onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) { Map<InstanceIdentifier<?>, DataObject> createdData = change.getCreatedData(); for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : createdData.entrySet()) { if (entry.getValue() instanceof Node) { Node node = (Node) entry.getValue(); createPipeline(node); } } } private void createPipeline(Node node) { List<Table> tableList = getTableList(node); for (Table table : tableList) { List<Short> nextIds = getNextTablesMiss(node.getId(), table.getId()); if (nextIds.isEmpty()) break; Short nextId = Collections.min(nextIds); Short currentId = table.getId(); addFlowGoto(node, currentId, nextId); } } private void addFlowGoto(Node node, Short currentId, Short nextId) { FlowBuilder flowBuilder = new FlowBuilder(); flowBuilder.setTableId(currentId); flowBuilder.setHardTimeout(0); flowBuilder.setPriority(0); flowBuilder.setMatch(new MatchBuilder().build()); flowBuilder.setInstructions( new InstructionsBuilder().setInstruction(Collections.singletonList( new InstructionBuilder().setInstruction( new GoToTableCaseBuilder().setGoToTable( new GoToTableBuilder().setTableId(nextId).build() ).build() ).setOrder(0).build() )).build()); String flowIdStr = "PipelineManager"; final FlowId flowId = new FlowId(flowIdStr); final FlowKey key = new FlowKey(flowId); flowBuilder.setKey(key); InstanceIdentifier<Flow> flowIID = InstanceIdentifier.builder(Nodes.class) .child(Node.class, new NodeKey(node.getId())).augmentation(FlowCapableNode.class) .child(Table.class, new TableKey(flowBuilder.getTableId())).child(Flow.class, flowBuilder.getKey()) .build(); WriteTransaction transaction = dataBroker.newWriteOnlyTransaction(); transaction.put(LogicalDatastoreType.CONFIGURATION, flowIID, flowBuilder.build(), true); transaction.submit(); } private List<TableFeatureProperties> getTableFeatureProperties(final NodeId nodeId, final Short tableId) { Node node = getDataObject(dataBroker.newReadOnlyTransaction(), InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId))); if (node == null) { return Collections.emptyList(); } FlowCapableNode flowCapableNode = node.getAugmentation(FlowCapableNode.class); List<TableFeatures> features = flowCapableNode.getTableFeatures(); if (features == null || features.isEmpty()) { return Collections.emptyList(); } return features.get(tableId).getTableProperties().getTableFeatureProperties(); } private List<Short> getNextTablesMiss(final NodeId nodeId, final Short tableId) { for (TableFeatureProperties tableFeatureProperties : getTableFeatureProperties(nodeId, tableId)) { if (tableFeatureProperties.getTableFeaturePropType() instanceof NextTableMiss) { NextTableMiss nextTableMiss = (NextTableMiss) tableFeatureProperties.getTableFeaturePropType(); return nextTableMiss.getTablesMiss().getTableIds(); } } return Collections.emptyList(); } @Override public boolean setTableId(NodeId nodeId, FlowBuilder flowBuilder) { List<Table> tableList = getTableList(nodeId); for (Table table : tableList) { List<TableFeatureProperties> tableFeaturePropertiesList = getTableFeatureProperties(nodeId, table.getId()); if (isFlowSupported(tableFeaturePropertiesList, flowBuilder)) { flowBuilder.setTableId(table.getId()); return true; } } return false; } /* InventoryDataServiceUtil.getDataObject() */ private static <T extends DataObject> T getDataObject(final ReadTransaction readOnlyTransaction, final InstanceIdentifier<T> identifier) { Optional<T> optionalData = null; try { optionalData = readOnlyTransaction.read(LogicalDatastoreType.OPERATIONAL, identifier).get(); if (optionalData.isPresent()) { return optionalData.get(); } } catch (ExecutionException | InterruptedException e) { LOG.error("Read transaction for identifier {} failed.", identifier, e); } return null; } private List<Table> getTableList(NodeId nodeId) { Node node = PipelineManagerProviderImpl.getDataObject(dataBroker.newReadOnlyTransaction(), InstanceIdentifier.create(Nodes.class).child(Node.class, new NodeKey(nodeId))); return getTableList(node); } private List<Table> getTableList(Node node) { FlowCapableNode flowCapableNode = node.getAugmentation(FlowCapableNode.class); List<Table> tableList = flowCapableNode.getTable(); Collections.sort(tableList, new Comparator<Table>() { @Override public int compare(Table o1, Table o2) { return o1.getId().compareTo(o2.getId()); } }); return tableList; } private boolean isFlowSupported(List<TableFeatureProperties> tableFeaturePropertiesList, FlowBuilder flowBuilder) { Instructions flowBuilderInstructions = flowBuilder.getInstructions(); if (flowBuilderInstructions == null) { return false; } List<SetFieldMatch> matchList = getMatchList(tableFeaturePropertiesList); return isMatchSupported(matchList, flowBuilder.getMatch()) && isInstructionsSupported(tableFeaturePropertiesList, flowBuilderInstructions.getInstruction()); } private boolean isInstructionsSupported(List<TableFeatureProperties> tableFeaturePropertiesList, List<Instruction> instructions) { for (Instruction instruction : instructions) { if (!isInstructionSupported(tableFeaturePropertiesList, instruction)) return false; } return true; } private boolean isInstructionSupported(List<TableFeatureProperties> tableFeaturePropertiesList, Instruction instruction) { List<Instruction> supportedInstructions = getInstructionList(tableFeaturePropertiesList); for (Instruction supportedInstructionProxy : supportedInstructions) { org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction supportedInstruction = supportedInstructionProxy.getInstruction(); if (instruction.getInstruction().getImplementedInterface().equals(supportedInstruction.getImplementedInterface())) { if (instruction.getInstruction() instanceof ApplyActionsCase) { ApplyActionsCase applyActionsCase = (ApplyActionsCase) instruction.getInstruction(); List<Action> supportedApplyActions = getApplyActionList(tableFeaturePropertiesList); for (Action action : applyActionsCase.getApplyActions().getAction()) { if (!isActionSupported(supportedApplyActions, action)) { return false; } } if (instruction.getInstruction() instanceof WriteActionsCase) { WriteActionsCase writeActionsCase = (WriteActionsCase) instruction.getInstruction(); List<Action> supportedWriteActions = getWriteActionList(tableFeaturePropertiesList); for (Action action : writeActionsCase.getWriteActions().getAction()) { if (!isActionSupported(supportedWriteActions, action)) { return false; } } } } return true; } } return false; } private boolean isActionSupported(List<Action> supportedApplyActions, Action action) { for (Action supportedApplyAction : supportedApplyActions) { if (supportedApplyAction.getAction().getImplementedInterface().equals(action.getAction().getImplementedInterface())) return true; } return false; } private boolean isFieldSupported(Class<? extends MatchField> field, List<SetFieldMatch> supportedFields) { for (SetFieldMatch supportedField : supportedFields) { if (isFieldMatch(field, supportedField.getMatchType())) return true; } return false; } private boolean isFieldMatch(Class<? extends MatchField> field, Class<? extends org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.MatchField> matchType) { return field.getSimpleName().equals(matchType.getSimpleName()); } private boolean isMatchSupported(List<SetFieldMatch> supportedMatchList, Match match) { MatchConvertorImpl matchConvertor = new MatchConvertorImpl(); List<MatchEntry> matchEntryList = matchConvertor.convert(match, null); for (MatchEntry matchEntry : matchEntryList) { if (!isFieldSupported(matchEntry.getOxmMatchField(), supportedMatchList)) return false; } return true; } private List<SetFieldMatch> getMatchList(List<TableFeatureProperties> tableFeaturePropertiesList) { for (TableFeatureProperties tableFeatureProperties : tableFeaturePropertiesList) { if (tableFeatureProperties.getTableFeaturePropType() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Match) { org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Match match = (org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Match) tableFeatureProperties.getTableFeaturePropType(); return match.getMatchSetfield().getSetFieldMatch(); } } return Collections.emptyList(); } private List<Instruction> getInstructionList(List<TableFeatureProperties> tableFeaturePropertiesList) { for (TableFeatureProperties tableFeatureProperties : tableFeaturePropertiesList) { if (tableFeatureProperties.getTableFeaturePropType() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Instructions) { org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Instructions instructions = (org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.Instructions) tableFeatureProperties.getTableFeaturePropType(); return instructions.getInstructions().getInstruction(); } } return Collections.emptyList(); } private List<Action> getApplyActionList(List<TableFeatureProperties> tableFeaturePropertiesList) { for (TableFeatureProperties tableFeatureProperties : tableFeaturePropertiesList) { if (tableFeatureProperties.getTableFeaturePropType() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplyActions) { org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplyActions actions = (org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.ApplyActions) tableFeatureProperties.getTableFeaturePropType(); return actions.getApplyActions().getAction(); } } return Collections.emptyList(); } private List<Action> getWriteActionList(List<TableFeatureProperties> tableFeaturePropertiesList) { for (TableFeatureProperties tableFeatureProperties : tableFeaturePropertiesList) { if (tableFeatureProperties.getTableFeaturePropType() instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteActions) { org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteActions actions = (org.opendaylight.yang.gen.v1.urn.opendaylight.table.types.rev131026.table.feature.prop.type.table.feature.prop.type.WriteActions) tableFeatureProperties.getTableFeaturePropType(); return actions.getWriteActions().getAction(); } } return Collections.emptyList(); } }