Add INFO.yaml for GBP
[groupbasedpolicy.git] / renderers / vpp / src / main / java / org / opendaylight / groupbasedpolicy / renderer / vpp / iface / VppEndpointLocationProvider.java
1 /*
2  * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.groupbasedpolicy.renderer.vpp.iface;
10
11 import static com.google.common.base.Preconditions.checkNotNull;
12
13 import java.util.Collection;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.concurrent.locks.ReentrantLock;
18 import java.util.stream.Collectors;
19
20 import javax.annotation.Nonnull;
21
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;
56
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;
64
65 public class VppEndpointLocationProvider
66         implements ClusteredDataTreeChangeListener<AddressEndpoint>, VPPLocationProvider, AutoCloseable {
67
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();
76
77     private ListenerRegistration<VppEndpointLocationProvider> registeredListener;
78     private final ReentrantLock THREAD_LOCK = new ReentrantLock();
79
80     public VppEndpointLocationProvider(DataBroker dataProvider) {
81         LocationProvider locationProvider = new LocationProviderBuilder().setProvider(VPP_ENDPOINT_LOCATION_PROVIDER)
82             .setPriority(PROVIDER_PRIORITY)
83             .build();
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);
90         registeredListener =
91                 dataProvider.registerDataTreeChangeListener(
92                         checkNotNull(
93                                 new DataTreeIdentifier<>(LogicalDatastoreType.OPERATIONAL,
94                                         InstanceIdentifier.builder(Endpoints.class)
95                                             .child(AddressEndpoints.class)
96                                             .child(AddressEndpoint.class)
97                                             .build())),
98                         this);
99     }
100
101     @Override
102     public void onDataTreeChanged(Collection<DataTreeModification<AddressEndpoint>> change) {
103         try {
104             THREAD_LOCK.lock();
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);
114                 }
115                 if (rootNode.getDataAfter() == null || !canCreateLocation(rootNode.getDataAfter())) {
116                     syncedChain.submitNow(rwTx);
117                     return;
118                 }
119                 LOG.debug("Resolving location for {}", rootNode.getDataAfter().getKey());
120                 syncEndpointLocation(rwTx, rootNode.getDataAfter());
121                 syncedChain.submitNow(rwTx);
122             });
123         } catch (Exception e) {
124             LOG.error("Failed to resolve location. {} ", e);
125         } finally {
126             THREAD_LOCK.unlock();
127         }
128     }
129
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);
138         }
139     }
140
141     @Override
142     public ListenableFuture<Void> createLocationForVppEndpoint(VppEndpoint vppEndpoint) {
143         try {
144             THREAD_LOCK.lock();
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);
149             }
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);
158         } finally {
159             THREAD_LOCK.unlock();
160         }
161         return Futures.immediateFuture(null);
162     }
163
164     @Override
165     public ListenableFuture<Void> deleteLocationForVppEndpoint(VppEndpoint vppEndpoint) {
166         try {
167             THREAD_LOCK.lock();
168             vppEndpoints.remove(vppEndpoint.getKey());
169         } catch (Exception e) {
170             LOG.warn("Failed to delete vpp endpoint {}", vppEndpoint.getKey());
171         } finally {
172             THREAD_LOCK.unlock();
173         }
174         return Futures.immediateFuture(null);
175     }
176
177     @Override
178     public ListenableFuture<Void> replaceLocationForEndpoint(@Nonnull ExternalLocationCase location,
179             @Nonnull AddressEndpointWithLocationKey addrEpWithLocKey) {
180         LOG.debug("Replacing location for endpoint {}", addrEpWithLocKey.getAddress());
181         THREAD_LOCK.lock();
182         try {
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);
193             }
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())
198                 .build();
199             rwTx.put(LogicalDatastoreType.CONFIGURATION,
200                     IidFactory.locationProviderIid(VPP_ENDPOINT_LOCATION_PROVIDER, providerLocation.getKey()),
201                     providerLocation);
202             syncedChain.submitNow(rwTx);
203         } catch (Exception e) {
204             LOG.error("Failed to replace location for endpoint {}", addrEpWithLocKey);
205         } finally {
206             THREAD_LOCK.unlock();
207         }
208         return Futures.immediateFuture(null);
209     }
210
211     @VisibleForTesting
212     synchronized boolean canCreateLocation(@Nonnull AddressEndpoint addrEp) {
213         if (!VppLocationUtils.validateEndpoint(addrEp)) {
214             return false;
215         }
216         return addrEp.getChildEndpoint()
217             .stream()
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);
223                 return true;
224             });
225     }
226
227     @Override
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);
233     }
234
235     private abstract class LocationWriter {
236
237         abstract void sync(ReadWriteTransaction rwTx, AddressEndpoint after);
238     }
239
240     LocationWriter absoluteLocationWriter = new LocationWriter() {
241
242         @Override
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);
255         }
256     };
257
258     LocationWriter externalLocationWriter = new LocationWriter() {
259
260         @Override
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);
271         }
272     };
273
274     LocationWriter relativeLocationWriter = new LocationWriter() {
275
276         @Override
277         void sync(ReadWriteTransaction rwTx, AddressEndpoint after) {
278             Preconditions.checkNotNull(after);
279             List<VppEndpoint> vppEps = VppLocationUtils.getL2ChildEndpoints(after)
280                 .stream()
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);
289         }
290     };
291 }