2 * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
11 import static com.google.common.base.Preconditions.checkNotNull;
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.List;
17 import java.util.concurrent.locks.ReentrantLock;
18 import java.util.stream.Collectors;
20 import javax.annotation.Nonnull;
22 import org.opendaylight.controller.md.sal.binding.api.ClusteredDataTreeChangeListener;
23 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
24 import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;
25 import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;
26 import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;
27 import org.opendaylight.controller.md.sal.binding.api.ReadWriteTransaction;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
30 import org.opendaylight.groupbasedpolicy.renderer.vpp.manager.VppNodeManager;
31 import org.opendaylight.groupbasedpolicy.renderer.vpp.util.CloseOnFailTransactionChain;
32 import org.opendaylight.groupbasedpolicy.util.DataStoreHelper;
33 import org.opendaylight.groupbasedpolicy.util.EndpointUtils;
34 import org.opendaylight.groupbasedpolicy.util.IidFactory;
35 import org.opendaylight.groupbasedpolicy.util.SyncedChain;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.Endpoints;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.AddressEndpoints;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpoint;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.endpoints.address.endpoints.AddressEndpointKey;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocation;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.AbsoluteLocationBuilder;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.absolute.location.absolute.location.location.type.ExternalLocationCase;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.base_endpoint.rev160427.has.child.endpoints.ChildEndpoint;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.ProviderName;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProvider;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.LocationProviderBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocation;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.endpoint_location_provider.rev160419.location.providers.location.provider.ProviderAddressEndpointLocationBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.renderer.rev151103.renderers.renderer.renderer.policy.configuration.endpoints.AddressEndpointWithLocationKey;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpoint;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.groupbasedpolicy.vpp_renderer.rev160425.config.VppEndpointKey;
52 import org.opendaylight.yangtools.concepts.ListenerRegistration;
53 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
57 import com.google.common.annotations.VisibleForTesting;
58 import com.google.common.base.Optional;
59 import com.google.common.base.Preconditions;
60 import com.google.common.collect.HashBasedTable;
61 import com.google.common.collect.Table;
62 import com.google.common.util.concurrent.Futures;
63 import com.google.common.util.concurrent.ListenableFuture;
65 public class VppEndpointLocationProvider
66 implements ClusteredDataTreeChangeListener<AddressEndpoint>, VPPLocationProvider, AutoCloseable {
68 private static final Logger LOG = LoggerFactory.getLogger(VppEndpointLocationProvider.class);
69 public static final ProviderName VPP_ENDPOINT_LOCATION_PROVIDER =
70 new ProviderName("VPP endpoint location provider");
71 public static final long PROVIDER_PRIORITY = 10L;
72 private final SyncedChain syncedChain;
73 private final Map<VppEndpointKey, VppEndpoint> vppEndpoints = new HashMap<>();
74 private final Table<VppEndpointKey, AddressEndpointKey, AddressEndpoint> pendingAddrEndpoints =
75 HashBasedTable.create();
77 private ListenerRegistration<VppEndpointLocationProvider> registeredListener;
78 private final ReentrantLock THREAD_LOCK = new ReentrantLock();
80 public VppEndpointLocationProvider(DataBroker dataProvider) {
81 LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
82 .setPriority(PROVIDER_PRIORITY)
84 syncedChain = new SyncedChain(checkNotNull(dataProvider).createTransactionChain(
85 new CloseOnFailTransactionChain(VppEndpointLocationProvider.class.getSimpleName())));
86 WriteTransaction wTx = syncedChain.newWriteOnlyTransaction();
87 wTx.put(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER),
88 locationProvider, true);
89 syncedChain.submitNow(wTx);
91 dataProvider.registerDataTreeChangeListener(
93 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
94 InstanceIdentifier.builder(Endpoints.class)
95 .child(AddressEndpoints.class)
96 .child(AddressEndpoint.class)
102 public void onDataTreeChanged(Collection<DataTreeModification<AddressEndpoint>> change) {
105 change.forEach(dtm -> {
106 ReadWriteTransaction rwTx = syncedChain.newReadWriteTransaction();
107 DataObjectModification<AddressEndpoint> rootNode = dtm.getRootNode();
108 if (rootNode.getDataBefore() != null) {
109 InstanceIdentifier<ProviderAddressEndpointLocation> iid =
110 IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER,
111 VppLocationUtils.locationProviderKey(rootNode.getDataBefore().getKey()));
112 LOG.debug("Clearing location {}", iid);
113 DataStoreHelper.removeIfExists(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
115 if (rootNode.getDataAfter() == null || !canCreateLocation(rootNode.getDataAfter())) {
116 syncedChain.submitNow(rwTx);
119 LOG.debug("Resolving location for {}", rootNode.getDataAfter().getKey());
120 syncEndpointLocation(rwTx, rootNode.getDataAfter());
121 syncedChain.submitNow(rwTx);
123 } catch (Exception e) {
124 LOG.error("Failed to resolve location. {} ", e);
126 THREAD_LOCK.unlock();
130 void syncEndpointLocation(ReadWriteTransaction rwTx, AddressEndpoint addrEp) {
131 LOG.trace("Processing endpoint {}", addrEp.getAddress());
132 if (EndpointUtils.isExternalEndpoint(rwTx, addrEp)) {
133 externalLocationWriter.sync(rwTx, addrEp);
134 } else if (VppLocationUtils.getL2ChildEndpoints(addrEp).size() > 1) {
135 relativeLocationWriter.sync(rwTx, addrEp);
136 } else if (VppLocationUtils.getL2ChildEndpoints(addrEp).size() == 1) {
137 absoluteLocationWriter.sync(rwTx, addrEp);
142 public ListenableFuture<Void> createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
145 vppEndpoints.put(vppEndpoint.getKey(), vppEndpoint);
146 Collection<AddressEndpoint> cachedAddrEp = pendingAddrEndpoints.row(vppEndpoint.getKey()).values();
147 if (cachedAddrEp == null || cachedAddrEp.isEmpty()) {
148 return Futures.immediateFuture(null);
150 ReadWriteTransaction rwTx = syncedChain.newReadWriteTransaction();
151 List<AddressEndpoint> result =
152 cachedAddrEp.stream().filter(ep -> canCreateLocation(ep)).collect(Collectors.toList());
153 result.forEach(ep -> syncEndpointLocation(rwTx, ep));
154 result.forEach(ep -> pendingAddrEndpoints.remove(vppEndpoint.getKey(), ep.getKey()));
155 syncedChain.submitNow(rwTx);
156 } catch (Exception e) {
157 LOG.error("Failed to resolve location for vpp endpoint {}. {}", vppEndpoint.getKey(), e);
159 THREAD_LOCK.unlock();
161 return Futures.immediateFuture(null);
165 public ListenableFuture<Void> deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
168 vppEndpoints.remove(vppEndpoint.getKey());
169 } catch (Exception e) {
170 LOG.warn("Failed to delete vpp endpoint {}", vppEndpoint.getKey());
172 THREAD_LOCK.unlock();
174 return Futures.immediateFuture(null);
178 public ListenableFuture<Void> replaceLocationForEndpoint(@Nonnull ExternalLocationCase location,
179 @Nonnull AddressEndpointWithLocationKey addrEpWithLocKey) {
180 LOG.debug("Replacing location for endpoint {}", addrEpWithLocKey.getAddress());
183 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.locationProviderIid(
184 VPP_ENDPOINT_LOCATION_PROVIDER, VppLocationUtils.providerLocationKey(addrEpWithLocKey));
185 ReadWriteTransaction rwTx = syncedChain.newReadWriteTransaction();
186 Optional<ProviderAddressEndpointLocation> optLoc =
187 DataStoreHelper.readFromDs(LogicalDatastoreType.CONFIGURATION, iid, rwTx);
188 if (!optLoc.isPresent() || optLoc.get().getAbsoluteLocation() == null) {
189 LOG.warn("No absolute location. Cannot modify bridge domain for endpoint {}.", addrEpWithLocKey);
190 // TODO failed future
191 syncedChain.submitNow(rwTx);
192 return Futures.immediateFuture(null);
194 AbsoluteLocation absoluteLocation = optLoc.get().getAbsoluteLocation();
195 ProviderAddressEndpointLocation providerLocation = new ProviderAddressEndpointLocationBuilder()
196 .setKey(iid.firstKeyOf(ProviderAddressEndpointLocation.class))
197 .setAbsoluteLocation(new AbsoluteLocationBuilder(absoluteLocation).setLocationType(location).build())
199 rwTx.put(LogicalDatastoreType.CONFIGURATION,
200 IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER, providerLocation.getKey()),
202 syncedChain.submitNow(rwTx);
203 } catch (Exception e) {
204 LOG.error("Failed to replace location for endpoint {}", addrEpWithLocKey);
206 THREAD_LOCK.unlock();
208 return Futures.immediateFuture(null);
212 synchronized boolean canCreateLocation(@Nonnull AddressEndpoint addrEp) {
213 if (!VppLocationUtils.validateEndpoint(addrEp)) {
216 return addrEp.getChildEndpoint()
218 .filter(child -> vppEndpoints.get(VppLocationUtils.vppEndpointKey(child.getKey())) == null)
219 .noneMatch(child -> {
220 VppEndpointKey key = VppLocationUtils.vppEndpointKey(child.getKey());
221 LOG.debug("Caching VPP endpoint {}", key.getAddress());
222 pendingAddrEndpoints.put(key, addrEp.getKey(), addrEp);
228 public void close() {
229 registeredListener.close();
230 WriteTransaction wTx = syncedChain.newWriteOnlyTransaction();
231 wTx.delete(LogicalDatastoreType.CONFIGURATION, IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER));
232 syncedChain.submitNow(wTx);
235 private abstract class LocationWriter {
237 abstract void sync(ReadWriteTransaction rwTx, AddressEndpoint after);
240 LocationWriter absoluteLocationWriter = new LocationWriter() {
243 void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
244 Preconditions.checkNotNull(after);
245 Preconditions.checkArgument(after.getChildEndpoint().size() == 1,
246 "Endpoint should have only one child endpoint " + after.getKey());
247 ChildEndpoint child = after.getChildEndpoint().get(0);
248 VppEndpoint vpp = vppEndpoints.get(VppLocationUtils.vppEndpointKey(child.getKey()));
249 ProviderAddressEndpointLocation location =
250 VppLocationUtils.createLocation(after.getKey(), VppLocationUtils.createAbsLocation(vpp));
251 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory
252 .locationProviderIid(VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey());
253 LOG.debug("Processing endpoint {} : Writing absolute location for endpoint.", after.getAddress());
254 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
258 LocationWriter externalLocationWriter = new LocationWriter() {
261 void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
262 Preconditions.checkNotNull(after);
263 Preconditions.checkArgument(EndpointUtils.isExternalEndpoint(rwTx, after),
264 "Endpoint " + after + " is not external");
265 ProviderAddressEndpointLocation location = VppLocationUtils.createLocation(after.getKey(), VppLocationUtils
266 .createRelativeAddressEndpointLocation(after.getKey(), VppNodeManager.resolvePublicInterfaces(rwTx)));
267 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory
268 .locationProviderIid(VppEndpointLocationProvider.VPP_ENDPOINT_LOCATION_PROVIDER, location.getKey());
269 LOG.debug("Processing endpoint {} : Writing relative location for external endpoint.", after.getAddress());
270 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid, location, true);
274 LocationWriter relativeLocationWriter = new LocationWriter() {
277 void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
278 Preconditions.checkNotNull(after);
279 List<VppEndpoint> vppEps = VppLocationUtils.getL2ChildEndpoints(after)
281 .filter(l2Child -> vppEndpoints.get(VppLocationUtils.vppEndpointKey(l2Child.getKey())) != null)
282 .map(l2Child -> vppEndpoints.get(VppLocationUtils.vppEndpointKey(l2Child.getKey())))
283 .collect(Collectors.toList());
284 InstanceIdentifier<ProviderAddressEndpointLocation> iid = IidFactory.locationProviderIid(
285 VPP_ENDPOINT_LOCATION_PROVIDER, VppLocationUtils.locationProviderKey(after.getKey()));
286 LOG.trace("Writing relative location for multi home endpoint {}", after.getAddress());
287 rwTx.put(LogicalDatastoreType.CONFIGURATION, iid,
288 VppLocationUtils.createLocation(after.getKey(), VppLocationUtils.createRelLocations(vppEps)), true);