/* * 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 org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.sal.binding.api.NotificationProviderService; import org.opendaylight.nic.mapping.api.IntentMappingService; import org.opendaylight.nic.of.renderer.api.OFRendererFlowService; import org.opendaylight.nic.of.renderer.api.OFRendererGraphService; import org.opendaylight.nic.of.renderer.api.Observer; import org.opendaylight.nic.of.renderer.api.Subject; import org.opendaylight.nic.of.renderer.exception.DataflowCreationException; import org.opendaylight.nic.of.renderer.exception.MeterCreationExeption; import org.opendaylight.nic.of.renderer.strategy.*; import org.opendaylight.nic.of.renderer.utils.TopologyUtils; import org.opendaylight.nic.pipeline_manager.PipelineManager; import org.opendaylight.nic.utils.FlowAction; import org.opendaylight.nic.utils.IntentUtils; import org.opendaylight.nic.utils.MdsalUtils; import org.opendaylight.nic.utils.exceptions.IntentInvalidException; import org.opendaylight.nic.utils.exceptions.PushDataflowException; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.tables.table.FlowBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.idmanager.rev160406.IdManagerService; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.Action; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.actions.action.Redirect; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.constraints.constraints.QosConstraint; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.subjects.subject.EndPointGroup; import org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intents.Intent; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.NodeId; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.node.NodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node; import org.opendaylight.yang.gen.v1.urn.opendaylight.meter.types.rev130918.MeterId; import org.opendaylight.yang.gen.v1.urn.opendaylight.nic.renderer.api.dataflow.rev170309.dataflows.Dataflow; import org.opendaylight.yangtools.concepts.Registration; import org.opendaylight.yangtools.yang.common.RpcResult; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.ServiceRegistration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * Created by saket on 8/19/15. */ public class OFRendererFlowManagerProvider implements OFRendererFlowService, Observer, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(OFRendererFlowManagerProvider.class); private Set<ServiceRegistration<?>> serviceRegistration; private IntentFlowManager intentFlowManager; private ArpFlowManager arpFlowManager; private LldpFlowManager lldpFlowManager; private IntentMappingService intentMappingService; private DataBroker dataBroker; private final PipelineManager pipelineManager; private OFRendererGraphService graphService; private MplsIntentFlowManager mplsIntentFlowManager; private QosConstraintManager qosConstraintManager; private Registration pktInRegistration; private RedirectFlowManager redirectFlowManager; private OFRuleWithMeterManager ofRuleWithMeterManager; private Subject topic; private MdsalUtils mdsalUtils; private IdManagerService idManagerService; private NotificationProviderService notificationProviderService; public OFRendererFlowManagerProvider(final DataBroker dataBroker, final PipelineManager pipelineManager, final IntentMappingService intentMappingService, final NotificationProviderService notificationProviderService, final IdManagerService idManagerService) { this.dataBroker = dataBroker; this.pipelineManager = pipelineManager; this.serviceRegistration = new HashSet<ServiceRegistration<?>>(); this.intentMappingService = intentMappingService; this.notificationProviderService = notificationProviderService; this.idManagerService = idManagerService; } public void init() { LOG.info("OF Renderer Provider Session Initiated"); // Register this service with karaf final BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); graphService = new NetworkGraphManager(); graphService.register(this); mplsIntentFlowManager = new MplsIntentFlowManager(dataBroker, pipelineManager); serviceRegistration.add(context.registerService(OFRendererFlowService.class, this, null)); serviceRegistration.add(context.registerService(OFRendererGraphService.class, graphService, null)); intentFlowManager = new IntentFlowManager(dataBroker, pipelineManager); arpFlowManager = new ArpFlowManager(dataBroker, pipelineManager); lldpFlowManager = new LldpFlowManager(dataBroker, pipelineManager); qosConstraintManager = new QosConstraintManager(dataBroker, pipelineManager); this.redirectFlowManager = new RedirectFlowManager(dataBroker, pipelineManager, graphService); this.pktInRegistration = notificationProviderService.registerNotificationListener(redirectFlowManager); this.ofRuleWithMeterManager = new OFRuleWithMeterManager(dataBroker, idManagerService); this.mdsalUtils = new MdsalUtils(dataBroker); } @Override public void pushIntentFlow(final Intent intent, final FlowAction flowAction) { // TODO: Extend to support other actions LOG.info("\n### Intent: {}, FlowAction: {}", intent.toString(), flowAction.getValue()); // Creates QoS configuration and stores profile in the Data Store. if (intent.getQosConfig() != null) { return; } //TODO: Change to use Command Pattern try { ActionStrategy actionStrategy = null; if (isMPLS(intent)) { actionStrategy = new MPLSExecutor(mplsIntentFlowManager, intentMappingService, graphService); } else if (isQoS(intent)) { actionStrategy = new QoSExecutor(qosConstraintManager, dataBroker); } else if (isRedirect(intent)) { actionStrategy = new RedirectExecutor(redirectFlowManager); } else { actionStrategy = new DefaultExecutor(intentFlowManager, dataBroker); } actionStrategy.execute(intent, flowAction); } catch (IntentInvalidException ie) { // TODO: Implement an action for Exception cases } } protected boolean isRedirect(final Intent intent) { Action actionContainer = null; try { actionContainer = IntentUtils.getAction(intent); } catch (IntentInvalidException e) { LOG.error(e.getMessage()); throw new NoSuchElementException(e.getMessage()); } return (Redirect.class.isInstance(actionContainer)); } protected boolean isMPLS(final Intent intent) throws IntentInvalidException { final EndPointGroup source = IntentUtils.extractSrcEndPointGroup(intent); final EndPointGroup target = IntentUtils.extractDstEndPointGroup(intent); final Map<String, String> sourceContent = getMappingServiceContent(source); final Map<String, String> targetContent = getMappingServiceContent(target); return (sourceContent.containsKey(OFRendererConstants.MPLS_LABEL_KEY) && targetContent.containsKey(OFRendererConstants.MPLS_LABEL_KEY)); } public boolean isQoS(final Intent intent) { Action actionContainer = null; try { actionContainer = IntentUtils.getAction(intent); } catch (IntentInvalidException e) { LOG.error(e.getMessage()); throw new NoSuchElementException(e.getMessage()); } final List<String> endPointGroups = IntentUtils.extractEndPointGroup(intent); return (checkQosConstraint(intent, actionContainer, endPointGroups)); } //FIXME move to a utility class @Override public void pushARPFlow(final NodeId nodeId, final FlowAction flowAction) { arpFlowManager.pushFlow(nodeId, flowAction); } @Override public void close() throws Exception { if (redirectFlowManager != null) { redirectFlowManager.close(); } if (pktInRegistration != null) { pktInRegistration.close(); } for (ServiceRegistration<?> service: serviceRegistration) { if (service != null) { service.unregister(); } } } /** * Push a LLDP flow onto an Inventory {@link NodeId} so that * OpenDaylight can know how the devices are connected to each others. * This function is necessary for OF protocols above 1.0 * @param nodeId The Inventory {@link NodeId} * @param flowAction The {@link FlowAction} to push */ @Override public void pushLLDPFlow(final NodeId nodeId, final FlowAction flowAction) { lldpFlowManager.pushFlow(nodeId, flowAction); } @Override public synchronized Dataflow pushDataFlow(Dataflow dataFlow) throws PushDataflowException { Dataflow result = null; try { if (dataFlow.isIsFlowMeter()) { switch (dataFlow.getRendererAction()) { case ADD: result = sendDataflow(dataFlow); break; case REMOVE: result = removeDataflow(dataFlow); break; } } } catch (ExecutionException e) { throw new PushDataflowException(e.getMessage()); } return result; } public Dataflow sendDataflow(final Dataflow dataflow) throws ExecutionException { final FlowBuilder flowBuilder = ofRuleWithMeterManager.createFlow(dataflow); final Map<Node, List<NodeConnector>> nodeMap = TopologyUtils.getNodes(dataBroker); for (Map.Entry<Node, List<NodeConnector>> entry : nodeMap.entrySet()) { ofRuleWithMeterManager.sendToMdsal(flowBuilder, entry.getKey().getId()); } return dataflow; } private Dataflow removeDataflow(final Dataflow dataflow) throws ExecutionException { final String dataflowId = dataflow.getId().getValue(); final FlowBuilder flowBuilder = ofRuleWithMeterManager.createFlow(dataflow); final Map<Node, List<NodeConnector>> nodeMap = TopologyUtils.getNodes(dataBroker); for (Map.Entry<Node, List<NodeConnector>> entry : nodeMap.entrySet()) { ofRuleWithMeterManager.removeFromMdsal(flowBuilder, entry.getKey().getId()); } removeMeter(dataflow.getMeterId().longValue(), dataflowId); return dataflow; } @Override public void pushDataFlow(final NodeId nodeId, final Dataflow dataflow) { try { final FlowBuilder flowBuilder = ofRuleWithMeterManager.createFlow(dataflow); ofRuleWithMeterManager.sendToMdsal(flowBuilder, nodeId); } catch (DataflowCreationException me) { LOG.error(me.getMessage()); } } @Override public MeterId createMeter(final String id, final long dropRate) throws MeterCreationExeption { return ofRuleWithMeterManager.createMeter(id, dropRate); } @Override public void removeMeter(final Long meterId, final String dataflowId) throws PushDataflowException { try { final Future<RpcResult<Void>> releaseResult = ofRuleWithMeterManager.removeMeter(meterId, dataflowId); releaseResult.get(5, TimeUnit.SECONDS); } catch (InterruptedException | ExecutionException | TimeoutException e) { throw new PushDataflowException(e.getMessage()); } } /** * Checks the Constraint name is present in the constraint container. * @param intent Intent * @param actionContainer Action * @param endPointGroups List of Endpoints * @return boolean */ private boolean checkQosConstraint(final Intent intent, final Action actionContainer, final List<String> endPointGroups) { //Check for constrain name in the intent. final org.opendaylight.yang.gen.v1.urn.opendaylight.intent.rev150122.intent.constraints.Constraints constraintContainer = intent.getConstraints().get(0).getConstraints(); if (!constraintContainer.getImplementedInterface().isAssignableFrom(QosConstraint.class)) { return false; } final String qosName = ((QosConstraint)constraintContainer).getQosConstraint().getQosName(); LOG.info("QosConstraint is set to: {}", qosName); if (qosName != null) { //Set the values to QosConstraintManager qosConstraintManager.setQosName(qosName); qosConstraintManager.setEndPointGroups(endPointGroups); qosConstraintManager.setAction(actionContainer); qosConstraintManager.setConstraint(constraintContainer); } else { LOG.trace("QoS Name is not set"); return false; } return true; } @Override public void update() { final Intent msg = (Intent) topic.getUpdate(this); if (msg != null) { pushIntentFlow(msg, FlowAction.ADD_FLOW); } } @Override public void setSubject(final Subject sub) { this.topic = sub; } private Map<String, String> getMappingServiceContent(final EndPointGroup endPointGroup) { Map<String, String> contentMap = null; if (endPointGroup != null && endPointGroup.getEndPointGroup() != null){ final String endPointGroupName = endPointGroup.getEndPointGroup().getName(); contentMap = intentMappingService.get(endPointGroupName); } return contentMap == null ? new HashMap<>() : contentMap; } }