/* * Copyright (c) 2014 Pacnet 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.openflowplugin.applications.lldpspeaker; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.openflowplugin.common.wait.SimpleTaskRetryLooper; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnector; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.inventory.rev130819.FlowCapableNodeConnectorBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortConfig; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.PortState; import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.port.rev130925.flow.capable.port.State; import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.Nodes; 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.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; /** * NodeConnectorInventoryEventTranslator is listening for changes in inventory operational DOM tree * and update LLDPSpeaker and topology. */ public class NodeConnectorInventoryEventTranslator<T extends DataObject> implements DataTreeChangeListener<T>, AutoCloseable { private static final InstanceIdentifier<State> II_TO_STATE = InstanceIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .augmentation(FlowCapableNodeConnector.class) .child(State.class) .build(); private static final InstanceIdentifier<FlowCapableNodeConnector> II_TO_FLOW_CAPABLE_NODE_CONNECTOR = InstanceIdentifier.builder(Nodes.class) .child(Node.class) .child(NodeConnector.class) .augmentation(FlowCapableNodeConnector.class) .build(); private static final long STARTUP_LOOP_TICK = 500L; private static final int STARTUP_LOOP_MAX_RETRIES = 8; private static final Logger LOG = LoggerFactory.getLogger(NodeConnectorInventoryEventTranslator.class); private final ListenerRegistration<DataTreeChangeListener> listenerOnPortRegistration; private final ListenerRegistration<DataTreeChangeListener> listenerOnPortStateRegistration; private final Set<NodeConnectorEventsObserver> observers; private final Map<InstanceIdentifier<?>,FlowCapableNodeConnector> iiToDownFlowCapableNodeConnectors = new HashMap<>(); public NodeConnectorInventoryEventTranslator(DataBroker dataBroker, NodeConnectorEventsObserver... observers) { this.observers = ImmutableSet.copyOf(observers); final DataTreeIdentifier<T> dtiToNodeConnector = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_FLOW_CAPABLE_NODE_CONNECTOR); final DataTreeIdentifier<T> dtiToNodeConnectorState = new DataTreeIdentifier(LogicalDatastoreType.OPERATIONAL, II_TO_STATE); final SimpleTaskRetryLooper looper = new SimpleTaskRetryLooper(STARTUP_LOOP_TICK, STARTUP_LOOP_MAX_RETRIES); try { listenerOnPortRegistration = looper.loopUntilNoException(new Callable<ListenerRegistration<DataTreeChangeListener>>() { @Override public ListenerRegistration<DataTreeChangeListener> call() throws Exception { return dataBroker.registerDataTreeChangeListener(dtiToNodeConnector, NodeConnectorInventoryEventTranslator.this); } }); listenerOnPortStateRegistration = looper.loopUntilNoException(new Callable<ListenerRegistration<DataTreeChangeListener>>() { @Override public ListenerRegistration<DataTreeChangeListener> call() throws Exception { return dataBroker.registerDataTreeChangeListener(dtiToNodeConnectorState, NodeConnectorInventoryEventTranslator.this); } }); } catch (Exception e) { LOG.error("DataTreeChangeListeners registration failed: {}", e); throw new IllegalStateException("NodeConnectorInventoryEventTranslator startup failed!", e); } LOG.info("NodeConnectorInventoryEventTranslator has started."); } @Override public void close() { if (listenerOnPortRegistration != null) { listenerOnPortRegistration.close(); } if (listenerOnPortStateRegistration != null) { listenerOnPortStateRegistration.close(); } } @Override public void onDataTreeChanged(@Nonnull Collection<DataTreeModification<T>> modifications) { for(DataTreeModification modification : modifications) { LOG.trace("Node connectors in inventory changed -> {}", modification.getRootNode().getModificationType()); switch (modification.getRootNode().getModificationType()) { case WRITE: processAddedConnector(modification); break; case SUBTREE_MODIFIED: processUpdatedConnector(modification); break; case DELETE: processRemovedConnector(modification); break; default: throw new IllegalArgumentException("Unhandled modification type: {}" + modification.getRootNode().getModificationType()); } } } private void processAddedConnector(final DataTreeModification<T> modification) { final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier(); InstanceIdentifier<NodeConnector> nodeConnectorInstanceId =identifier.firstIdentifierOf(NodeConnector.class); if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter(); if (!isPortDown(flowConnector)) { notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); } else { iiToDownFlowCapableNodeConnectors.put(nodeConnectorInstanceId, flowConnector); } } } private void processUpdatedConnector(final DataTreeModification<T> modification) { final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier(); InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class); if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { FlowCapableNodeConnector flowConnector = (FlowCapableNodeConnector) modification.getRootNode().getDataAfter(); if (isPortDown(flowConnector)) { notifyNodeConnectorDisappeared(nodeConnectorInstanceId); } else { notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowConnector); } } else if (compareIITail(identifier, II_TO_STATE)) { FlowCapableNodeConnector flowNodeConnector = iiToDownFlowCapableNodeConnectors.get(nodeConnectorInstanceId); if (flowNodeConnector != null) { State state = (State) modification.getRootNode().getDataAfter(); if (!state.isLinkDown()) { FlowCapableNodeConnectorBuilder flowCapableNodeConnectorBuilder = new FlowCapableNodeConnectorBuilder(flowNodeConnector); flowCapableNodeConnectorBuilder.setState(state); notifyNodeConnectorAppeared(nodeConnectorInstanceId, flowCapableNodeConnectorBuilder.build()); iiToDownFlowCapableNodeConnectors.remove(nodeConnectorInstanceId); } } } } private void processRemovedConnector(final DataTreeModification<T> modification) { final InstanceIdentifier<T> identifier = modification.getRootPath().getRootIdentifier(); if (compareIITail(identifier, II_TO_FLOW_CAPABLE_NODE_CONNECTOR)) { InstanceIdentifier<NodeConnector> nodeConnectorInstanceId = identifier.firstIdentifierOf(NodeConnector.class); notifyNodeConnectorDisappeared(nodeConnectorInstanceId); } } private boolean compareIITail(final InstanceIdentifier<?> ii1, final InstanceIdentifier<?> ii2) { return Iterables.getLast(ii1.getPathArguments()).equals(Iterables.getLast(ii2.getPathArguments())); } private static boolean isPortDown(final FlowCapableNodeConnector flowCapableNodeConnector) { PortState portState = flowCapableNodeConnector.getState(); PortConfig portConfig = flowCapableNodeConnector.getConfiguration(); return portState != null && portState.isLinkDown() || portConfig != null && portConfig.isPORTDOWN(); } private void notifyNodeConnectorAppeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId, final FlowCapableNodeConnector flowConnector) { for (NodeConnectorEventsObserver observer : observers) { observer.nodeConnectorAdded(nodeConnectorInstanceId, flowConnector); } } private void notifyNodeConnectorDisappeared(final InstanceIdentifier<NodeConnector> nodeConnectorInstanceId) { for (NodeConnectorEventsObserver observer : observers) { observer.nodeConnectorRemoved(nodeConnectorInstanceId); } } }