/* * Copyright (c) 2016 Cisco Systems, Inc. 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.groupbasedpolicy.ne.location.provider; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import javax.annotation.Nullable; import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener; import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataObjectModification; import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier; import org.opendaylight.controller.md.sal.binding.api.DataTreeModification; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.groupbasedpolicy.util.NetUtils; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpPrefix; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocation; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCaseBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.network.elements.rev160407.NetworkElements; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.network.elements.rev160407.NetworkElementsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.network.elements.rev160407.network.elements.NetworkElement; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.network.elements.rev160407.network.elements.network.element.Interface; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint.network.elements.rev160407.network.elements.network.element._interface.EndpointNetwork; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.IpPrefixType; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.forwarding.l2_l3.rev160427.L3Context; 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 com.google.common.annotations.VisibleForTesting; public class NeLocationProvider implements ClusteredDataTreeChangeListener<NetworkElements>, AutoCloseable { private static final Logger LOG = LoggerFactory.getLogger(NeLocationProvider.class); public static final String NE_LOCATION_PROVIDER_NAME = "ne-location-provider"; private List<AddressEndpoint> endpoints; private NetworkElements networkElements; private DataBroker dataBroker; private ListenerRegistration<NeLocationProvider> listenerRegistration; private EndpointsListener endpointsListener; public NeLocationProvider(DataBroker dataBroker) { this.listenerRegistration = dataBroker.registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.builder(NetworkElements.class).build()), this); this.endpoints = new ArrayList<>(); this.networkElements = new NetworkElementsBuilder().build(); this.dataBroker = dataBroker; this.endpointsListener = new EndpointsListener(dataBroker, this); LOG.info("NE location provider created"); } @Override public void close() { this.listenerRegistration.close(); this.endpointsListener.close(); } public synchronized void onEndpointsChange(Collection<DataTreeModification<AddressEndpoint>> changes) { WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); for (DataTreeModification<AddressEndpoint> change : changes) { switch (change.getRootNode().getModificationType()) { case DELETE: { AddressEndpoint endpoint = change.getRootNode().getDataBefore(); removeLocationForEndpoint(endpoint, wtx); this.endpoints.remove(endpoint); break; } case WRITE: { AddressEndpoint endpoint = change.getRootNode().getDataBefore(); if (endpoint != null) { this.endpoints.remove(endpoint); this.endpoints.add(change.getRootNode().getDataAfter()); break; } endpoint = change.getRootNode().getDataAfter(); createLocationForEndpoint(endpoint, wtx); this.endpoints.add(endpoint); break; } case SUBTREE_MODIFIED: { this.endpoints.remove(change.getRootNode().getDataBefore()); this.endpoints.add(change.getRootNode().getDataAfter()); break; } } } DataStoreHelper.submitToDs(wtx); } private void createLocationForEndpoint(AddressEndpoint endpoint, WriteTransaction wtx) { for (NetworkElement ne : nullToEmpty(networkElements.getNetworkElement())) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { if (endpoint.getContextType().isAssignableFrom(L3Context.class) && endpoint.getContextId().equals(en.getL3ContextId()) && endpoint.getAddressType().isAssignableFrom(IpPrefixType.class) && NetUtils .samePrefix(new IpPrefix(endpoint.getAddress().toCharArray()), en.getIpPrefix())) { InstanceIdentifier<AbsoluteLocation> iid = IidFactory .providerAddressEndpointLocationIid(NE_LOCATION_PROVIDER_NAME, IpPrefixType.class, endpoint.getAddress(), endpoint.getContextType(), endpoint.getContextId()) .child(AbsoluteLocation.class); wtx.put(LogicalDatastoreType.CONFIGURATION, iid, createRealLocation(ne.getIid(), iface.getIid()), true); LOG.debug("New location created for endpoint {}", endpoint); return; } } } } } private void removeLocationForEndpoint(AddressEndpoint endpoint, WriteTransaction wtx) { for (NetworkElement ne : nullToEmpty(networkElements.getNetworkElement())) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { if (endpoint.getContextType().isAssignableFrom(L3Context.class) && endpoint.getContextId().equals(en.getL3ContextId()) && endpoint.getAddressType().isAssignableFrom(IpPrefixType.class) && NetUtils .samePrefix(new IpPrefix(endpoint.getAddress().toCharArray()), en.getIpPrefix())) { InstanceIdentifier<AbsoluteLocation> iid = IidFactory .providerAddressEndpointLocationIid(NE_LOCATION_PROVIDER_NAME, IpPrefixType.class, endpoint.getAddress(), endpoint.getContextType(), endpoint.getContextId()) .child(AbsoluteLocation.class); wtx.delete(LogicalDatastoreType.CONFIGURATION, iid); LOG.debug("Location deleted for endpoint {}", endpoint); return; } } } } } @Override public synchronized void onDataTreeChanged(Collection<DataTreeModification<NetworkElements>> changes) { WriteTransaction wtx = dataBroker.newWriteOnlyTransaction(); for (DataTreeModification<NetworkElements> change : changes) { switch (change.getRootNode().getModificationType()) { case DELETE: { NetworkElements nes = change.getRootNode().getDataBefore(); for (NetworkElement ne : nullToEmpty(nes.getNetworkElement())) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } } } networkElements = new NetworkElementsBuilder().build(); LOG.debug("Network elements removed"); break; } case WRITE: { NetworkElements nes = change.getRootNode().getDataBefore(); if (nes != null) { for (NetworkElement ne : nullToEmpty(nes.getNetworkElement())) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } } } } nes = change.getRootNode().getDataAfter(); for (NetworkElement ne : nullToEmpty(nes.getNetworkElement())) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processCreatedEN(en, ne.getIid(), iface.getIid(), wtx); } } } networkElements = nes; LOG.debug("New Network elements created {}", change.getRootNode().getDataAfter()); break; } case SUBTREE_MODIFIED: { List<DataObjectModification<NetworkElement>> modifiedNetworkElements = getModifiedNetworkElements(change.getRootNode()); for (DataObjectModification<NetworkElement> netElement : modifiedNetworkElements) { processNetworkElementChange(netElement, wtx); } break; } } } DataStoreHelper.submitToDs(wtx); } private List<DataObjectModification<NetworkElement>> getModifiedNetworkElements( DataObjectModification<NetworkElements> modifiedNEs) { Collection<DataObjectModification<? extends DataObject>> potentialModifiedNetworkElements = modifiedNEs.getModifiedChildren(); if (potentialModifiedNetworkElements == null) { return Collections.emptyList(); } List<DataObjectModification<NetworkElement>> nes = new ArrayList<>(); for (DataObjectModification<? extends DataObject> potentialModifiedNetworkElement : potentialModifiedNetworkElements) { if (potentialModifiedNetworkElement.getDataType().isAssignableFrom(NetworkElement.class)) { nes.add((DataObjectModification<NetworkElement>) potentialModifiedNetworkElement); } } return nes; } private void processNetworkElementChange(DataObjectModification<NetworkElement> netElement, WriteTransaction wtx) { switch (netElement.getModificationType()) { case DELETE: { NetworkElement ne = netElement.getDataBefore(); for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } } networkElements.getNetworkElement().remove(ne); LOG.debug("Netowrk element {} removed", netElement.getDataBefore()); break; } case WRITE: { NetworkElement ne = netElement.getDataBefore(); if (ne != null) { for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } } networkElements.getNetworkElement().remove(ne); } ne = netElement.getDataAfter(); for (Interface iface : nullToEmpty(ne.getInterface())) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processCreatedEN(en, ne.getIid(), iface.getIid(), wtx); } } networkElements.getNetworkElement().add(ne); LOG.debug("Created new Network element {}", netElement.getDataAfter()); break; } case SUBTREE_MODIFIED: { List<DataObjectModification<Interface>> modifiedInterfaces = getModifiedInterfaces(netElement); for (DataObjectModification<Interface> modifiedInterface : modifiedInterfaces) { processInterfaceChange(modifiedInterface, netElement.getDataBefore(), wtx); } break; } } } private List<DataObjectModification<Interface>> getModifiedInterfaces( DataObjectModification<NetworkElement> netElement) { Collection<DataObjectModification<? extends DataObject>> potentialModifiedInterfaces = netElement.getModifiedChildren(); if (potentialModifiedInterfaces == null) { return Collections.emptyList(); } List<DataObjectModification<Interface>> interfaces = new ArrayList<>(); for (DataObjectModification<? extends DataObject> potentialModifiedInterface : potentialModifiedInterfaces) { if (potentialModifiedInterface.getDataType().isAssignableFrom(Interface.class)) { interfaces.add((DataObjectModification<Interface>) potentialModifiedInterface); } } return interfaces; } private void processInterfaceChange(DataObjectModification<Interface> modifiedInterface, NetworkElement nodeBefore, WriteTransaction wtx) { switch (modifiedInterface.getModificationType()) { case DELETE: { Interface iface = modifiedInterface.getDataBefore(); for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } int nodeIndex = getIndexOf(nodeBefore); networkElements.getNetworkElement().get(nodeIndex).getInterface().remove(iface); LOG.debug("Interface {} removed", modifiedInterface.getDataBefore()); break; } case WRITE: { Interface iface = modifiedInterface.getDataBefore(); int nodeIndex = getIndexOf(nodeBefore); if (iface != null) { for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processDeletedEN(en, wtx); } networkElements.getNetworkElement().get(nodeIndex).getInterface().remove(iface); } iface = modifiedInterface.getDataAfter(); for (EndpointNetwork en : nullToEmpty(iface.getEndpointNetwork())) { processCreatedEN(en, nodeBefore.getIid(), iface.getIid(), wtx); } networkElements.getNetworkElement().get(nodeIndex).getInterface().add(iface); LOG.debug("Created new Interface {}", modifiedInterface.getDataAfter()); break; } case SUBTREE_MODIFIED: { List<DataObjectModification<EndpointNetwork>> modifiedENs = getModifiedEndpointNetworks(modifiedInterface); for (DataObjectModification<EndpointNetwork> modifiedEN : modifiedENs) { processEndpointNetworkChange(modifiedEN, nodeBefore, modifiedInterface.getDataBefore(), wtx); } break; } } } private List<DataObjectModification<EndpointNetwork>> getModifiedEndpointNetworks( DataObjectModification<Interface> modifiedInterface) { Collection<DataObjectModification<? extends DataObject>> potentialModifiedEPs = modifiedInterface.getModifiedChildren(); if (potentialModifiedEPs == null) { return Collections.emptyList(); } List<DataObjectModification<EndpointNetwork>> eps = new ArrayList<>(); for (DataObjectModification<? extends DataObject> potentialModifiedEP : potentialModifiedEPs) { if (potentialModifiedEP.getDataType().isAssignableFrom(EndpointNetwork.class)) { eps.add((DataObjectModification<EndpointNetwork>) potentialModifiedEP); } } return eps; } private void processEndpointNetworkChange(DataObjectModification<EndpointNetwork> modifiedEN, NetworkElement nodeBefore, Interface ifaceBefore, WriteTransaction wtx) { switch (modifiedEN.getModificationType()) { case DELETE: { processDeletedEN(modifiedEN.getDataBefore(), wtx); int nodeIndex = getIndexOf(nodeBefore); int ifaceIndex = getIndexOf(ifaceBefore, nodeIndex); networkElements.getNetworkElement() .get(nodeIndex) .getInterface() .get(ifaceIndex) .getEndpointNetwork() .remove(modifiedEN.getDataBefore()); LOG.debug("Endpoint network {} removed", modifiedEN.getDataBefore()); break; } case WRITE: { processCreatedEN(modifiedEN.getDataAfter(), nodeBefore.getIid(), ifaceBefore.getIid(), wtx); int nodeIndex = getIndexOf(nodeBefore); int ifaceIndex = getIndexOf(ifaceBefore, nodeIndex); networkElements.getNetworkElement() .get(nodeIndex) .getInterface() .get(ifaceIndex) .getEndpointNetwork() .add(modifiedEN.getDataAfter()); LOG.debug("Created new Endpoint network {}", modifiedEN.getDataAfter()); break; } case SUBTREE_MODIFIED: { LOG.debug("EndpointNetwork {} changed", modifiedEN.getDataAfter().getKey()); break; } } } private void processCreatedEN(EndpointNetwork en, InstanceIdentifier<?> nodeIID, String connectorIID, WriteTransaction wtx) { for (AddressEndpoint endpoint : endpoints) { if (endpoint.getContextType().isAssignableFrom(L3Context.class) && endpoint.getContextId().equals(en.getL3ContextId()) && endpoint.getAddressType().isAssignableFrom(IpPrefixType.class) && NetUtils.samePrefix(new IpPrefix(endpoint.getAddress().toCharArray()), en.getIpPrefix())) { InstanceIdentifier<AbsoluteLocation> iid = IidFactory .providerAddressEndpointLocationIid(NE_LOCATION_PROVIDER_NAME, IpPrefixType.class, endpoint.getAddress(), endpoint.getContextType(), endpoint.getContextId()) .child(AbsoluteLocation.class); wtx.put(LogicalDatastoreType.CONFIGURATION, iid, createRealLocation(nodeIID, connectorIID), true); LOG.debug("New location created for endpoint {}", endpoint); } } return; } private void processDeletedEN(EndpointNetwork en, WriteTransaction wtx) { for (AddressEndpoint endpoint : endpoints) { if (endpoint.getContextType().isAssignableFrom(L3Context.class) && endpoint.getContextId().equals(en.getL3ContextId()) && endpoint.getAddressType().isAssignableFrom(IpPrefixType.class) && NetUtils.samePrefix(new IpPrefix(endpoint.getAddress().toCharArray()), en.getIpPrefix())) { InstanceIdentifier<AbsoluteLocation> iid = IidFactory .providerAddressEndpointLocationIid(NE_LOCATION_PROVIDER_NAME, IpPrefixType.class, endpoint.getAddress(), endpoint.getContextType(), endpoint.getContextId()) .child(AbsoluteLocation.class); wtx.delete(LogicalDatastoreType.CONFIGURATION, iid); LOG.debug("Location deleted for endpoint {}", endpoint); } } return; } private AbsoluteLocation createRealLocation(InstanceIdentifier<?> node, String iface) { return new AbsoluteLocationBuilder() .setLocationType(new ExternalLocationCaseBuilder().setExternalNodeMountPoint(node) .setExternalNodeConnector(iface).build()).build(); } private <T> List<T> nullToEmpty(@Nullable List<T> list) { return list == null ? Collections.emptyList() : list; } private int getIndexOf(NetworkElement ne) { for (NetworkElement listNE : networkElements.getNetworkElement()) { if (ne.getIid().equals(listNE.getIid())) { return networkElements.getNetworkElement().indexOf(listNE); } } return -1; } private int getIndexOf(Interface iface, int nodeIndex) { for (Interface listIface : networkElements.getNetworkElement().get(nodeIndex).getInterface()) { if (iface.getIid().equals(listIface.getIid())) { return networkElements.getNetworkElement().get(nodeIndex).getInterface().indexOf(listIface); } } return -1; } @VisibleForTesting synchronized List<AddressEndpoint> getEndpoints() { return endpoints; } @VisibleForTesting synchronized NetworkElements getNetworkElements() { return networkElements; } }