/* * 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.renderer.vpp.iface; import static com.google.common.base.Preconditions.checkNotNull; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.stream.Collectors; import javax.annotation.Nonnull; import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain; 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.ReadWriteTransaction; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.CloseOnFailTransactionChain; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.KeyFactory; import org.opendaylight.groupbasedpolicy.renderer.vpp.util.VppIidFactory; import org.opendaylight.groupbasedpolicy.util.DataStoreHelper; import org.opendaylight.groupbasedpolicy.util.DataTreeChangeHandler; import org.opendaylight.groupbasedpolicy.util.EndpointUtils; import org.opendaylight.groupbasedpolicy.util.IidFactory; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.Endpoints; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.AddressEndpoints; 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.ExternalLocationCase; 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.base_endpoint.rev160427.has.relative.location.RelativeLocations; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.RelativeLocationsBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.relative.location.relative.locations.ExternalLocationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.parent.child.endpoints.parent.endpoint.choice.parent.endpoint._case.ParentEndpointKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.ProviderName; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProviderBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocationKey; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointBuilder; import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.NodeId; import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; public class VppEndpointLocationProvider extends DataTreeChangeHandler<AddressEndpoint> { private static final Logger LOG = LoggerFactory.getLogger(VppEndpointLocationProvider.class); public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER = new ProviderName("VPP endpoint location provider"); public static final long PROVIDER_PRIORITY = 10L; private final BindingTransactionChain txChain; private final Map<VppEndpointKey, VppEndpoint> vppEndpoints = new HashMap<>(); private final Map<VppEndpointKey, DataObjectModification<AddressEndpoint>> cachedVppEndpoints = new HashMap<>(); public VppEndpointLocationProvider(DataBroker dataProvider) { super(dataProvider); LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER) .setPriority(PROVIDER_PRIORITY) .build(); txChain = checkNotNull(dataProvider) .createTransactionChain(new CloseOnFailTransactionChain(VppEndpointLocationProvider.class.getSimpleName())); WriteTransaction wTx = txChain.newWriteOnlyTransaction(); wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER), locationProvider, true); Futures.addCallback(wTx.submit(), new FutureCallback<Void>() { @Override public void onSuccess(Void result) { LOG.debug("{} was created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue()); } @Override public void onFailure(Throwable t) { LOG.error("{} was NOT created", VPP_ENDPOINT_LOCATION_PROVIDER.getValue()); } }); registerDataTreeChangeListener(new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL, InstanceIdentifier.builder(Endpoints.class).child(AddressEndpoints.class).child(AddressEndpoint.class).build())); } @Override protected void onWrite(DataObjectModification<AddressEndpoint> rootNode, InstanceIdentifier<AddressEndpoint> rootIdentifier) { LOG.debug("onWrite triggered by {}", rootNode.getDataAfter()); try { if (EndpointUtils.isExternalEndpoint(dataProvider, rootNode.getDataAfter())) { writeLocation(createRelativeAddressEndpointLocation(rootNode.getDataAfter().getKey(), VppNodeManager.resolvePublicInterfaces(dataProvider))).get(); } else { createAbsoluteAddressEndpointLocation(null, rootNode).get(); } } catch (InterruptedException | ExecutionException e) { LOG.error("Failed to write location for endpoint {}. {}", rootNode.getDataAfter().getKey(), e.getMessage()); } } @Override protected void onDelete(DataObjectModification<AddressEndpoint> rootNode, InstanceIdentifier<AddressEndpoint> rootIdentifier) { LOG.debug("onDelete triggered by {}", rootNode.getDataBefore()); try { if (EndpointUtils.isExternalEndpoint(dataProvider, rootNode.getDataBefore())) { deleteLocation(createProviderAddressEndpointLocationKey(rootNode.getDataBefore().getKey())).get(); } else { createAbsoluteAddressEndpointLocation(null, rootNode).get(); } } catch (InterruptedException | ExecutionException e) { LOG.error("Failed to delete location for endpoint {}. {}", rootNode.getDataBefore().getKey(), e.getMessage()); } } @Override protected void onSubtreeModified(DataObjectModification<AddressEndpoint> rootNode, InstanceIdentifier<AddressEndpoint> rootIdentifier) { LOG.debug("onSubtreeModified triggered by change: before={} after={}", rootNode.getDataBefore(), rootNode.getDataAfter()); if (rootNode.getDataBefore() != null) { onDelete(rootNode, rootIdentifier); } if (rootNode.getDataAfter() != null) { onWrite(rootNode, rootIdentifier); } } public ListenableFuture<Void> createLocationForVppEndpoint(VppEndpoint vppEndpoint) { return createAbsoluteAddressEndpointLocation(vppEndpoint, null); } public ListenableFuture<Void> deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) { // removing VPP EP from cache out of since block, it's not needed for the other thread. vppEndpoints.remove(vppEndpoint.getKey()); return deleteLocation(createProviderAddressEndpointLocationKey(vppEndpoint)); } /** * There are two inputs from which we need to resolve location - {@link AddressEndpoint} and {@link VppEndpoint} * These data are delivered by different threads which meet here. */ @VisibleForTesting synchronized ListenableFuture<Void> createAbsoluteAddressEndpointLocation(VppEndpoint vppEndpoint, DataObjectModification<AddressEndpoint> rootNode) { if (vppEndpoint != null) { vppEndpoints.put(vppEndpoint.getKey(), vppEndpoint); if (cachedVppEndpoints.get(vppEndpoint.getKey()) != null) { return processAddrEp(cachedVppEndpoints.get(vppEndpoint.getKey())); } } else if (rootNode != null) { return processAddrEp(rootNode); } return Futures.immediateFuture(null); } private ListenableFuture<Void> processAddrEp(DataObjectModification<AddressEndpoint> rootNode) { if (rootNode != null) { AddressEndpointChange aec = new AddressEndpointChange(rootNode, dataProvider); switch (rootNode.getModificationType()) { case WRITE: case SUBTREE_MODIFIED: { VppEndpoint vpp = vppEndpoints.get(vppEndpointKeyFrom(rootNode.getDataAfter().getKey())); if (vpp == null) { VppEndpointKey key = vppEndpointKeyFrom(rootNode.getDataAfter().getKey()); cachedVppEndpoints.put(key, rootNode); return Futures.immediateFuture(null); } if (aec.hasMoreParents()) { return aec.syncMultiparents(); } return aec.write(); } case DELETE: { if (aec.hasMoreParents()) { return aec.syncMultiparents(); } else { return aec.delete(); } } } } return Futures.immediateFuture(null); } private ProviderAddressEndpointLocation createAbsoluteLocationFromVppEndpoint(VppEndpoint vppEndpoint) { InstanceIdentifier<Node> vppNodeIid = VppIidFactory.getNetconfNodeIid(vppEndpoint.getVppNodeId()); String restIfacePath = VppPathMapper.interfaceToRestPath(vppEndpoint.getVppInterfaceName()); AbsoluteLocation absoluteLocation = new AbsoluteLocationBuilder().setLocationType(new ExternalLocationCaseBuilder() .setExternalNodeMountPoint(vppNodeIid).setExternalNodeConnector(restIfacePath).build()).build(); return new ProviderAddressEndpointLocationBuilder() .setKey(createProviderAddressEndpointLocationKey(vppEndpoint)) .setAbsoluteLocation(absoluteLocation) .build(); } public ProviderAddressEndpointLocation createRelativeAddressEndpointLocation(@Nonnull AddressEndpointKey addrEp, @Nonnull Map<NodeId, String> publicIntfNamesByNodes) { RelativeLocations relLocations = new RelativeLocationsBuilder() .setExternalLocation(publicIntfNamesByNodes.keySet() .stream() .filter(nodeId -> publicIntfNamesByNodes.get(nodeId) != null) .map(nodeId -> new ExternalLocationBuilder() .setExternalNodeMountPoint(VppIidFactory.getNetconfNodeIid(nodeId)) .setExternalNodeConnector( VppPathMapper.interfaceToRestPath(publicIntfNamesByNodes.get(nodeId))) .build()) .collect(Collectors.toList())) .build(); return new ProviderAddressEndpointLocationBuilder().setKey(createProviderAddressEndpointLocationKey(addrEp)) .setRelativeLocations(relLocations) .build(); } public ListenableFuture<Void> writeLocation(ProviderAddressEndpointLocation location) { WriteTransaction wTx = txChain.newWriteOnlyTransaction(); wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey()), location, true); return Futures.transform(wTx.submit(), new Function<Void, Void>() { @Override public Void apply(Void input) { LOG.debug("{} provided location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), location); return null; } }); } public ListenableFuture<Void> deleteLocation(ProviderAddressEndpointLocationKey key) { ReadWriteTransaction rwTx = txChain.newReadWriteTransaction(); LOG.debug("Deleting location for {}", key); DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, key), rwTx); return Futures.transform(rwTx.submit(), new Function<Void, Void>() { @Override public Void apply(Void input) { LOG.debug("{} removed location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), key); return null; } }); } public ListenableFuture<Void> replaceLocationForEndpoint(@Nonnull ExternalLocationCase location, @Nonnull AddressEndpointWithLocationKey addrEpWithLocKey) { ProviderAddressEndpointLocationKey provAddrEpLocKey = KeyFactory.providerAddressEndpointLocationKey(addrEpWithLocKey); AbsoluteLocation absoluteLocation = new AbsoluteLocationBuilder().setLocationType(location).build(); ProviderAddressEndpointLocation providerAddressEndpointLocation = new ProviderAddressEndpointLocationBuilder() .setKey(provAddrEpLocKey).setAbsoluteLocation(absoluteLocation).build(); WriteTransaction wTx = txChain.newWriteOnlyTransaction(); wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, providerAddressEndpointLocation.getKey()), providerAddressEndpointLocation); LOG.debug("Updating location for {}", provAddrEpLocKey); return Futures.transform(wTx.submit(), new Function<Void, Void>() { @Override public Void apply(Void input) { LOG.debug("{} replaced location: {}", VPP_ENDPOINT_LOCATION_PROVIDER.getValue(), providerAddressEndpointLocation); return null; } }); } static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(VppEndpoint vpp) { return new ProviderAddressEndpointLocationKey(vpp.getAddress(), vpp.getAddressType(), vpp.getContextId(), vpp.getContextType()); } static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(AddressEndpointKey key) { return new ProviderAddressEndpointLocationKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType()); } private static ProviderAddressEndpointLocationKey createProviderAddressEndpointLocationKey(ParentEndpointKey key) { return new ProviderAddressEndpointLocationKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType()); } private VppEndpointKey vppEndpointKeyFrom(AddressEndpointKey key) { return new VppEndpointKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType()); } private VppEndpointKey vppEndpointKeyFrom(ParentEndpointKey key) { return new VppEndpointKey(key.getAddress(), key.getAddressType(), key.getContextId(), key.getContextType()); } @Override public void close() { super.closeRegisteredListener(); WriteTransaction wTx = txChain.newWriteOnlyTransaction(); wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER)); wTx.submit(); } private class AddressEndpointChange { private final AddressEndpoint before; private final AddressEndpoint after; private final DataBroker dataBroker; public AddressEndpointChange(DataObjectModification<AddressEndpoint> addrEp, @Nonnull DataBroker dataBroker) { this.before = addrEp.getDataBefore(); this.after = addrEp.getDataAfter(); this.dataBroker = dataBroker; } boolean hasMoreParents() { return (before != null && EndpointUtils.getParentEndpoints(before.getParentEndpointChoice()).size() > 1) || (after != null && EndpointUtils.getParentEndpoints(after.getParentEndpointChoice()).size() > 1); } ListenableFuture<Void> syncMultiparents() { ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction(); if (before != null) { for (ParentEndpoint pe : EndpointUtils.getParentEndpoints(before.getParentEndpointChoice())) { InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(pe.getKey())); DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx); } } if (after != null) { for (ParentEndpoint pe : EndpointUtils.getParentEndpoints(after.getParentEndpointChoice())) { VppEndpoint vppEndpoint = vppEndpoints.get(vppEndpointKeyFrom(after.getKey())); InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid(VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(pe.getKey())); ProviderAddressEndpointLocation location = createAbsoluteLocationFromVppEndpoint( new VppEndpointBuilder(vppEndpoint).setKey(vppEndpointKeyFrom(pe.getKey())).build()); rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true); } } return rwTx.submit(); } ListenableFuture<Void> write() { VppEndpoint vpp = vppEndpoints.get(vppEndpointKeyFrom(after.getKey())); WriteTransaction wTx = dataBroker.newWriteOnlyTransaction(); ProviderAddressEndpointLocation location = createAbsoluteLocationFromVppEndpoint(vpp); InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid( VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(vpp)); wTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true); return wTx.submit(); } ListenableFuture<Void> delete() { ReadWriteTransaction rwTx = dataBroker.newReadWriteTransaction(); InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.providerAddressEndpointLocationIid( VPP_ENDPOINT_LOCATION_PROVIDER, createProviderAddressEndpointLocationKey(before.getKey())); DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx); return rwTx.submit(); } } }